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.
41 #include "security-util.h"
43 #include "sockaddr-util.h"
45 #ifdef KRB5_HEIMDAL_INCLUDES
49 #define BROKEN_MEMORY_CCACHE
51 #ifdef BROKEN_MEMORY_CCACHE
53 * If you don't have atexit() or on_exit(), you could just consider
54 * making atexit() empty and clean up your ticket files some other
59 #define atexit(func) on_exit(func, 0)
61 #define atexit(func) (you must to resolve lack of atexit)
62 #endif /* HAVE_ON_EXIT */
63 #endif /* ! HAVE_ATEXIT */
65 #ifndef KRB5_HEIMDAL_INCLUDES
66 #include <gssapi/gssapi_generic.h>
68 #include <gssapi/gssapi.h>
72 #ifndef KRB5_ENV_CCNAME
73 #define KRB5_ENV_CCNAME "KRB5CCNAME"
77 * consider undefining when kdestroy() is fixed. The current version does
78 * not work under krb5-1.2.4 in rh7.3, perhaps others.
80 #define KDESTROY_VIA_UNLINK 1
83 * Where the keytab lives, if defined. Otherwise it expects something in the
86 /* #define AMANDA_KEYTAB "/.amanda-v5-keytab" */
89 * The name of the principal we authenticate with, if defined. Otherwise
90 * it expects something in the config file.
92 /* #define AMANDA_PRINCIPAL "service/amanda" */
95 * The lifetime of our tickets in seconds. This may or may not need to be
98 #define AMANDA_TKT_LIFETIME (12*60*60)
102 * The name of the service in /etc/services. This probably shouldn't be
105 #define AMANDA_KRB5_SERVICE_NAME "k5amanda"
108 * The default port to use if above entry in /etc/services doesn't exist
110 #define AMANDA_KRB5_DEFAULT_PORT 10082
113 * The timeout in seconds for each step of the GSS negotiation phase
115 #define GSS_TIMEOUT 30
118 * This is the tcp stream buffer size
120 #define KRB5_STREAM_BUFSIZE (32768 * 2)
123 * This is the max number of outgoing connections we can have at once.
124 * planner/amcheck/etc will open a bunch of connections as it tries
125 * to contact everything. We need to limit this to avoid blowing
126 * the max number of open file descriptors a process can have.
128 #define AMANDA_KRB5_MAXCONN 40
132 * Number of seconds krb5 has to start up
134 #define CONNECT_TIMEOUT 20
137 * Cache the local hostname
139 static char myhostname[MAX_HOSTNAME_LENGTH+1];
143 * Interface functions
145 static void krb5_accept(const struct security_driver *,
146 char *(*)(char *, void *),
148 void (*)(security_handle_t *, pkt_t *),
150 static void krb5_connect(const char *,
151 char *(*)(char *, void *),
152 void (*)(void *, security_handle_t *, security_status_t), void *, void *);
154 static void krb5_init(void);
155 #ifdef BROKEN_MEMORY_CCACHE
156 static void cleanup(void);
158 static const char *get_tgt(char *keytab_name, char *principal_name);
159 static int gss_server(struct tcp_conn *);
160 static int gss_client(struct sec_handle *);
161 static const char *gss_error(OM_uint32, OM_uint32);
162 static char *krb5_checkuser(char *host, char *name, char *realm);
164 static int k5_encrypt(void *cookie, void *buf, ssize_t buflen,
165 void **encbuf, ssize_t *encbuflen);
166 static int k5_decrypt(void *cookie, void *buf, ssize_t buflen,
167 void **encbuf, ssize_t *encbuflen);
169 static ssize_t krb5_tcpm_recv_token(struct tcp_conn *rc, int fd, int *handle,
170 char **errmsg, char **buf, ssize_t *size,
173 * This is our interface to the outside world.
175 const security_driver_t krb5_security_driver = {
179 sec_get_authenticated_peer_name_hostname,
183 stream_recvpkt_cancel,
192 tcpm_stream_read_sync,
193 tcpm_stream_read_cancel,
194 tcpm_close_connection,
199 static int newhandle = 1;
204 static int runkrb5(struct sec_handle *);
207 char *principal_name;
210 * krb5 version of a security handle allocator. Logically sets
211 * up a network "connection".
215 const char *hostname,
216 char * (*conf_fn)(char *, void *),
217 void (*fn)(void *, security_handle_t *, security_status_t),
221 struct sec_handle *rh;
226 assert(hostname != NULL);
228 auth_debug(1, "krb5: krb5_connect: %s\n", hostname);
232 rh = alloc(sizeof(*rh));
233 security_handleinit(&rh->sech, &krb5_security_driver);
236 rh->ev_timeout = NULL;
239 result = resolve_hostname(hostname, 0, NULL, &canonname);
241 dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
242 security_seterror(&rh->sech, _("resolve_hostname(%s): %s\n"), hostname,
243 gai_strerror(result));
244 (*fn)(arg, &rh->sech, S_ERROR);
247 if (canonname == NULL) {
248 dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname);
249 security_seterror(&rh->sech,
250 _("resolve_hostname(%s) did not return a canonical name\n"), hostname);
251 (*fn)(arg, &rh->sech, S_ERROR);
255 rh->hostname = canonname; /* will be replaced */
256 canonname = NULL; /* steal reference */
257 rh->rs = tcpma_stream_client(rh, newhandle++);
258 rh->rc->conf_fn = conf_fn;
259 rh->rc->datap = datap;
260 rh->rc->recv_security_ok = NULL;
261 rh->rc->prefix_packet = NULL;
266 amfree(rh->hostname);
267 rh->hostname = stralloc(rh->rs->rc->hostname);
270 keytab_name = AMANDA_KEYTAB;
273 keytab_name = conf_fn("krb5keytab", datap);
276 #ifdef AMANDA_PRINCIPAL
277 principal_name = AMANDA_PRINCIPAL;
280 principal_name = conf_fn("krb5principal", datap);
285 * We need to open a new connection.
287 * XXX need to eventually limit number of outgoing connections here.
289 if(rh->rc->read == -1) {
296 * The socket will be opened async so hosts that are down won't
297 * block everything. We need to register a write event
298 * so we will know when the socket comes alive.
300 * Overload rh->rs->ev_read to provide a write event handle.
301 * We also register a timeout.
305 rh->rs->ev_read = event_register((event_id_t)(rh->rs->rc->write),
306 EV_WRITEFD, sec_connect_callback, rh);
307 rh->ev_timeout = event_register(CONNECT_TIMEOUT, EV_TIME,
308 sec_connect_timeout, rh);
315 (*fn)(arg, &rh->sech, S_ERROR);
320 * Setup to handle new incoming connections
324 const struct security_driver *driver,
325 char *(*conf_fn)(char *, void *),
328 void (*fn)(security_handle_t *, pkt_t *),
334 char hostname[NI_MAXHOST];
341 if (getpeername(in, (struct sockaddr *)&sin, &len) < 0) {
342 dbprintf(_("getpeername returned: %s\n"),
347 if ((result = getnameinfo((struct sockaddr *)&sin, len,
348 hostname, NI_MAXHOST, NULL, 0, 0) != 0)) {
349 dbprintf(_("getnameinfo failed: %s\n"),
350 gai_strerror(result));
353 if (check_name_give_sockaddr(hostname,
354 (struct sockaddr *)&sin, &errmsg) < 0) {
355 dbprintf(_("check_name_give_sockaddr(%s): %s\n"),
362 rc = sec_tcp_conn_get(hostname, 0);
363 rc->conf_fn = conf_fn;
365 rc->recv_security_ok = NULL;
366 rc->prefix_packet = NULL;
367 copy_sockaddr(&rc->peer, &sin);
371 if (gss_server(rc) < 0)
372 error("gss_server failed: %s\n", rc->errmsg);
374 sec_tcp_conn_read(rc);
378 * Forks a krb5 to the host listed in rc->hostname
379 * Returns negative on error, with an errmsg in rc->errmsg.
383 struct sec_handle * rh)
387 in_port_t my_port, port;
388 struct tcp_conn * rc = rh->rc;
391 if ((sp = getservbyname(AMANDA_KRB5_SERVICE_NAME, "tcp")) == NULL)
392 port = htons(AMANDA_KRB5_DEFAULT_PORT);
396 if ((err = get_tgt(keytab_name, principal_name)) != NULL) {
397 security_seterror(&rh->sech, "%s: could not get TGT: %s",
403 server_socket = stream_client(rc->hostname,
404 (in_port_t)(ntohs(port)),
411 if(server_socket < 0) {
412 security_seterror(&rh->sech,
413 "%s", strerror(errno));
418 rc->read = rc->write = server_socket;
420 if (gss_client(rh) < 0) {
432 * Negotiate a krb5 gss context from the client end.
436 struct sec_handle *rh)
438 struct sec_stream *rs = rh->rs;
439 struct tcp_conn *rc = rs->rc;
440 gss_buffer_desc send_tok, recv_tok, AA;
442 OM_uint32 maj_stat, min_stat;
443 unsigned int ret_flags;
449 auth_debug(1, "gss_client\n");
451 send_tok.value = vstralloc("host/", rs->rc->hostname, NULL);
452 send_tok.length = strlen(send_tok.value) + 1;
453 maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NULL_OID,
455 if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
456 security_seterror(&rh->sech, _("can't import name %s: %s"),
457 (char *)send_tok.value, gss_error(maj_stat, min_stat));
458 amfree(send_tok.value);
461 amfree(send_tok.value);
462 rc->gss_context = GSS_C_NO_CONTEXT;
463 maj_stat = gss_display_name(&min_stat, gss_name, &AA, &doid);
464 dbprintf(_("gss_name %s\n"), (char *)AA.value);
467 * Perform the context-establishement loop.
469 * Every generated token is stored in send_tok which is then
470 * transmitted to the server; every received token is stored in
471 * recv_tok (empty on the first pass) to be processed by
472 * the next call to gss_init_sec_context.
474 * GSS-API guarantees that send_tok's length will be non-zero
475 * if and only if the server is expecting another token from us,
476 * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
477 * and only if the server has another token to send us.
480 recv_tok.value = NULL;
481 for (recv_tok.length = 0;;) {
483 maj_stat = gss_init_sec_context(&min_stat,
488 (OM_uint32)GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG,
489 0, NULL, /* no channel bindings */
490 (recv_tok.length == 0 ? GSS_C_NO_BUFFER : &recv_tok),
491 NULL, /* ignore mech type */
494 NULL); /* ignore time_rec */
496 if (recv_tok.length != 0) {
497 amfree(recv_tok.value);
500 if (maj_stat != (OM_uint32)GSS_S_COMPLETE && maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED) {
501 security_seterror(&rh->sech,
502 _("error getting gss context: %s %s"),
503 gss_error(maj_stat, min_stat), (char *)send_tok.value);
508 * Send back the response
510 if (send_tok.length != 0 && tcpm_send_token(rc, rc->write, rs->handle, &errmsg, send_tok.value, send_tok.length) < 0) {
511 security_seterror(&rh->sech, "%s", rc->errmsg);
512 gss_release_buffer(&min_stat, &send_tok);
515 gss_release_buffer(&min_stat, &send_tok);
518 * If we need to continue, then register for more packets
520 if (maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED)
523 rvalue = krb5_tcpm_recv_token(rc, rc->read, &rc->handle,
525 (void *)&recv_tok.value,
526 (ssize_t *)&recv_tok.length, 60);
529 security_seterror(&rh->sech,
530 _("recv error in gss loop: %s"), rc->errmsg);
532 security_seterror(&rh->sech, _("EOF in gss loop"));
540 gss_release_name(&min_stat, &gss_name);
545 * Negotiate a krb5 gss context from the server end.
551 OM_uint32 maj_stat, min_stat, ret_flags;
552 gss_buffer_desc send_tok, recv_tok, AA;
555 gss_cred_id_t gss_creds;
556 char *p, *realm, *msg;
562 auth_debug(1, "gss_server\n");
567 * We need to be root while in gss_acquire_cred() to read the host key
568 * out of the default keytab. We also need to be root in
569 * gss_accept_context() thanks to the replay cache code.
571 if (!set_root_privs(0)) {
572 g_snprintf(errbuf, SIZEOF(errbuf),
573 _("can't take root privileges to read krb5 host key: %s"), strerror(errno));
577 rc->gss_context = GSS_C_NO_CONTEXT;
578 send_tok.value = vstralloc("host/", myhostname, NULL);
579 send_tok.length = strlen(send_tok.value) + 1;
580 for (p = send_tok.value; *p != '\0'; p++) {
581 if (isupper((int)*p))
584 maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NULL_OID,
586 if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
588 g_snprintf(errbuf, SIZEOF(errbuf),
589 _("can't import name %s: %s"), (char *)send_tok.value,
590 gss_error(maj_stat, min_stat));
591 amfree(send_tok.value);
594 amfree(send_tok.value);
596 maj_stat = gss_display_name(&min_stat, gss_name, &AA, &doid);
597 dbprintf(_("gss_name %s\n"), (char *)AA.value);
598 maj_stat = gss_acquire_cred(&min_stat, gss_name, 0,
599 GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &gss_creds, NULL, NULL);
600 if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
601 g_snprintf(errbuf, SIZEOF(errbuf),
602 _("can't acquire creds for host key host/%s: %s"), myhostname,
603 gss_error(maj_stat, min_stat));
604 gss_release_name(&min_stat, &gss_name);
608 gss_release_name(&min_stat, &gss_name);
610 for (recv_tok.length = 0;;) {
611 recv_tok.value = NULL;
612 rvalue = krb5_tcpm_recv_token(rc, rc->read, &rc->handle,
614 /* (void *) is to avoid type-punning warning */
615 (char **)(void *)&recv_tok.value,
616 (ssize_t *)&recv_tok.length, 60);
619 g_snprintf(errbuf, SIZEOF(errbuf),
620 _("recv error in gss loop: %s"), rc->errmsg);
623 g_snprintf(errbuf, SIZEOF(errbuf), _("EOF in gss loop"));
627 maj_stat = gss_accept_sec_context(&min_stat, &rc->gss_context,
628 gss_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS,
629 &gss_name, &doid, &send_tok, &ret_flags, NULL, NULL);
631 if (maj_stat != (OM_uint32)GSS_S_COMPLETE &&
632 maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED) {
633 g_snprintf(errbuf, SIZEOF(errbuf),
634 _("error accepting context: %s"), gss_error(maj_stat, min_stat));
635 amfree(recv_tok.value);
638 amfree(recv_tok.value);
640 if (send_tok.length != 0 && tcpm_send_token(rc, rc->write, 0, &errmsg, send_tok.value, send_tok.length) < 0) {
641 strncpy(errbuf, rc->errmsg, SIZEOF(errbuf) - 1);
642 errbuf[SIZEOF(errbuf) - 1] = '\0';
644 gss_release_buffer(&min_stat, &send_tok);
647 gss_release_buffer(&min_stat, &send_tok);
651 * If we need to get more from the client, then register for
654 if (maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED)
658 maj_stat = gss_display_name(&min_stat, gss_name, &send_tok, &doid);
659 if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
660 g_snprintf(errbuf, SIZEOF(errbuf),
661 _("can't display gss name: %s"), gss_error(maj_stat, min_stat));
662 gss_release_name(&min_stat, &gss_name);
665 gss_release_name(&min_stat, &gss_name);
667 /* get rid of the realm */
668 if ((p = strchr(send_tok.value, '@')) == NULL) {
669 g_snprintf(errbuf, SIZEOF(errbuf),
670 _("malformed gss name: %s"), (char *)send_tok.value);
671 amfree(send_tok.value);
678 * If the principal doesn't match, complain
680 if ((msg = krb5_checkuser(rc->hostname, send_tok.value, realm)) != NULL) {
681 g_snprintf(errbuf, SIZEOF(errbuf),
682 _("access not allowed from %s: %s"), (char *)send_tok.value, msg);
683 amfree(send_tok.value);
686 amfree(send_tok.value);
692 rc->errmsg = stralloc(errbuf);
696 auth_debug(1, _("gss_server returning %d\n"), rval);
701 * Setup some things about krb5. This should only be called once.
706 static int beenhere = 0;
708 char *myfqhostname=NULL;
714 #ifndef BROKEN_MEMORY_CCACHE
715 putenv(stralloc("KRB5_ENV_CCNAME=MEMORY:amanda_ccache"));
718 * MEMORY ccaches seem buggy and cause a lot of internal heap
719 * corruption. malloc has been known to core dump. This behavior
720 * has been witnessed in Cygnus' kerbnet 1.2, MIT's krb V 1.0.5 and
721 * MIT's krb V -current as of 3/17/1999.
723 * We just use a lame ccache scheme with a uid suffix.
728 ccache = malloc(128);
729 g_snprintf(ccache, SIZEOF(ccache),
730 "KRB5_ENV_CCNAME=FILE:/tmp/amanda_ccache.%ld.%ld",
731 (long)geteuid(), (long)getpid());
736 gethostname(myhostname, SIZEOF(myhostname) - 1);
737 myhostname[SIZEOF(myhostname) - 1] = '\0';
740 * In case it isn't fully qualified, do a DNS lookup. Ignore
741 * any errors (this is best-effort).
743 if (resolve_hostname(myhostname, SOCK_STREAM, NULL, &myfqhostname) == 0
744 && myfqhostname != NULL) {
745 strncpy(myhostname, myfqhostname, SIZEOF(myhostname)-1);
746 myhostname[SIZEOF(myhostname)-1] = '\0';
747 amfree(myfqhostname);
751 * Lowercase the results. We assume all host/ principals will be
754 for (p = myhostname; *p != '\0'; p++) {
755 if (isupper((int)*p))
760 #ifdef BROKEN_MEMORY_CCACHE
764 #ifdef KDESTROY_VIA_UNLINK
766 g_snprintf(ccache, SIZEOF(ccache), "/tmp/amanda_ccache.%ld.%ld",
767 (long)geteuid(), (long)getpid());
776 * Get a ticket granting ticket and stuff it in the cache
781 char * principal_name)
783 krb5_context context;
785 krb5_principal client = NULL, server = NULL;
790 #ifdef KRB5_HEIMDAL_INCLUDES
791 krb5_data tgtname = { KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME };
793 krb5_data tgtname = { 0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME };
795 static char *error = NULL;
801 if ((ret = krb5_init_context(&context)) != 0) {
802 error = vstrallocf(_("error initializing krb5 context: %s"),
807 /*krb5_init_ets(context);*/
810 error = vstrallocf(_("error -- no krb5 keytab defined"));
814 if(!principal_name) {
815 error = vstrallocf(_("error -- no krb5 principal defined"));
820 * Resolve keytab file into a keytab object
822 if ((ret = krb5_kt_resolve(context, keytab_name, &keytab)) != 0) {
823 error = vstrallocf(_("error resolving keytab %s: %s"), keytab_name,
829 * Resolve the amanda service held in the keytab into a principal
832 ret = krb5_parse_name(context, principal_name, &client);
834 error = vstrallocf(_("error parsing %s: %s"), principal_name,
839 #ifdef KRB5_HEIMDAL_INCLUDES
840 ret = krb5_build_principal_ext(context, &server,
841 krb5_realm_length(*krb5_princ_realm(context, client)),
842 krb5_realm_data(*krb5_princ_realm(context, client)),
843 tgtname.length, tgtname.data,
844 krb5_realm_length(*krb5_princ_realm(context, client)),
845 krb5_realm_data(*krb5_princ_realm(context, client)),
848 ret = krb5_build_principal_ext(context, &server,
849 krb5_princ_realm(context, client)->length,
850 krb5_princ_realm(context, client)->data,
851 tgtname.length, tgtname.data,
852 krb5_princ_realm(context, client)->length,
853 krb5_princ_realm(context, client)->data,
857 error = vstrallocf(_("error while building server name: %s"),
862 ret = krb5_timeofday(context, &now);
864 error = vstrallocf(_("error getting time of day: %s"), error_message(ret));
868 memset(&creds, 0, SIZEOF(creds));
869 creds.times.starttime = 0;
870 creds.times.endtime = now + AMANDA_TKT_LIFETIME;
872 creds.client = client;
873 creds.server = server;
876 * Get a ticket for the service, using the keytab
878 ret = krb5_get_in_tkt_with_keytab(context, 0, NULL, NULL, NULL,
879 keytab, 0, &creds, 0);
882 error = vstrallocf(_("error getting ticket for %s: %s"),
883 principal_name, error_message(ret));
887 if ((ret = krb5_cc_default(context, &ccache)) != 0) {
888 error = vstrallocf(_("error initializing ccache: %s"), error_message(ret));
891 if ((ret = krb5_cc_initialize(context, ccache, client)) != 0) {
892 error = vstrallocf(_("error initializing ccache: %s"), error_message(ret));
895 if ((ret = krb5_cc_store_cred(context, ccache, &creds)) != 0) {
896 error = vstrallocf(_("error storing creds in ccache: %s"),
900 krb5_cc_close(context, ccache);
902 krb5_free_cred_contents(context, &creds);
905 krb5_free_principal(context, client);
906 krb5_free_principal(context, server);
908 krb5_free_context(context);
912 #ifndef KDESTROY_VIA_UNLINK
919 krb5_context context;
922 if ((krb5_init_context(&context)) != 0) {
925 if ((krb5_cc_default(context, &ccache)) != 0) {
929 krb5_cc_destroy(context, ccache);
930 krb5_cc_close(context, ccache);
933 krb5_free_context(context);
939 * Formats an error from the gss api
946 static gss_buffer_desc msg;
947 OM_uint32 min_stat, msg_ctx;
950 gss_release_buffer(&min_stat, &msg);
953 if (major == GSS_S_FAILURE)
954 gss_display_status(&min_stat, minor, GSS_C_MECH_CODE, GSS_C_NULL_OID,
957 gss_display_status(&min_stat, major, GSS_C_GSS_CODE, GSS_C_NULL_OID,
959 return ((const char *)msg.value);
970 struct tcp_conn *rc = cookie;
971 gss_buffer_desc dectok;
972 gss_buffer_desc enctok;
973 OM_uint32 maj_stat, min_stat;
976 if (rc->conf_fn && rc->conf_fn("kencrypt", rc->datap)) {
977 auth_debug(1, _("krb5: k5_encrypt: enter %p\n"), rc);
979 dectok.length = buflen;
983 assert(rc->gss_context != GSS_C_NO_CONTEXT);
984 maj_stat = gss_seal(&min_stat, rc->gss_context, 1,
985 GSS_C_QOP_DEFAULT, &dectok, &conf_state,
987 if (maj_stat != (OM_uint32)GSS_S_COMPLETE || conf_state == 0) {
988 auth_debug(1, _("krb5 encrypt error to %s: %s\n"),
989 rc->hostname, gss_error(maj_stat, min_stat));
992 auth_debug(1, _("krb5: k5_encrypt: give %zu bytes\n"),
994 *encbuf = enctok.value;
995 *encbuflen = enctok.length;
1000 auth_debug(1, _("krb5: k5_encrypt: exit\n"));
1014 struct tcp_conn *rc = cookie;
1015 gss_buffer_desc enctok;
1016 gss_buffer_desc dectok;
1017 OM_uint32 maj_stat, min_stat;
1018 int conf_state, qop_state;
1020 if (rc->conf_fn && rc->conf_fn("kencrypt", rc->datap)) {
1021 auth_debug(1, _("krb5: k5_decrypt: enter\n"));
1022 if (rc->auth == 1) {
1023 enctok.length = buflen;
1026 auth_debug(1, _("krb5: k5_decrypt: decrypting %zu bytes\n"), enctok.length);
1028 assert(rc->gss_context != GSS_C_NO_CONTEXT);
1029 maj_stat = gss_unseal(&min_stat, rc->gss_context, &enctok, &dectok,
1030 &conf_state, &qop_state);
1031 if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
1032 auth_debug(1, _("krb5 decrypt error from %s: %s\n"),
1033 rc->hostname, gss_error(maj_stat, min_stat));
1036 auth_debug(1, _("krb5: k5_decrypt: give %zu bytes\n"),
1038 *decbuf = dectok.value;
1039 *decbuflen = dectok.length;
1042 *decbuflen = buflen;
1044 auth_debug(1, _("krb5: k5_decrypt: exit\n"));
1047 *decbuflen = buflen;
1053 * check ~/.k5amandahosts to see if this principal is allowed in. If it's
1054 * hardcoded, then we don't check the realm
1057 krb5_checkuser( char * host,
1061 #ifdef AMANDA_PRINCIPAL
1062 if(strcmp(name, AMANDA_PRINCIPAL) == 0) {
1065 return(vstrallocf(_("does not match compiled in default")));
1070 char *result = _("generic error"); /* default is to not permit */
1075 char *filehost = NULL, *fileuser = NULL, *filerealm = NULL;
1077 assert( host != NULL);
1078 assert( name != NULL);
1080 if((pwd = getpwnam(CLIENT_LOGIN)) == NULL) {
1081 result = vstrallocf(_("can not find user %s"), CLIENT_LOGIN);
1083 localuid = pwd->pw_uid;
1085 #ifdef USE_AMANDAHOSTS
1086 ptmp = stralloc2(pwd->pw_dir, "/.k5amandahosts");
1088 ptmp = stralloc2(pwd->pw_dir, "/.k5login");
1092 result = vstrallocf(_("could not find home directory for %s"), CLIENT_LOGIN);
1097 * check to see if the ptmp file does nto exist.
1099 if(access(ptmp, R_OK) == -1 && errno == ENOENT) {
1101 * in this case we check to see if the principal matches
1102 * the destination user mimicing the .k5login functionality.
1104 if(strcmp(name, CLIENT_LOGIN) != 0) {
1105 result = vstrallocf(_("%s does not match %s"),
1106 name, CLIENT_LOGIN);
1113 auth_debug(1, _("opening ptmp: %s\n"), (ptmp)?ptmp: "NULL!");
1114 if((fp = fopen(ptmp, "r")) == NULL) {
1115 result = vstrallocf(_("can not open %s"), ptmp);
1118 auth_debug(1, _("opened ptmp\n"));
1120 if (fstat(fileno(fp), &sbuf) != 0) {
1121 result = vstrallocf(_("cannot fstat %s: %s"), ptmp, strerror(errno));
1125 if (sbuf.st_uid != localuid) {
1126 result = vstrallocf(_("%s is owned by %ld, should be %ld"),
1127 ptmp, (long)sbuf.st_uid, (long)localuid);
1130 if ((sbuf.st_mode & 077) != 0) {
1131 result = vstrallocf(
1132 _("%s: incorrect permissions; file must be accessible only by its owner"), ptmp);
1136 while ((line = agets(fp)) != NULL) {
1137 if (line[0] == '\0') {
1142 /* if there's more than one column, then it's the host */
1143 if( (filehost = strtok(line, " \t")) == NULL) {
1149 * if there's only one entry, then it's a username and we have
1150 * no hostname. (so the principal is allowed from anywhere.
1152 if((fileuser = strtok(NULL, " \t")) == NULL) {
1153 fileuser = filehost;
1157 if(filehost && strcmp(filehost, host) != 0) {
1161 auth_debug(1, _("found a host match\n"));
1164 if( (filerealm = strchr(fileuser, '@')) != NULL) {
1165 *filerealm++ = '\0';
1169 * we have a match. We're going to be a little bit insecure
1170 * and indicate that the principal is correct but the realm is
1171 * not if that's the case. Technically we should say nothing
1172 * and let the user figure it out, but it's helpful for debugging.
1173 * You likely only get this far if you've turned on cross-realm auth
1176 auth_debug(1, _("comparing %s %s\n"), fileuser, name);
1177 if(strcmp(fileuser, name) == 0) {
1178 auth_debug(1, _("found a match!\n"));
1179 if(realm && filerealm && (strcmp(realm, filerealm)!=0)) {
1189 result = vstrallocf(_("no match in %s"), ptmp);
1194 #endif /* AMANDA_PRINCIPAL */
1198 * return -1 on error
1199 * return 0 on EOF: *handle = H_EOF && *size = 0 if socket closed
1200 * return 0 on EOF: *handle = handle && *size = 0 if stream closed
1201 * return size : *handle = handle && *size = size for data read
1205 krb5_tcpm_recv_token(
1206 struct tcp_conn *rc,
1214 unsigned int netint[2];
1216 assert(SIZEOF(netint) == 8);
1218 switch (net_read(fd, &netint, SIZEOF(netint), timeout)) {
1221 *errmsg = newvstrallocf(*errmsg, _("recv error: %s"), strerror(errno));
1222 auth_debug(1, _("krb5_tcpm_recv_token: A return(-1)\n"));
1227 *errmsg = newvstrallocf(*errmsg, _("SOCKET_EOF"));
1228 auth_debug(1, _("krb5_tcpm_recv_token: A return(0)\n"));
1234 *size = (ssize_t)ntohl(netint[0]);
1235 *handle = (int)ntohl(netint[1]);
1236 /* amanda protocol packet can be above NETWORK_BLOCK_BYTES */
1237 if (*size > 128*NETWORK_BLOCK_BYTES || *size < 0) {
1238 if (isprint((int)(*size ) & 0xFF) &&
1239 isprint((int)(*size >> 8 ) & 0xFF) &&
1240 isprint((int)(*size >> 16) & 0xFF) &&
1241 isprint((int)(*size >> 24) & 0xFF) &&
1242 isprint((*handle ) & 0xFF) &&
1243 isprint((*handle >> 8 ) & 0xFF) &&
1244 isprint((*handle >> 16) & 0xFF) &&
1245 isprint((*handle >> 24) & 0xFF)) {
1248 s[0] = ((int)(*size) >> 24) & 0xFF;
1249 s[1] = ((int)(*size) >> 16) & 0xFF;
1250 s[2] = ((int)(*size) >> 8) & 0xFF;
1251 s[3] = ((int)(*size) ) & 0xFF;
1252 s[4] = (*handle >> 24) & 0xFF;
1253 s[5] = (*handle >> 16) & 0xFF;
1254 s[6] = (*handle >> 8 ) & 0xFF;
1255 s[7] = (*handle ) & 0xFF;
1257 while(i<100 && isprint((int)s[i]) && s[i] != '\n') {
1258 switch(net_read(fd, &s[i], 1, 0)) {
1259 case -1: s[i] = '\0'; break;
1260 case 0: s[i] = '\0'; break;
1262 dbprintf(_("read: %c\n"), s[i]); i++; s[i]=' ';
1267 *errmsg = newvstrallocf(*errmsg, _("krb5_tcpm_recv_token: invalid size: %s"), s);
1268 dbprintf(_("krb5_tcpm_recv_token: invalid size %s\n"), s);
1270 *errmsg = newvstrallocf(*errmsg, _("krb5_tcpm_recv_token: invalid size"));
1271 dbprintf(_("krb5_tcpm_recv_token: invalid size %zd\n"), *size);
1277 *buf = alloc((size_t)*size);
1280 auth_debug(1, _("krb5_tcpm_recv_token: read EOF from %d\n"), *handle);
1281 *errmsg = newvstrallocf(*errmsg, _("EOF"));
1284 switch (net_read(fd, *buf, (size_t)*size, timeout)) {
1287 *errmsg = newvstrallocf(*errmsg, _("recv error: %s"), strerror(errno));
1288 auth_debug(1, _("krb5_tcpm_recv_token: B return(-1)\n"));
1292 *errmsg = newvstrallocf(*errmsg, _("SOCKET_EOF"));
1293 auth_debug(1, _("krb5_tcpm_recv_token: B return(0)\n"));
1299 auth_debug(1, _("krb5_tcpm_recv_token: read %zd bytes from %d\n"), *size, *handle);
1301 if (*size > 0 && rc->driver->data_decrypt != NULL) {
1304 rc->driver->data_decrypt(rc, *buf, *size, &decbuf, &decsize);
1305 if (*buf != (char *)decbuf) {
1307 *buf = (char *)decbuf;