2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1999 University of Maryland
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
28 * $Id: krb5-security.c,v 1.22 2006/06/16 10:55:05 martinea Exp $
30 * krb5-security.c - kerberos V5 security module
32 * XXX still need to check for initial keyword on connect so we can skip
33 * over shell garbage and other stuff that krb5 might want to spew out.
42 #include "security-util.h"
46 #ifdef KRB5_HEIMDAL_INCLUDES
52 #define BROKEN_MEMORY_CCACHE
54 #ifdef BROKEN_MEMORY_CCACHE
56 * If you don't have atexit() or on_exit(), you could just consider
57 * making atexit() empty and clean up your ticket files some other
62 #define atexit(func) on_exit(func, 0)
64 #define atexit(func) (you must to resolve lack of atexit)
65 #endif /* HAVE_ON_EXIT */
66 #endif /* ! HAVE_ATEXIT */
68 #ifndef KRB5_HEIMDAL_INCLUDES
69 #include <gssapi/gssapi_generic.h>
71 #include <gssapi/gssapi.h>
75 #ifndef KRB5_ENV_CCNAME
76 #define KRB5_ENV_CCNAME "KRB5CCNAME"
79 #define k5printf(x) auth_debug(1,x)
83 * consider undefining when kdestroy() is fixed. The current version does
84 * not work under krb5-1.2.4 in rh7.3, perhaps others.
86 #define KDESTROY_VIA_UNLINK 1
89 * Define this if you want all network traffic encrypted. This will
90 * extract a serious performance hit.
92 * It would be nice if we could do this on a filesystem-by-filesystem basis.
94 /*#define AMANDA_KRB5_ENCRYPT*/
97 * Where the keytab lives, if defined. Otherwise it expects something in the
100 /* #define AMANDA_KEYTAB "/.amanda-v5-keytab" */
103 * The name of the principal we authenticate with, if defined. Otherwise
104 * it expects something in the config file.
106 /* #define AMANDA_PRINCIPAL "service/amanda" */
109 * The lifetime of our tickets in seconds. This may or may not need to be
112 #define AMANDA_TKT_LIFETIME (12*60*60)
116 * The name of the service in /etc/services. This probably shouldn't be
119 #define AMANDA_KRB5_SERVICE_NAME "k5amanda"
122 * The default port to use if above entry in /etc/services doesn't exist
124 #define AMANDA_KRB5_DEFAULT_PORT 10082
127 * The timeout in seconds for each step of the GSS negotiation phase
129 #define GSS_TIMEOUT 30
132 * The largest buffer we can send/receive.
134 #define AMANDA_MAX_TOK_SIZE (MAX_TAPE_BLOCK_BYTES * 4)
137 * This is the tcp stream buffer size
139 #define KRB5_STREAM_BUFSIZE (MAX_TAPE_BLOCK_BYTES * 2)
142 * This is the max number of outgoing connections we can have at once.
143 * planner/amcheck/etc will open a bunch of connections as it tries
144 * to contact everything. We need to limit this to avoid blowing
145 * the max number of open file descriptors a process can have.
147 #define AMANDA_KRB5_MAXCONN 40
151 * Number of seconds krb5 has to start up
153 #define CONNECT_TIMEOUT 20
156 * Cache the local hostname
158 static char myhostname[MAX_HOSTNAME_LENGTH+1];
162 * Interface functions
164 static void krb5_accept(const struct security_driver *, int, int,
165 void (*)(security_handle_t *, pkt_t *));
166 static void krb5_connect(const char *,
167 char *(*)(char *, void *),
168 void (*)(void *, security_handle_t *, security_status_t), void *, void *);
170 static void krb5_init(void);
171 #ifdef BROKEN_MEMORY_CCACHE
172 static void cleanup(void);
174 static const char *get_tgt(char *keytab_name, char *principal_name);
175 static int gss_server(struct tcp_conn *);
176 static int gss_client(struct sec_handle *);
177 static const char *gss_error(OM_uint32, OM_uint32);
178 static char *krb5_checkuser(char *host, char *name, char *realm);
180 #ifdef AMANDA_KRB5_ENCRYPT
181 static int k5_encrypt(void *cookie, void *buf, ssize_t buflen,
182 void **encbuf, ssize_t *encbuflen);
183 static int k5_decrypt(void *cookie, void *buf, ssize_t buflen,
184 void **encbuf, ssize_t *encbuflen);
188 * This is our interface to the outside world.
190 const security_driver_t krb5_security_driver = {
197 stream_recvpkt_cancel,
206 tcpm_stream_read_sync,
207 tcpm_stream_read_cancel,
208 tcpm_close_connection,
209 #ifdef AMANDA_KRB5_ENCRYPT
218 static int newhandle = 1;
223 static int runkrb5(struct sec_handle *);
226 char *principal_name;
229 * krb5 version of a security handle allocator. Logically sets
230 * up a network "connection".
234 const char *hostname,
235 char * (*conf_fn)(char *, void *),
236 void (*fn)(void *, security_handle_t *, security_status_t),
240 struct sec_handle *rh;
242 struct addrinfo hints;
243 struct addrinfo *res = NULL;
246 assert(hostname != NULL);
247 (void)conf_fn; /* Quiet unused parameter warning */
248 (void)datap; /* Quiet unused parameter warning */
250 k5printf(("%s: krb5: krb5_connect: %s\n", debug_prefix_time(NULL),
255 rh = alloc(sizeof(*rh));
256 security_handleinit(&rh->sech, &krb5_security_driver);
259 rh->ev_timeout = NULL;
263 hints.ai_flags = AI_CANONNAME | AI_V4MAPPED | AI_ALL;
264 hints.ai_family = AF_UNSPEC;
266 hints.ai_flags = AI_CANONNAME;
267 hints.ai_family = AF_INET;
269 hints.ai_socktype = SOCK_DGRAM;
270 hints.ai_protocol = IPPROTO_UDP;
271 hints.ai_addrlen = 0;
272 hints.ai_addr = NULL;
273 hints.ai_canonname = NULL;
274 hints.ai_next = NULL;
275 result = getaddrinfo(hostname, NULL, &hints, &res);
278 hints.ai_flags = AI_CANONNAME;
279 hints.ai_family = AF_UNSPEC;
280 result = getaddrinfo(hostname, NULL, &hints, &res);
284 dbprintf(("krb5_connect: getaddrinfo(%s): %s\n", hostname, gai_strerror(result)));
285 security_seterror(&rh->sech, "getaddrinfo(%s): %s\n", hostname,
286 gai_strerror(result));
287 (*fn)(arg, &rh->sech, S_ERROR);
291 rh->hostname = stralloc(res->ai_canonname); /* will be replaced */
292 rh->rs = tcpma_stream_client(rh, newhandle++);
293 rh->rc->recv_security_ok = NULL;
294 rh->rc->prefix_packet = NULL;
299 amfree(rh->hostname);
300 rh->hostname = stralloc(rh->rs->rc->hostname);
303 keytab_name = AMANDA_KEYTAB;
306 keytab_name = conf_fn("krb5keytab", datap);
309 #ifdef AMANDA_PRINCIPAL
310 principal_name = AMANDA_PRINCIPAL;
313 principal_name = conf_fn("krb5principal", datap);
318 * We need to open a new connection.
320 * XXX need to eventually limit number of outgoing connections here.
322 if(rh->rc->read == -1) {
329 * The socket will be opened async so hosts that are down won't
330 * block everything. We need to register a write event
331 * so we will know when the socket comes alive.
333 * Overload rh->rs->ev_read to provide a write event handle.
334 * We also register a timeout.
338 rh->rs->ev_read = event_register((event_id_t)(rh->rs->rc->write),
339 EV_WRITEFD, sec_connect_callback, rh);
340 rh->ev_timeout = event_register(CONNECT_TIMEOUT, EV_TIME,
341 sec_connect_timeout, rh);
346 (*fn)(arg, &rh->sech, S_ERROR);
350 * Setup to handle new incoming connections
354 const struct security_driver *driver,
357 void (*fn)(security_handle_t *, pkt_t *))
359 struct sockaddr_storage sin;
362 char hostname[NI_MAXHOST];
369 if (getpeername(in, (struct sockaddr *)&sin, &len) < 0) {
370 dbprintf(("%s: getpeername returned: %s\n", debug_prefix_time(NULL),
374 if ((result = getnameinfo((struct sockaddr *)&sin, len,
375 hostname, NI_MAXHOST, NULL, 0, 0) != 0)) {
376 dbprintf(("%s: getnameinfo failed: %s\n",
377 debug_prefix_time(NULL), gai_strerror(result)));
380 if (check_name_give_sockaddr(hostname,
381 (struct sockaddr *)&sin, &errmsg) < 0) {
386 rc = sec_tcp_conn_get(hostname, 0);
387 rc->recv_security_ok = NULL;
388 rc->prefix_packet = NULL;
389 memcpy(&rc->peer, &sin, sizeof(rc->peer));
393 if (gss_server(rc) < 0)
394 error("gss_server failed: %s\n", rc->errmsg);
396 sec_tcp_conn_read(rc);
400 * Forks a krb5 to the host listed in rc->hostname
401 * Returns negative on error, with an errmsg in rc->errmsg.
405 struct sec_handle * rh)
409 in_port_t my_port, port;
411 struct tcp_conn * rc = rh->rc;
414 if ((sp = getservbyname(AMANDA_KRB5_SERVICE_NAME, "tcp")) == NULL)
415 port = htons(AMANDA_KRB5_DEFAULT_PORT);
421 if ((err = get_tgt(keytab_name, principal_name)) != NULL) {
422 security_seterror(&rh->sech, "%s: could not get TGT: %s",
427 server_socket = stream_client(rc->hostname,
428 (in_port_t)(ntohs(port)),
434 if(server_socket < 0) {
435 security_seterror(&rh->sech,
436 "%s", strerror(errno));
442 rc->read = rc->write = server_socket;
444 if (gss_client(rh) < 0) {
453 * Negotiate a krb5 gss context from the client end.
457 struct sec_handle *rh)
459 struct sec_stream *rs = rh->rs;
460 struct tcp_conn *rc = rs->rc;
461 gss_buffer_desc send_tok, recv_tok, AA;
463 OM_uint32 maj_stat, min_stat;
464 unsigned int ret_flags;
470 k5printf(("gss_client\n"));
472 send_tok.value = vstralloc("host/", rs->rc->hostname, NULL);
473 send_tok.length = strlen(send_tok.value) + 1;
474 maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NULL_OID,
476 if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
477 security_seterror(&rh->sech, "can't import name %s: %s",
478 (char *)send_tok.value, gss_error(maj_stat, min_stat));
479 amfree(send_tok.value);
482 amfree(send_tok.value);
483 rc->gss_context = GSS_C_NO_CONTEXT;
484 maj_stat = gss_display_name(&min_stat, gss_name, &AA, &doid);
485 dbprintf(("gss_name %s\n", (char *)AA.value));
488 * Perform the context-establishement loop.
490 * Every generated token is stored in send_tok which is then
491 * transmitted to the server; every received token is stored in
492 * recv_tok (empty on the first pass) to be processed by
493 * the next call to gss_init_sec_context.
495 * GSS-API guarantees that send_tok's length will be non-zero
496 * if and only if the server is expecting another token from us,
497 * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
498 * and only if the server has another token to send us.
501 recv_tok.value = NULL;
502 for (recv_tok.length = 0;;) {
504 maj_stat = gss_init_sec_context(&min_stat,
509 (OM_uint32)GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG,
510 0, NULL, /* no channel bindings */
511 (recv_tok.length == 0 ? GSS_C_NO_BUFFER : &recv_tok),
512 NULL, /* ignore mech type */
515 NULL); /* ignore time_rec */
517 if (recv_tok.length != 0) {
518 amfree(recv_tok.value);
521 if (maj_stat != (OM_uint32)GSS_S_COMPLETE && maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED) {
522 security_seterror(&rh->sech,
523 "error getting gss context: %s %s",
524 gss_error(maj_stat, min_stat), (char *)send_tok.value);
529 * Send back the response
531 if (send_tok.length != 0 && tcpm_send_token(rc, rc->write, rs->handle, &errmsg, send_tok.value, send_tok.length) < 0) {
532 security_seterror(&rh->sech, rc->errmsg);
533 gss_release_buffer(&min_stat, &send_tok);
536 gss_release_buffer(&min_stat, &send_tok);
539 * If we need to continue, then register for more packets
541 if (maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED)
544 rvalue = tcpm_recv_token(rc, rc->read, &rc->handle, &rc->errmsg,
545 (void *)&recv_tok.value,
546 (ssize_t *)&recv_tok.length, 60);
549 security_seterror(&rh->sech,
550 "recv error in gss loop: %s", rc->errmsg);
552 security_seterror(&rh->sech, "EOF in gss loop");
560 gss_release_name(&min_stat, &gss_name);
565 * Negotiate a krb5 gss context from the server end.
571 OM_uint32 maj_stat, min_stat, ret_flags;
572 gss_buffer_desc send_tok, recv_tok, AA;
575 gss_cred_id_t gss_creds;
576 char *p, *realm, *msg;
583 k5printf(("gss_server\n"));
588 * We need to be root while in gss_acquire_cred() to read the host key
589 * out of the default keytab. We also need to be root in
590 * gss_accept_context() thanks to the replay cache code.
594 snprintf(errbuf, SIZEOF(errbuf),
595 "real uid is %ld, needs to be 0 to read krb5 host key",
599 if (seteuid(0) < 0) {
600 snprintf(errbuf, SIZEOF(errbuf),
601 "can't seteuid to uid 0: %s", strerror(errno));
605 rc->gss_context = GSS_C_NO_CONTEXT;
606 send_tok.value = vstralloc("host/", myhostname, NULL);
607 send_tok.length = strlen(send_tok.value) + 1;
608 for (p = send_tok.value; *p != '\0'; p++) {
609 if (isupper((int)*p))
612 maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NULL_OID,
614 if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
616 snprintf(errbuf, SIZEOF(errbuf),
617 "can't import name %s: %s", (char *)send_tok.value,
618 gss_error(maj_stat, min_stat));
619 amfree(send_tok.value);
622 amfree(send_tok.value);
624 maj_stat = gss_display_name(&min_stat, gss_name, &AA, &doid);
625 dbprintf(("gss_name %s\n", (char *)AA.value));
626 maj_stat = gss_acquire_cred(&min_stat, gss_name, 0,
627 GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &gss_creds, NULL, NULL);
628 if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
629 snprintf(errbuf, SIZEOF(errbuf),
630 "can't acquire creds for host key host/%s: %s", myhostname,
631 gss_error(maj_stat, min_stat));
632 gss_release_name(&min_stat, &gss_name);
636 gss_release_name(&min_stat, &gss_name);
638 for (recv_tok.length = 0;;) {
639 recv_tok.value = NULL;
640 rvalue = tcpm_recv_token(rc, rc->read, &rc->handle, &rc->errmsg,
641 (char **)&recv_tok.value,
642 (ssize_t *)&recv_tok.length, 60);
645 snprintf(errbuf, SIZEOF(errbuf),
646 "recv error in gss loop: %s", rc->errmsg);
649 snprintf(errbuf, SIZEOF(errbuf), "EOF in gss loop");
653 maj_stat = gss_accept_sec_context(&min_stat, &rc->gss_context,
654 gss_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS,
655 &gss_name, &doid, &send_tok, &ret_flags, NULL, NULL);
657 if (maj_stat != (OM_uint32)GSS_S_COMPLETE &&
658 maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED) {
659 snprintf(errbuf, SIZEOF(errbuf),
660 "error accepting context: %s", gss_error(maj_stat, min_stat));
661 amfree(recv_tok.value);
664 amfree(recv_tok.value);
666 if (send_tok.length != 0 && tcpm_send_token(rc, rc->write, 0, &errmsg, send_tok.value, send_tok.length) < 0) {
667 strncpy(errbuf, rc->errmsg, SIZEOF(errbuf) - 1);
668 errbuf[SIZEOF(errbuf) - 1] = '\0';
670 gss_release_buffer(&min_stat, &send_tok);
673 gss_release_buffer(&min_stat, &send_tok);
677 * If we need to get more from the client, then register for
680 if (maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED)
684 maj_stat = gss_display_name(&min_stat, gss_name, &send_tok, &doid);
685 if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
686 snprintf(errbuf, SIZEOF(errbuf),
687 "can't display gss name: %s", gss_error(maj_stat, min_stat));
688 gss_release_name(&min_stat, &gss_name);
691 gss_release_name(&min_stat, &gss_name);
693 /* get rid of the realm */
694 if ((p = strchr(send_tok.value, '@')) == NULL) {
695 snprintf(errbuf, SIZEOF(errbuf),
696 "malformed gss name: %s", (char *)send_tok.value);
697 amfree(send_tok.value);
704 * If the principal doesn't match, complain
706 if ((msg = krb5_checkuser(rc->hostname, send_tok.value, realm)) != NULL) {
707 snprintf(errbuf, SIZEOF(errbuf),
708 "access not allowed from %s: %s", (char *)send_tok.value, msg);
709 amfree(send_tok.value);
712 amfree(send_tok.value);
718 rc->errmsg = stralloc(errbuf);
722 k5printf(("gss_server returning %d\n", rval));
727 * Setup some things about krb5. This should only be called once.
732 static int beenhere = 0;
734 char *myfqhostname=NULL;
740 #ifndef BROKEN_MEMORY_CCACHE
741 putenv(stralloc("KRB5_ENV_CCNAME=MEMORY:amanda_ccache"));
744 * MEMORY ccaches seem buggy and cause a lot of internal heap
745 * corruption. malloc has been known to core dump. This behavior
746 * has been witnessed in Cygnus' kerbnet 1.2, MIT's krb V 1.0.5 and
747 * MIT's krb V -current as of 3/17/1999.
749 * We just use a lame ccache scheme with a uid suffix.
754 ccache = malloc(128);
755 snprintf(ccache, SIZEOF(ccache),
756 "KRB5_ENV_CCNAME=FILE:/tmp/amanda_ccache.%ld.%ld",
757 (long)geteuid(), (long)getpid());
762 gethostname(myhostname, SIZEOF(myhostname) - 1);
763 myhostname[SIZEOF(myhostname) - 1] = '\0';
766 * In case it isn't fully qualified, do a DNS lookup. Ignore
767 * any errors (this is best-effort).
769 if (try_resolving_hostname(myhostname, &myfqhostname) == 0
770 && myfqhostname != NULL) {
771 strncpy(myhostname, myfqhostname, SIZEOF(myhostname)-1);
772 myhostname[SIZEOF(myhostname)-1] = '\0';
773 amfree(myfqhostname);
777 * Lowercase the results. We assume all host/ principals will be
780 for (p = myhostname; *p != '\0'; p++) {
781 if (isupper((int)*p))
786 #ifdef BROKEN_MEMORY_CCACHE
790 #ifdef KDESTROY_VIA_UNLINK
792 snprintf(ccache, SIZEOF(ccache), "/tmp/amanda_ccache.%ld.%ld",
793 (long)geteuid(), (long)getpid());
802 * Get a ticket granting ticket and stuff it in the cache
807 char * principal_name)
809 krb5_context context;
811 krb5_principal client = NULL, server = NULL;
816 #ifdef KRB5_HEIMDAL_INCLUDES
817 krb5_data tgtname = { KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME };
819 krb5_data tgtname = { 0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME };
821 static char *error = NULL;
827 if ((ret = krb5_init_context(&context)) != 0) {
828 error = vstralloc("error initializing krb5 context: ",
829 error_message(ret), NULL);
833 /*krb5_init_ets(context);*/
836 error = vstralloc("error -- no krb5 keytab defined", NULL);
840 if(!principal_name) {
841 error = vstralloc("error -- no krb5 principal defined", NULL);
846 * Resolve keytab file into a keytab object
848 if ((ret = krb5_kt_resolve(context, keytab_name, &keytab)) != 0) {
849 error = vstralloc("error resolving keytab ", keytab_name, ": ",
850 error_message(ret), NULL);
855 * Resolve the amanda service held in the keytab into a principal
858 ret = krb5_parse_name(context, principal_name, &client);
860 error = vstralloc("error parsing ", principal_name, ": ",
861 error_message(ret), NULL);
865 #ifdef KRB5_HEIMDAL_INCLUDES
866 ret = krb5_build_principal_ext(context, &server,
867 krb5_realm_length(*krb5_princ_realm(context, client)),
868 krb5_realm_data(*krb5_princ_realm(context, client)),
869 tgtname.length, tgtname.data,
870 krb5_realm_length(*krb5_princ_realm(context, client)),
871 krb5_realm_data(*krb5_princ_realm(context, client)),
874 ret = krb5_build_principal_ext(context, &server,
875 krb5_princ_realm(context, client)->length,
876 krb5_princ_realm(context, client)->data,
877 tgtname.length, tgtname.data,
878 krb5_princ_realm(context, client)->length,
879 krb5_princ_realm(context, client)->data,
883 error = vstralloc("error while building server name: ",
884 error_message(ret), NULL);
888 ret = krb5_timeofday(context, &now);
890 error = vstralloc("error getting time of day: ", error_message(ret),
895 memset(&creds, 0, SIZEOF(creds));
896 creds.times.starttime = 0;
897 creds.times.endtime = now + AMANDA_TKT_LIFETIME;
899 creds.client = client;
900 creds.server = server;
903 * Get a ticket for the service, using the keytab
905 ret = krb5_get_in_tkt_with_keytab(context, 0, NULL, NULL, NULL,
906 keytab, 0, &creds, 0);
909 error = vstralloc("error getting ticket for ", principal_name,
910 ": ", error_message(ret), NULL);
914 if ((ret = krb5_cc_default(context, &ccache)) != 0) {
915 error = vstralloc("error initializing ccache: ", error_message(ret),
919 if ((ret = krb5_cc_initialize(context, ccache, client)) != 0) {
920 error = vstralloc("error initializing ccache: ", error_message(ret),
924 if ((ret = krb5_cc_store_cred(context, ccache, &creds)) != 0) {
925 error = vstralloc("error storing creds in ccache: ",
926 error_message(ret), NULL);
929 krb5_cc_close(context, ccache);
931 krb5_free_cred_contents(context, &creds);
934 krb5_free_principal(context, client);
935 krb5_free_principal(context, server);
937 krb5_free_context(context);
941 #ifndef KDESTROY_VIA_UNLINK
948 krb5_context context;
951 if ((krb5_init_context(&context)) != 0) {
954 if ((krb5_cc_default(context, &ccache)) != 0) {
958 krb5_cc_destroy(context, ccache);
959 krb5_cc_close(context, ccache);
962 krb5_free_context(context);
968 * Formats an error from the gss api
975 static gss_buffer_desc msg;
976 OM_uint32 min_stat, msg_ctx;
979 gss_release_buffer(&min_stat, &msg);
982 if (major == GSS_S_FAILURE)
983 gss_display_status(&min_stat, minor, GSS_C_MECH_CODE, GSS_C_NULL_OID,
986 gss_display_status(&min_stat, major, GSS_C_GSS_CODE, GSS_C_NULL_OID,
988 return ((const char *)msg.value);
991 #ifdef AMANDA_KRB5_ENCRYPT
1000 struct tcp_conn *rc = cookie;
1001 gss_buffer_desc dectok;
1002 gss_buffer_desc enctok;
1003 OM_uint32 maj_stat, min_stat;
1006 k5printf(("krb5: k5_encrypt: enter %p\n", rc));
1008 dectok.length = buflen;
1011 if (rc->auth == 1) {
1012 assert(rc->gss_context != GSS_C_NO_CONTEXT);
1013 maj_stat = gss_seal(&min_stat, rc->gss_context, 1,
1014 GSS_C_QOP_DEFAULT, &dectok, &conf_state, &enctok);
1015 if (maj_stat != (OM_uint32)GSS_S_COMPLETE || conf_state == 0) {
1016 k5printf(("krb5 encrypt error to %s: %s\n",
1017 rc->hostname, gss_error(maj_stat, min_stat)));
1020 k5printf(("krb5: k5_encrypt: give %zu bytes\n", enctok.length));
1021 *encbuf = enctok.value;
1022 *encbuflen = enctok.length;
1025 *encbuflen = buflen;
1027 k5printf(("krb5: k5_encrypt: exit\n"));
1039 struct tcp_conn *rc = cookie;
1040 gss_buffer_desc enctok;
1041 gss_buffer_desc dectok;
1042 OM_uint32 maj_stat, min_stat;
1043 int conf_state, qop_state;
1045 k5printf(("krb5: k5_decrypt: enter\n"));
1047 if (rc->auth == 1) {
1048 enctok.length = buflen;
1051 k5printf(("krb5: k5_decrypt: decrypting %zu bytes\n", enctok.length));
1053 assert(rc->gss_context != GSS_C_NO_CONTEXT);
1054 maj_stat = gss_unseal(&min_stat, rc->gss_context, &enctok, &dectok,
1055 &conf_state, &qop_state);
1056 if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
1057 k5printf(("krb5 decrypt error from %s: %s\n",
1058 rc->hostname, gss_error(maj_stat, min_stat)));
1061 k5printf(("krb5: k5_decrypt: give %zu bytes\n", dectok.length));
1062 *decbuf = dectok.value;
1063 *decbuflen = dectok.length;
1066 *decbuflen = buflen;
1068 k5printf(("krb5: k5_decrypt: exit\n"));
1074 * check ~/.k5amandahosts to see if this principal is allowed in. If it's
1075 * hardcoded, then we don't check the realm
1078 krb5_checkuser( char * host,
1082 #ifdef AMANDA_PRINCIPAL
1083 if(strcmp(name, AMANDA_PRINCIPAL) == 0) {
1086 return(vstralloc("does not match compiled in default"));
1091 char *result = "generic error"; /* default is to not permit */
1096 char *filehost = NULL, *fileuser = NULL, *filerealm = NULL;
1097 char n1[NUM_STR_SIZE];
1098 char n2[NUM_STR_SIZE];
1100 assert( host != NULL);
1101 assert( name != NULL);
1103 if((pwd = getpwnam(CLIENT_LOGIN)) == NULL) {
1104 result = vstralloc("can not find user ", CLIENT_LOGIN, NULL);
1106 localuid = pwd->pw_uid;
1108 #ifdef USE_AMANDAHOSTS
1109 ptmp = stralloc2(pwd->pw_dir, "/.k5amandahosts");
1111 ptmp = stralloc2(pwd->pw_dir, "/.k5login");
1115 result = vstralloc("could not find home directory for ", CLIENT_LOGIN, NULL);
1120 * check to see if the ptmp file does nto exist.
1122 if(access(ptmp, R_OK) == -1 && errno == ENOENT) {
1124 * in this case we check to see if the principal matches
1125 * the destination user mimicing the .k5login functionality.
1127 if(strcmp(name, CLIENT_LOGIN) != 0) {
1128 result = vstralloc(name, " does not match ",
1129 CLIENT_LOGIN, NULL);
1136 k5printf(("opening ptmp: %s\n", (ptmp)?ptmp: "NULL!"));
1137 if((fp = fopen(ptmp, "r")) == NULL) {
1138 result = vstralloc("can not open ", ptmp, NULL);
1141 k5printf(("opened ptmp\n"));
1143 if (fstat(fileno(fp), &sbuf) != 0) {
1144 result = vstralloc("cannot fstat ", ptmp, ": ", strerror(errno), NULL);
1148 if (sbuf.st_uid != localuid) {
1149 snprintf(n1, SIZEOF(n1), "%ld", (long) sbuf.st_uid);
1150 snprintf(n2, SIZEOF(n2), "%ld", (long) localuid);
1151 result = vstralloc(ptmp, ": ",
1157 if ((sbuf.st_mode & 077) != 0) {
1158 result = stralloc2(ptmp,
1159 ": incorrect permissions; file must be accessible only by its owner");
1163 while ((line = agets(fp)) != NULL) {
1164 if (line[0] == '\0') {
1169 #if defined(SHOW_SECURITY_DETAIL) /* { */
1170 k5printf(("%s: processing line: <%s>\n", debug_prefix(NULL), line));
1172 /* if there's more than one column, then it's the host */
1173 if( (filehost = strtok(line, " \t")) == NULL) {
1179 * if there's only one entry, then it's a username and we have
1180 * no hostname. (so the principal is allowed from anywhere.
1182 if((fileuser = strtok(NULL, " \t")) == NULL) {
1183 fileuser = filehost;
1187 if(filehost && strcmp(filehost, host) != 0) {
1191 k5printf(("found a host match\n"));
1194 if( (filerealm = strchr(fileuser, '@')) != NULL) {
1195 *filerealm++ = '\0';
1199 * we have a match. We're going to be a little bit insecure
1200 * and indicate that the principal is correct but the realm is
1201 * not if that's the case. Technically we should say nothing
1202 * and let the user figure it out, but it's helpful for debugging.
1203 * You likely only get this far if you've turned on cross-realm auth
1206 k5printf(("comparing %s %s\n", fileuser, name));
1207 if(strcmp(fileuser, name) == 0) {
1208 k5printf(("found a match!\n"));
1209 if(realm && filerealm && (strcmp(realm, filerealm)!=0)) {
1219 result = vstralloc("no match in ", ptmp, NULL);
1224 #endif /* AMANDA_PRINCIPAL */
1229 void krb5_security_dummy(void);
1232 krb5_security_dummy(void)
1236 #endif /* KRB5_SECURITY */