2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1993,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: krb4-security.c,v 1.18 2006/07/13 03:22:20 paddy_s Exp $
30 * krb4-security.c - helper functions for kerberos v4 security.
44 #include "security-util.h"
51 * If you don't have atexit() or on_exit(), you could just consider
52 * making atexit() empty and clean up your ticket files some other
57 #define atexit(func) on_exit(func, 0)
59 #define atexit(func) (you must to resolve lack of atexit)
60 #endif /* HAVE_ON_EXIT */
61 #endif /* ! HAVE_ATEXIT */
64 * This is the tcp stream buffer size
66 #ifndef STREAM_BUFSIZE
67 #define STREAM_BUFSIZE (32768*2)
70 int krb_set_lifetime(int);
71 int kuserok(AUTH_DAT *, char *);
74 * This is the private handle data
77 security_handle_t sech; /* MUST be first */
78 struct sockaddr_in6 peer; /* host on other side */
79 char hostname[MAX_HOSTNAME_LENGTH+1]; /* human form of above */
80 char proto_handle[32]; /* protocol handle for this req */
81 int sequence; /* last sequence number we received */
82 char inst[INST_SZ]; /* krb4 instance form of above */
83 char realm[REALM_SZ]; /* krb4 realm of this host */
84 unsigned long cksum; /* cksum of the req packet we sent */
85 des_cblock session_key; /* session key */
88 * The rest is used for the async recvpkt/recvpkt_cancel
91 void (*fn)(void *, pkt_t *, security_status_t);
92 /* func to call when packet recvd */
93 void *arg; /* argument to pass function */
94 event_handle_t *ev_timeout; /* timeout handle for recv */
95 TAILQ_ENTRY(krb4_handle) tq; /* queue handle */
99 * This is the internal security_stream data for krb4.
102 security_stream_t secstr; /* MUST be first */
103 struct krb4_handle *krb4_handle; /* pointer into above */
104 int fd; /* io file descriptor */
105 in_port_t port; /* local port this is bound to */
106 int socket; /* fd for server-side accepts */
107 event_handle_t *ev_read; /* read event handle */
108 char databuf[STREAM_BUFSIZE]; /* read buffer */
110 void (*fn)(void *, void *, ssize_t);/* read event fn */
111 void *arg; /* arg for previous */
115 * Interface functions
117 static void krb4_connect(const char *, char *(*)(char *, void *),
118 void (*)(void *, security_handle_t *, security_status_t),
120 static void krb4_accept(const struct security_driver *, int, int, void (*)(security_handle_t *, pkt_t *));
121 static void krb4_close(void *);
122 static int krb4_sendpkt(void *, pkt_t *);
123 static void krb4_recvpkt(void *, void (*)(void *, pkt_t *, security_status_t),
125 static void krb4_recvpkt_cancel(void *);
126 static void * krb4_stream_server(void *);
127 static int krb4_stream_accept(void *);
128 static void * krb4_stream_client(void *, int);
129 static void krb4_stream_close(void *);
130 static int krb4_stream_auth(void *);
131 static int krb4_stream_id(void *);
132 static int krb4_stream_write(void *, const void *, size_t);
133 static void krb4_stream_read(void *, void (*)(void *, void *, int), void *);
134 static int krb4_stream_read_sync(void *, void **);
135 static void krb4_stream_read_cancel(void *);
139 * This is our interface to the outside world.
141 const security_driver_t krb4_security_driver = {
157 krb4_stream_read_sync,
158 krb4_stream_read_cancel,
159 sec_close_connection_none,
165 * Cache the local hostname
167 static char hostname[MAX_HOSTNAME_LENGTH+1];
170 * This is the dgram_t that we use to send and recv protocol packets
171 * over the net. There is only one per process, so it lives globally
174 static dgram_t netfd;
177 * This is a queue of outstanding async requests
180 TAILQ_HEAD(, krb4_handle) tailq;
183 TAILQ_HEAD_INITIALIZER(handleq.tailq), 0
187 * Macros to add or remove krb4_handles from the above queue
189 #define handleq_add(kh) do { \
190 assert(handleq.qlength == 0 ? TAILQ_FIRST(&handleq.tailq) == NULL : 1); \
191 TAILQ_INSERT_TAIL(&handleq.tailq, kh, tq); \
195 #define handleq_remove(kh) do { \
196 assert(handleq.qlength > 0); \
197 assert(TAILQ_FIRST(&handleq.tailq) != NULL); \
198 TAILQ_REMOVE(&handleq.tailq, kh, tq); \
200 assert((handleq.qlength == 0) ^ (TAILQ_FIRST(&handleq.tailq) != NULL)); \
203 #define handleq_first() TAILQ_FIRST(&handleq.tailq)
204 #define handleq_next(kh) TAILQ_NEXT(kh, tq)
208 * This is the event manager's handle for our netfd
210 static event_handle_t *ev_netfd;
213 * This is a function that should be called if a new security_handle_t is
214 * created. If NULL, no new handles are created.
215 * It is passed the new handle and the received pkt
217 static void (*accept_fn)(security_handle_t *, pkt_t *);
221 * This is a structure used in encoding the cksum in a mutual-auth
222 * transaction. The checksum is placed in here first before encryption
223 * because encryption requires at least 8 bytes of data, and an unsigned
224 * long on most machines (32 bit ones) is 4 bytes.
234 static unsigned long krb4_cksum(const char *);
235 static void krb4_getinst(const char *, char *, size_t);
236 static void host2key(const char *, const char *, des_cblock *);
237 static void init(void);
238 static void inithandle(struct krb4_handle *, struct hostent *, int,
240 static void get_tgt(void);
241 static void killtickets(void);
242 static void recvpkt_callback(void *);
243 static void recvpkt_timeout(void *);
244 static int recv_security_ok(struct krb4_handle *, pkt_t *);
245 static void stream_read_callback(void *);
246 static void stream_read_sync_callback(void *);
247 static int knet_write(int, const void *, size_t);
248 static int knet_read(int, void *, size_t, int);
250 static int add_ticket(struct krb4_handle *, const pkt_t *, dgram_t *);
251 static void add_mutual_auth(struct krb4_handle *, dgram_t *);
252 static int check_ticket(struct krb4_handle *, const pkt_t *,
253 const char *, unsigned long);
254 static int check_mutual_auth(struct krb4_handle *, const char *);
256 static const char *kpkthdr2str(const struct krb4_handle *, const pkt_t *);
257 static int str2kpkthdr(const char *, pkt_t *, char *, size_t, int *);
259 static const char *bin2astr(const unsigned char *, int);
260 static void astr2bin(const unsigned char *, unsigned char *, int *);
262 static void encrypt_data(void *, size_t, des_cblock *);
263 static void decrypt_data(void *, size_t, des_cblock *);
265 #define HOSTNAME_INSTANCE inst
267 static char *ticketfilename = NULL;
272 if (ticketfilename != NULL)
273 unlink(ticketfilename);
274 amfree(ticketfilename);
278 * Setup some things about krb4. This should only be called once.
285 static int beenhere = 0;
291 gethostname(hostname, SIZEOF(hostname) - 1);
292 hostname[SIZEOF(hostname) - 1] = '\0';
294 if (atexit(killtickets) < 0)
295 error(_("could not setup krb4 exit handler: %s"), strerror(errno));
298 * [XXX] It could be argued that if KRBTKFILE is set outside of amanda,
299 * that it's value should be used instead of us setting one up.
300 * This file also needs to be removed so that no extra tickets are
303 g_snprintf(tktfile, SIZEOF(tktfile), "/tmp/tkt%ld-%ld.amanda",
304 (long)getuid(), (long)getpid());
305 ticketfilename = stralloc(tktfile);
306 unlink(ticketfilename);
307 krb_set_tkt_string(ticketfilename);
308 #if defined(HAVE_PUTENV)
310 char *tkt_env = stralloc2("KRBTKFILE=", ticketfilename);
314 setenv("KRBTKFILE", ticketfile, 1);
318 dgram_bind(&netfd, &port);
322 * Get a ticket granting ticket and stuff it in the cache
327 char realm[REALM_SZ];
330 strncpy(realm, krb_realmofhost(hostname), SIZEOF(realm) - 1);
331 realm[SIZEOF(realm) - 1] = '\0';
333 rc = krb_get_svc_in_tkt(SERVER_HOST_PRINCIPAL, SERVER_HOST_INSTANCE,
334 realm, "krbtgt", realm, TICKET_LIFETIME, SERVER_HOST_KEY_FILE);
337 error(_("could not get krbtgt for %s.%s@%s from %s: %s"),
338 SERVER_HOST_PRINCIPAL, SERVER_HOST_INSTANCE, realm,
339 SERVER_HOST_KEY_FILE, krb_err_txt[rc]);
342 krb_set_lifetime(TICKET_LIFETIME);
347 * krb4 version of a security handle allocator. Logically sets
348 * up a network "connection".
352 const char *hostname,
353 char * (*conf_fn)(char *, void *),
354 void (*fn)(void *, security_handle_t *, security_status_t),
358 struct krb4_handle *kh;
364 (void)conf_fn; /* Quiet unused parameter warning */
365 (void)datap; /* Quiet unused parameter warning */
367 assert(hostname != NULL);
370 * Make sure we're initted
374 kh = alloc(SIZEOF(*kh));
375 security_handleinit(&kh->sech, &krb4_security_driver);
377 if ((he = gethostbyname(hostname)) == NULL) {
378 security_seterror(&kh->sech,
379 _("%s: could not resolve hostname"), hostname);
380 (*fn)(arg, &kh->sech, S_ERROR);
383 if ((se = getservbyname(KAMANDA_SERVICE_NAME, "udp")) == NULL)
384 port = (int)KAMANDA_SERVICE_DEFAULT;
386 port = ntohs(se->s_port);
387 g_snprintf(handle, SIZEOF(handle), "%ld", (long)time(NULL));
388 inithandle(kh, he, (int)port, handle);
389 (*fn)(arg, &kh->sech, S_OK);
393 * Setup to handle new incoming connections
397 const struct security_driver *driver,
400 void (*fn)(security_handle_t *, pkt_t *))
402 (void)driver; /* Quiet unused parameter warning */
403 (void)out; /* Quiet unused parameter warning */
406 * Make sure we're initted
411 * We assume that in and out both point to the same socket
413 dgram_socket(&netfd, in);
416 * Assign the function and return. When they call recvpkt later,
417 * the recvpkt callback will call this function when it discovers
418 * new incoming connections
422 if (ev_netfd == NULL)
423 ev_netfd = event_register((event_id_t)netfd.socket, EV_READFD,
424 recvpkt_callback, NULL);
428 * Given a hostname and a port, setup a krb4_handle
432 struct krb4_handle *kh,
439 * Get the instance and realm for this host
440 * (krb_realmofhost always returns something)
442 krb4_getinst(he->h_name, kh->inst, SIZEOF(kh->inst));
443 strncpy(kh->realm, krb_realmofhost(he->h_name), SIZEOF(kh->realm) - 1);
444 kh->realm[SIZEOF(kh->realm) - 1] = '\0';
447 * Save a copy of the hostname
449 strncpy(kh->hostname, he->h_name, SIZEOF(kh->hostname) - 1);
450 kh->hostname[SIZEOF(kh->hostname) - 1] = '\0';
453 * We have no checksum or session key at this point
456 memset(kh->session_key, 0, SIZEOF(kh->session_key));
459 * Setup our peer info. We don't do anything with the sequence yet,
460 * so just leave it at 0.
462 kh->peer.sin6_family = (sa_family_t)AF_INET6;
463 kh->peer.sin6_port = (in_port_t)port;
464 kh->peer.sin6_addr = *(struct in6_addr *)he->h_addr;
465 strncpy(kh->proto_handle, handle, SIZEOF(kh->proto_handle) - 1);
466 kh->proto_handle[SIZEOF(kh->proto_handle) - 1] = '\0';
470 kh->ev_timeout = NULL;
474 * frees a handle allocated by the above
481 krb4_recvpkt_cancel(inst);
486 * Transmit a packet. Add security information first.
493 struct krb4_handle *kh = cookie;
499 * Initialize this datagram
504 * Add the header to the packet
506 dgram_cat(&netfd, kpkthdr2str(kh, pkt));
509 * Add the security info. This depends on which kind of packet we're
515 * Requests get sent with a ticket embedded in the header. The
516 * checksum is generated from the contents of the body.
518 if (add_ticket(kh, pkt, &netfd) < 0)
523 * Replies get sent with a mutual authenticator added. The
524 * mutual authenticator is the encrypted checksum from the
527 add_mutual_auth(kh, &netfd);
533 * The other types have no security stuff added for krb4.
540 * Add the body, and send it
542 dgram_cat(&netfd, pkt->body);
543 if (dgram_send_addr(&kh->peer, &netfd) != 0) {
544 security_seterror(&kh->sech,
545 _("send %s to %s failed: %s"), pkt_type2str(pkt->type),
546 kh->hostname, strerror(errno));
553 * Set up to receive a packet asyncronously, and call back when
559 void (*fn)(void *, pkt_t *, security_status_t),
563 struct krb4_handle *kh = cookie;
565 assert(netfd.socket >= 0);
569 * We register one event handler for our network fd which takes
570 * care of all of our async requests. When all async requests
571 * have either been satisfied or cancelled, we unregister our
572 * network event handler.
574 if (ev_netfd == NULL) {
575 assert(handleq.qlength == 0);
576 ev_netfd = event_register((event_id_t)netfd.socket, EV_READFD,
577 recvpkt_callback, NULL);
581 * Multiple recvpkt calls override previous ones
582 * If kh->fn is NULL then it is not in the queue.
586 if (kh->ev_timeout != NULL)
587 event_release(kh->ev_timeout);
589 kh->ev_timeout = NULL;
591 kh->ev_timeout = event_register((event_id_t)timeout, EV_TIME,
592 recvpkt_timeout, kh);
598 * Remove a async receive request from the queue
599 * If it is the last one to be removed, then remove the event handler
600 * for our network fd.
606 struct krb4_handle *kh = cookie;
610 if (kh->fn != NULL) {
615 if (kh->ev_timeout != NULL)
616 event_release(kh->ev_timeout);
617 kh->ev_timeout = NULL;
619 if (handleq.qlength == 0 && accept_fn == NULL &&
621 event_release(ev_netfd);
627 * Create the server end of a stream. For krb4, this means setup a tcp
628 * socket for receiving a connection.
634 struct krb4_handle *kh = h;
635 struct krb4_stream *ks;
639 ks = alloc(SIZEOF(*ks));
640 security_streaminit(&ks->secstr, &krb4_security_driver);
641 ks->socket = stream_server(&ks->port, STREAM_BUFSIZE, STREAM_BUFSIZE, 1);
642 if (ks->socket < 0) {
643 security_seterror(&kh->sech,
644 _("can't create server stream: %s"), strerror(errno));
649 ks->krb4_handle = kh;
655 * Accept an incoming connection on a stream_server socket
661 struct krb4_stream *ks = s;
662 struct krb4_handle *kh;
665 kh = ks->krb4_handle;
667 assert(ks->socket >= 0);
668 assert(ks->fd == -1);
670 ks->fd = stream_accept(ks->socket, 30, STREAM_BUFSIZE, STREAM_BUFSIZE);
672 security_stream_seterror(&ks->secstr,
673 _("can't accept new stream connection: %s"), strerror(errno));
680 * Return a connected stream.
687 struct krb4_handle *kh = h;
688 struct krb4_stream *ks;
692 ks = alloc(SIZEOF(*ks));
693 security_streaminit(&ks->secstr, &krb4_security_driver);
694 ks->fd = stream_client(kh->hostname, id, STREAM_BUFSIZE, STREAM_BUFSIZE,
697 security_seterror(&kh->sech,
698 _("can't connect stream to %s port %d: %s"), kh->hostname, id,
704 ks->socket = -1; /* we're a client */
705 ks->krb4_handle = kh;
711 * Close and unallocate resources for a stream.
717 struct krb4_stream *ks = s;
723 if (ks->socket != -1)
725 krb4_stream_read_cancel(ks);
730 * Authenticate a stream
732 * XXX this whole thing assumes the size of struct timeval is consistent,
733 * which is may not be! We need to extract the network byte order data
734 * into byte arrays and send those.
740 struct krb4_stream *ks = s;
741 struct krb4_handle *kh;
743 struct timeval local, enc;
750 kh = ks->krb4_handle;
752 /* make sure we're open */
755 /* make sure our timeval is what we're expecting, see above */
756 assert(SIZEOF(struct timeval) == 8);
759 * Get the current time, put it in network byte order, encrypt it
760 * and present it to the other side.
762 gettimeofday(&local, &tz);
763 enc.tv_sec = (long)htonl((guint32)local.tv_sec);
764 enc.tv_usec = (long)htonl((guint32)local.tv_usec);
765 encrypt_data(&enc, SIZEOF(enc), &kh->session_key);
766 if (knet_write(fd, &enc, SIZEOF(enc)) < 0) {
767 security_stream_seterror(&ks->secstr,
768 _("krb4 stream handshake write error: %s"), strerror(errno));
773 * Read back the other side's presentation. Increment the seconds
774 * and useconds by one. Reencrypt, and present to the other side.
775 * Timeout in 10 seconds.
777 if (knet_read(fd, &enc, SIZEOF(enc), 60) < 0) {
778 security_stream_seterror(&ks->secstr,
779 _("krb4 stream handshake read error: %s"), strerror(errno));
782 decrypt_data(&enc, SIZEOF(enc), &kh->session_key);
783 /* XXX do timestamp checking here */
784 enc.tv_sec = (long)htonl(ntohl((guint32)enc.tv_sec) + 1);
785 enc.tv_usec =(long)htonl(ntohl((guint32)enc.tv_usec) + 1);
786 encrypt_data(&enc, SIZEOF(enc), &kh->session_key);
788 if (knet_write(fd, &enc, SIZEOF(enc)) < 0) {
789 security_stream_seterror(&ks->secstr,
790 _("krb4 stream handshake write error: %s"), strerror(errno));
795 * Read back the other side's processing of our data.
796 * If they incremented it properly, then succeed.
797 * Timeout in 10 seconds.
799 if (knet_read(fd, &enc, SIZEOF(enc), 60) < 0) {
800 security_stream_seterror(&ks->secstr,
801 _("krb4 stream handshake read error: %s"), strerror(errno));
804 decrypt_data(&enc, SIZEOF(enc), &kh->session_key);
805 if ((ntohl((guint32)enc.tv_sec) == (uint32_t)(local.tv_sec + 1)) &&
806 (ntohl((guint32)enc.tv_usec) == (uint32_t)(local.tv_usec + 1)))
809 security_stream_seterror(&ks->secstr,
810 _("krb4 handshake failed: sent %ld,%ld - recv %ld,%ld"),
811 (long)(local.tv_sec + 1), (long)(local.tv_usec + 1),
812 (long)ntohl((guint32)enc.tv_sec),
813 (long)ntohl((guint32)enc.tv_usec));
818 * Returns the stream id for this stream. This is just the local
825 struct krb4_stream *ks = s;
833 * Write a chunk of data to a stream. Blocks until completion.
841 struct krb4_stream *ks = s;
845 if (knet_write(ks->fd, buf, size) < 0) {
846 security_stream_seterror(&ks->secstr,
847 _("write error on stream %d: %s"), ks->fd, strerror(errno));
854 * Submit a request to read some data. Calls back with the given
855 * function and arg when completed.
860 void (*fn)(void *, void *, ssize_t),
863 struct krb4_stream *ks = s;
868 * Only one read request can be active per stream.
870 if (ks->ev_read != NULL)
871 event_release(ks->ev_read);
873 ks->ev_read = event_register((event_id_t)ks->fd, EV_READFD,
874 stream_read_callback, ks);
880 * Write a chunk of data to a stream. Blocks until completion.
883 krb4_stream_read_sync(
887 struct krb4_stream *ks = s;
889 (void)buf; /* Quiet unused variable warning */
892 if (ks->ev_read != NULL)
893 event_release(ks->ev_read);
895 ks->ev_read = event_register((event_id_t)ks->fd, EV_READFD,
896 stream_read_sync_callback, ks);
897 event_wait(ks->ev_read);
898 return((ssize_t)ks->len);
902 * Callback for krb4_stream_read_sync
905 stream_read_sync_callback(
908 struct krb4_stream *ks = arg;
912 assert(ks->fd != -1);
915 * Remove the event first, and then call the callback.
916 * We remove it first because we don't want to get in their
917 * way if they reschedule it.
919 krb4_stream_read_cancel(ks);
920 n = read(ks->fd, ks->databuf, sizeof(ks->databuf));
922 security_stream_seterror(&ks->secstr,
928 * Cancel a previous stream read request. It's ok if we didn't have a read
932 krb4_stream_read_cancel(
935 struct krb4_stream *ks = s;
939 if (ks->ev_read != NULL) {
940 event_release(ks->ev_read);
946 * Callback for krb4_stream_read
949 stream_read_callback(
952 struct krb4_stream *ks = arg;
956 assert(ks->fd != -1);
959 * Remove the event first, and then call the callback.
960 * We remove it first because we don't want to get in their
961 * way if they reschedule it.
963 krb4_stream_read_cancel(ks);
964 n = read(ks->fd, ks->databuf, SIZEOF(ks->databuf));
966 security_stream_seterror(&ks->secstr,
968 (*ks->fn)(ks->arg, ks->databuf, n);
972 * The callback for recvpkt() for the event handler
973 * Determines if this packet is for this security handle,
974 * and does the real callback if so.
981 struct sockaddr_in6 peer;
984 struct krb4_handle *kh;
986 void (*fn)(void *, pkt_t *, security_status_t);
989 (void)cookie; /* Quiet unused parameter warning */
990 assert(cookie == NULL);
993 * Find the handle that this packet is associated with. We
994 * need to peek at the packet to see what is in it, but we
995 * want to save the actual reading for later.
998 if (dgram_recv(&netfd, 0, &peer) < 0)
1000 if (str2kpkthdr(netfd.cur, &pkt, handle, SIZEOF(handle), &sequence) < 0)
1003 for (kh = handleq_first(); kh != NULL; kh = handleq_next(kh)) {
1004 if (strcmp(kh->proto_handle, handle) == 0 &&
1005 cmp_sockaddr(&kh->peer, &peer, 0) == 0) {
1006 kh->sequence = sequence;
1009 * We need to cancel the recvpkt request before calling
1010 * the callback because the callback may reschedule us.
1014 krb4_recvpkt_cancel(kh);
1015 if (recv_security_ok(kh, &pkt) < 0)
1016 (*fn)(arg, NULL, S_ERROR);
1018 (*fn)(arg, &pkt, S_OK);
1023 * If we didn't find a handle, then check for a new incoming packet.
1024 * If no accept handler was setup, then just return.
1026 if (accept_fn == NULL)
1029 he = gethostbyaddr((void *)&peer.sin6_addr, SIZEOF(peer.sin6_addr), AF_INET6);
1032 kh = alloc(SIZEOF(*kh));
1033 security_handleinit(&kh->sech, &krb4_security_driver);
1034 inithandle(kh, he, (int)peer.sin6_port, handle);
1037 * Check the security of the packet. If it is bad, then pass NULL
1038 * to the accept function instead of a packet.
1040 if (recv_security_ok(kh, &pkt) < 0)
1041 (*accept_fn)(&kh->sech, NULL);
1043 (*accept_fn)(&kh->sech, &pkt);
1047 * This is called when a handle times out before receiving a packet.
1053 struct krb4_handle *kh = cookie;
1054 void (*fn)(void *, pkt_t *, security_status_t);
1059 assert(kh->ev_timeout != NULL);
1062 krb4_recvpkt_cancel(kh);
1063 (*fn)(arg, NULL, S_TIMEOUT);
1067 * Add a ticket to the message
1071 struct krb4_handle *kh,
1080 kh->cksum = (long)krb4_cksum(pkt->body);
1081 #if CLIENT_HOST_INSTANCE == HOSTNAME_INSTANCE
1083 * User requested that all instances be based on the target
1086 strncpy(inst, kh->inst, SIZEOF(inst) - 1);
1089 * User requested a fixed instance.
1091 strncpy(inst, CLIENT_HOST_INSTANCE, SIZEOF(inst) - 1);
1093 inst[SIZEOF(inst) - 1] = '\0';
1096 * Get a ticket with the user-defined service and instance,
1097 * and using the checksum of the body of the request packet.
1099 rc = krb_mk_req(&ticket, CLIENT_HOST_PRINCIPAL, inst, kh->realm,
1101 if (rc == NO_TKT_FIL) {
1102 /* It's been kdestroyed. Get a new one and try again */
1104 rc = krb_mk_req(&ticket, CLIENT_HOST_PRINCIPAL, inst, kh->realm,
1108 security_seterror(&kh->sech,
1109 _("krb_mk_req failed: %s (%d)"), error_message(rc), rc);
1113 * We now have the ticket. Put it into the packet, and send
1116 security = vstralloc("SECURITY TICKET ",
1117 bin2astr(ticket.dat, ticket.length), "\n", NULL);
1118 dgram_cat(msg, security);
1125 * Add the mutual authenticator. This is the checksum from
1130 struct krb4_handle *kh,
1133 union mutual mutual;
1137 assert(msg != NULL);
1138 assert(kh->cksum != 0);
1139 assert(kh->session_key[0] != '\0');
1141 memset(&mutual, 0, SIZEOF(mutual));
1142 mutual.cksum = (unsigned long)htonl((guint32)kh->cksum + 1);
1143 encrypt_data(&mutual, SIZEOF(mutual), &kh->session_key);
1145 security = vstralloc("SECURITY MUTUAL-AUTH ",
1146 bin2astr((unsigned char *)mutual.pad,
1147 (int)sizeof(mutual.pad)), "\n", NULL);
1148 dgram_cat(msg, security);
1153 * Check the security of a received packet. Returns negative on error
1154 * or security violation and otherwise returns 0 and fills in the
1159 struct krb4_handle *kh,
1162 char *tok, *security, *body;
1163 unsigned long cksum;
1166 assert(pkt != NULL);
1169 * Set this preemptively before we mangle the body.
1171 security_seterror(&kh->sech,
1172 _("bad %s SECURITY line from %s: '%s'"), pkt_type2str(pkt->type),
1173 kh->hostname, pkt->body);
1177 * The first part of the body should be the security info. Deal with it.
1178 * We only need to do this on a few packet types.
1180 * First, parse the SECURITY line in the packet, if it exists.
1181 * Increment the cur pointer past it to the data section after
1182 * parsing is finished.
1184 if (strncmp(pkt->body, "SECURITY", SIZEOF("SECURITY") - 1) == 0) {
1185 tok = strtok(pkt->body, " ");
1186 assert(strcmp(tok, "SECURITY") == 0);
1187 /* security info goes until the newline */
1188 security = strtok(NULL, "\n");
1189 body = strtok(NULL, "");
1191 * If the body is f-ked, then try to recover.
1194 if (security != NULL)
1195 body = security + strlen(security) + 2;
1205 * Get a checksum of the non-security parts of the body
1207 cksum = krb4_cksum(body);
1210 * Now deal with the data we did (or didn't) parse above
1212 switch (pkt->type) {
1215 * Request packets have a ticket after the security tag
1216 * Get the ticket, make sure the checksum agrees with the
1217 * checksum of the request we sent.
1219 * Must look like: SECURITY TICKET [ticket str]
1222 /* there must be some security info */
1223 if (security == NULL)
1226 /* second word must be TICKET */
1227 if ((tok = strtok(security, " ")) == NULL)
1229 if (strcmp(tok, "TICKET") != 0) {
1230 security_seterror(&kh->sech,
1231 _("REQ SECURITY line parse error, expecting TICKET, got %s"), tok);
1235 /* the third word is the encoded ticket */
1236 if ((tok = strtok(NULL, "")) == NULL)
1238 if (check_ticket(kh, pkt, tok, cksum) < 0)
1241 /* we're good to go */
1246 * Reply packets check the mutual authenticator for this host.
1248 * Must look like: SECURITY MUTUAL-AUTH [mutual auth str]
1250 if (security == NULL)
1252 if ((tok = strtok(security, " ")) == NULL)
1254 if (strcmp(tok, "MUTUAL-AUTH") != 0) {
1255 security_seterror(&kh->sech,
1256 "REP SECURITY line parse error, expecting MUTUAL-AUTH, got %s",
1260 if ((tok = strtok(NULL, "")) == NULL)
1262 if (check_mutual_auth(kh, tok) < 0)
1269 * These packets have no security. They should, but such
1270 * is life. We can't change it without breaking compatibility.
1272 * XXX Should we complain if argc > 0? (ie, some security info was
1280 * If there is security info at the front of the packet, we need
1281 * to shift the rest of the data up and nuke it.
1283 if (body != pkt->body)
1284 memmove(pkt->body, body, strlen(body) + 1);
1289 * Check the ticket in a REQ packet for authenticity
1293 struct krb4_handle *kh,
1295 const char * ticket_str,
1296 unsigned long cksum)
1305 (void)pkt; /* Quiet unused parameter warning */
1308 assert(pkt != NULL);
1309 assert(ticket_str != NULL);
1311 ticket.length = (int)sizeof(ticket.dat);
1312 astr2bin((unsigned char *)ticket_str, ticket.dat, &ticket.length);
1313 assert(ticket.length > 0);
1315 /* get a copy of the instance into writable memory */
1316 #if CLIENT_HOST_INSTANCE == HOSTNAME_INSTANCE
1317 strncpy(inst, krb_get_phost(hostname), SIZEOF(inst) - 1);
1319 strncpy(inst, CLIENT_HOST_INSTANCE, SIZEOF(inst) - 1);
1321 inst[SIZEOF(inst) - 1] = '\0';
1323 /* get the checksum out of the ticket */
1324 rc = krb_rd_req(&ticket, CLIENT_HOST_PRINCIPAL, inst,
1325 kh->peer.sin6_addr.s_addr, &auth, CLIENT_HOST_KEY_FILE);
1327 security_seterror(&kh->sech,
1328 _("krb_rd_req failed for %s: %s (%d)"), kh->hostname,
1329 error_message(rc), rc);
1333 /* verify and save the checksum and session key */
1334 if (auth.checksum != cksum) {
1335 security_seterror(&kh->sech,
1336 _("krb4 checksum mismatch for %s (remote=%lu, local=%lu)"),
1337 kh->hostname, (long)auth.checksum, cksum);
1340 kh->cksum = (unsigned long)cksum;
1341 memcpy(kh->session_key, auth.session, SIZEOF(kh->session_key));
1344 * If CHECK_USERID is set, then we need to specifically
1345 * check the userid we're forcing ourself to. Otherwise,
1346 * just check the login we're currently setuid to.
1349 if ((pwd = getpwnam(CLIENT_LOGIN)) == NULL)
1350 error(_("error [getpwnam(%s) fails]"), CLIENT_LOGIN);
1352 if ((pwd = getpwuid(getuid())) == NULL)
1353 error(_("error [getpwuid(%d) fails]"), getuid());
1356 /* save the username in case it's overwritten */
1357 user = stralloc(pwd->pw_name);
1359 /* check the klogin file */
1360 if (kuserok(&auth, user)) {
1361 security_seterror(&kh->sech,
1362 _("access as %s not allowed from %s.%s@%s"), user, auth.pname,
1363 auth.pinst, auth.prealm);
1374 * Verify that the packet received is secure by verifying that it has
1375 * the same checksum as our request + 1.
1379 struct krb4_handle *kh,
1380 const char * mutual_auth_str)
1382 union mutual mutual;
1386 assert(mutual_auth_str != NULL);
1387 assert(kh->inst[0] != '\0');
1388 /* we had better have a checksum from a request we sent */
1389 assert(kh->cksum != 0);
1391 /* convert the encoded string into binary data */
1392 len = (int)sizeof(mutual);
1393 astr2bin((unsigned char *)mutual_auth_str, (unsigned char *)&mutual, &len);
1395 /* unencrypt the string using the key in the ticket file */
1396 host2key(kh->hostname, kh->inst, &kh->session_key);
1397 decrypt_data(&mutual, (size_t)len, &kh->session_key);
1398 mutual.cksum = (unsigned long)ntohl((guint32)mutual.cksum);
1400 /* the data must be the same as our request cksum + 1 */
1401 if (mutual.cksum != (kh->cksum + 1)) {
1402 security_seterror(&kh->sech,
1403 _("krb4 checksum mismatch from %s (remote=%lu, local=%lu)"),
1404 kh->hostname, mutual.cksum, kh->cksum + 1);
1412 * Convert a pkt_t into a header string for our packet
1416 const struct krb4_handle * kh,
1419 static char retbuf[256];
1422 assert(pkt != NULL);
1424 g_snprintf(retbuf, SIZEOF(retbuf), "Amanda %d.%d %s HANDLE %s SEQ %d\n",
1425 VERSION_MAJOR, VERSION_MINOR, pkt_type2str(pkt->type),
1426 kh->proto_handle, kh->sequence);
1428 /* check for truncation. If only we had asprintf()... */
1429 assert(retbuf[strlen(retbuf) - 1] == '\n');
1435 * Parses out the header line in 'str' into the pkt and handle
1436 * Returns negative on parse error.
1440 const char *origstr,
1449 assert(origstr != NULL);
1450 assert(pkt != NULL);
1452 str = stralloc(origstr);
1454 /* "Amanda %d.%d <ACK,NAK,...> HANDLE %s SEQ %d\n" */
1456 /* Read in "Amanda" */
1457 if ((tok = strtok(str, " ")) == NULL || strcmp(tok, "Amanda") != 0)
1460 /* nothing is done with the major/minor numbers currently */
1461 if ((tok = strtok(NULL, " ")) == NULL || strchr(tok, '.') == NULL)
1464 /* Read in the packet type */
1465 if ((tok = strtok(NULL, " ")) == NULL)
1467 pkt_init(pkt, pkt_str2type(tok), "");
1468 if (pkt->type == (pktype_t)-1)
1471 /* Read in "HANDLE" */
1472 if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "HANDLE") != 0)
1475 /* parse the handle */
1476 if ((tok = strtok(NULL, " ")) == NULL)
1478 strncpy(handle, tok, handlesize - 1);
1479 handle[handlesize - 1] = '\0';
1482 if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "SEQ") != 0)
1485 /* parse the sequence number */
1486 if ((tok = strtok(NULL, "\n")) == NULL)
1488 *sequence = atoi(tok);
1490 /* Save the body, if any */
1491 if ((tok = strtok(NULL, "")) != NULL)
1498 #if 0 /* XXX we have no way of passing this back up */
1499 security_seterror(&kh->sech,
1500 _("parse error in packet header : '%s'"), origstr);
1508 const char *hostname,
1515 strncpy(realm, krb_realmofhost((char *)hostname), SIZEOF(realm) - 1);
1516 realm[SIZEOF(realm) - 1] = '\0';
1517 #if CLIENT_HOST_INSTANCE != HOSTNAME_INSTANCE
1518 inst = CLIENT_HOST_INSTANCE
1520 krb_get_cred(CLIENT_HOST_PRINCIPAL, (char *)inst, realm, &cred);
1521 memcpy(key, cred.session, SIZEOF(des_cblock));
1526 * Convert a chunk of data into a string.
1530 const unsigned char *buf,
1533 static const char tohex[] = "0123456789ABCDEF";
1534 static char *str = NULL;
1536 const unsigned char *p;
1541 * calculate output string len
1542 * We quote everything, so each input byte == 3 output chars, plus
1543 * two more for quotes
1545 slen = ((size_t)len * 3) + 2;
1547 /* allocate string and fill it in */
1550 str = alloc(slen + 1);
1554 for (i = 0; i < len; i++) {
1556 *q++ = tohex[(*p >> 4) & 0xF];
1557 *q++ = tohex[*p & 0xF];
1563 /* make sure we didn't overrun our allocated buffer */
1564 assert((size_t)(q - str) == slen);
1570 * Convert an encoded string into a block of data bytes
1574 const unsigned char *astr,
1575 unsigned char * buf,
1578 const unsigned char *p;
1580 #define fromhex(h) (isdigit((int)h) ? (h) - '0' : (h) - 'A' + 10)
1583 * Skip leading quote, if any
1589 * Run through the string. Anything starting with a $ is a three
1590 * char representation of this byte. Everything else is literal.
1592 for (p = astr, q = buf; *p != '"' && *p != '\0'; ) {
1596 *q++ = (fromhex(p[1]) << 4) + fromhex(p[2]);
1599 if ((int)(q - buf) >= *lenp)
1605 static unsigned long
1611 memset(seed, 0, SIZEOF(seed));
1613 * The first arg is an unsigned char * in some krb4 implementations,
1614 * and in others, it's a des_cblock *. Just make it void here
1615 * to shut them all up.
1617 return (quad_cksum((void *)str, NULL, (long)strlen(str), 1, &seed));
1628 * Copy the first part of the hostname up to the first '.' into
1629 * the buffer provided. Leave room for a NULL.
1631 while (size > 1 && *hname != '\0' && *hname != '.') {
1632 *inst++ = isupper((int)*hname) ? tolower((int)*hname) : *hname;
1645 des_key_schedule sched;
1648 * First arg is des_cblock * in some places, and just des_cblock in
1649 * others. Since des_cblock is a char array, they are equivalent.
1650 * Just cast it to void * to keep both compilers quiet. typedefing
1651 * arrays should be outlawed.
1653 des_key_sched((void *)key, sched);
1654 des_pcbc_encrypt(data, data, (long)length, sched, key, DES_ENCRYPT);
1664 des_key_schedule sched;
1666 des_key_sched((void *)key, sched);
1667 des_pcbc_encrypt(data, data, (long)length, sched, key, DES_DECRYPT);
1671 * like write(), but always writes out the entire buffer.
1679 const char *buf = vbuf; /* so we can do ptr arith */
1683 n = write(fd, buf, size);
1693 * Like read(), but waits until the entire buffer has been filled.
1702 char *buf = vbuf; /* ptr arith */
1705 SELECT_ARG_TYPE readfds;
1710 FD_SET(fd, &readfds);
1711 tv.tv_sec = timeout;
1713 switch (select(fd + 1, &readfds, NULL, NULL, &tv)) {
1720 assert(FD_ISSET(fd, &readfds));
1726 n = read(fd, buf, size);
1729 /* we only tolerate so many eof's */
1730 if (n == 0 && ++neof > 1024) {
1741 /* -------------------------- */
1742 /* debug routines */
1747 const unsigned char * buf,
1752 dbprintf("%s:", str);
1753 for(i=0;i<len;i++) {
1756 dbprintf(" %02X", buf[i]);
1766 dbprintf(_("%s: length %d chk %lX\n"), str, tktp->length, tktp->mbz);
1767 print_hex(_("ticket data"), tktp->dat, tktp->length);
1775 g_printf("\nAuth Data:\n");
1776 g_printf(" Principal \"%s\" Instance \"%s\" Realm \"%s\"\n",
1777 authp->pname, authp->pinst, authp->prealm);
1778 g_printf(" cksum %d life %d keylen %ld\n", authp->checksum,
1779 authp->life, SIZEOF(authp->session));
1780 print_hex("session key", authp->session, SIZEOF(authp->session));
1788 g_printf("\nCredentials:\n");
1789 g_printf(" service \"%s\" instance \"%s\" realm \"%s\" life %d kvno %d\n",
1790 credp->service, credp->instance, credp->realm, credp->lifetime,
1792 print_hex("session key", credp->session, SIZEOF(credp->session));
1793 print_hex("ticket", credp->ticket_st.dat, credp->ticket_st.length);