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.
45 #include "security-util.h"
52 * If you don't have atexit() or on_exit(), you could just consider
53 * making atexit() empty and clean up your ticket files some other
58 #define atexit(func) on_exit(func, 0)
60 #define atexit(func) (you must to resolve lack of atexit)
61 #endif /* HAVE_ON_EXIT */
62 #endif /* ! HAVE_ATEXIT */
64 int krb_set_lifetime(int);
65 int kuserok(AUTH_DAT *, char *);
68 * This is the private handle data
71 security_handle_t sech; /* MUST be first */
72 struct sockaddr_in6 peer; /* host on other side */
73 char hostname[MAX_HOSTNAME_LENGTH+1]; /* human form of above */
74 char proto_handle[32]; /* protocol handle for this req */
75 int sequence; /* last sequence number we received */
76 char inst[INST_SZ]; /* krb4 instance form of above */
77 char realm[REALM_SZ]; /* krb4 realm of this host */
78 unsigned long cksum; /* cksum of the req packet we sent */
79 des_cblock session_key; /* session key */
82 * The rest is used for the async recvpkt/recvpkt_cancel
85 void (*fn)(void *, pkt_t *, security_status_t);
86 /* func to call when packet recvd */
87 void *arg; /* argument to pass function */
88 event_handle_t *ev_timeout; /* timeout handle for recv */
89 TAILQ_ENTRY(krb4_handle) tq; /* queue handle */
93 * This is the internal security_stream data for krb4.
96 security_stream_t secstr; /* MUST be first */
97 struct krb4_handle *krb4_handle; /* pointer into above */
98 int fd; /* io file descriptor */
99 int port; /* local port this is bound to */
100 int socket; /* fd for server-side accepts */
101 event_handle_t *ev_read; /* read event handle */
102 char databuf[MAX_TAPE_BLOCK_BYTES]; /* read buffer */
104 void (*fn)(void *, void *, ssize_t);/* read event fn */
105 void *arg; /* arg for previous */
109 * This is the tcp stream buffer size
111 #define STREAM_BUFSIZE (MAX_TAPE_BLOCK_BYTES * 2)
114 * Interface functions
116 static void krb4_connect(const char *, char *(*)(char *, void *),
117 void (*)(void *, security_handle_t *, security_status_t),
119 static void krb4_accept(const struct security_driver *, int, int, void (*)(security_handle_t *, pkt_t *));
120 static void krb4_close(void *);
121 static int krb4_sendpkt(void *, pkt_t *);
122 static void krb4_recvpkt(void *, void (*)(void *, pkt_t *, security_status_t),
124 static void krb4_recvpkt_cancel(void *);
125 static void * krb4_stream_server(void *);
126 static int krb4_stream_accept(void *);
127 static void * krb4_stream_client(void *, int);
128 static void krb4_stream_close(void *);
129 static int krb4_stream_auth(void *);
130 static int krb4_stream_id(void *);
131 static int krb4_stream_write(void *, const void *, size_t);
132 static void krb4_stream_read(void *, void (*)(void *, void *, int), void *);
133 static int krb4_stream_read_sync(void *, void **);
134 static void krb4_stream_read_cancel(void *);
138 * This is our interface to the outside world.
140 const security_driver_t krb4_security_driver = {
156 krb4_stream_read_sync,
157 krb4_stream_read_cancel,
158 sec_close_connection_none,
164 * Cache the local hostname
166 static char hostname[MAX_HOSTNAME_LENGTH+1];
169 * This is the dgram_t that we use to send and recv protocol packets
170 * over the net. There is only one per process, so it lives globally
173 static dgram_t netfd;
176 * This is a queue of outstanding async requests
179 TAILQ_HEAD(, krb4_handle) tailq;
182 TAILQ_HEAD_INITIALIZER(handleq.tailq), 0
186 * Macros to add or remove krb4_handles from the above queue
188 #define handleq_add(kh) do { \
189 assert(handleq.qlength == 0 ? TAILQ_FIRST(&handleq.tailq) == NULL : 1); \
190 TAILQ_INSERT_TAIL(&handleq.tailq, kh, tq); \
194 #define handleq_remove(kh) do { \
195 assert(handleq.qlength > 0); \
196 assert(TAILQ_FIRST(&handleq.tailq) != NULL); \
197 TAILQ_REMOVE(&handleq.tailq, kh, tq); \
199 assert((handleq.qlength == 0) ^ (TAILQ_FIRST(&handleq.tailq) != NULL)); \
202 #define handleq_first() TAILQ_FIRST(&handleq.tailq)
203 #define handleq_next(kh) TAILQ_NEXT(kh, tq)
207 * This is the event manager's handle for our netfd
209 static event_handle_t *ev_netfd;
212 * This is a function that should be called if a new security_handle_t is
213 * created. If NULL, no new handles are created.
214 * It is passed the new handle and the received pkt
216 static void (*accept_fn)(security_handle_t *, pkt_t *);
220 * This is a structure used in encoding the cksum in a mutual-auth
221 * transaction. The checksum is placed in here first before encryption
222 * because encryption requires at least 8 bytes of data, and an unsigned
223 * long on most machines (32 bit ones) is 4 bytes.
233 static unsigned long krb4_cksum(const char *);
234 static void krb4_getinst(const char *, char *, size_t);
235 static void host2key(const char *, const char *, des_cblock *);
236 static void init(void);
237 static void inithandle(struct krb4_handle *, struct hostent *, int,
239 static void get_tgt(void);
240 static void killtickets(void);
241 static void recvpkt_callback(void *);
242 static void recvpkt_timeout(void *);
243 static int recv_security_ok(struct krb4_handle *, pkt_t *);
244 static void stream_read_callback(void *);
245 static void stream_read_sync_callback(void *);
246 static int net_write(int, const void *, size_t);
247 static int net_read(int, void *, size_t, int);
249 static int add_ticket(struct krb4_handle *, const pkt_t *, dgram_t *);
250 static void add_mutual_auth(struct krb4_handle *, dgram_t *);
251 static int check_ticket(struct krb4_handle *, const pkt_t *,
252 const char *, unsigned long);
253 static int check_mutual_auth(struct krb4_handle *, const char *);
255 static const char *pkthdr2str(const struct krb4_handle *, const pkt_t *);
256 static int str2pkthdr(const char *, pkt_t *, char *, size_t, int *);
258 static const char *bin2astr(const unsigned char *, int);
259 static void astr2bin(const unsigned char *, unsigned char *, int *);
261 static void encrypt_data(void *, size_t, des_cblock *);
262 static void decrypt_data(void *, size_t, des_cblock *);
264 #define HOSTNAME_INSTANCE inst
266 static char *ticketfilename = NULL;
271 if (ticketfilename != NULL)
272 unlink(ticketfilename);
273 amfree(ticketfilename);
277 * Setup some things about krb4. This should only be called once.
284 static int beenhere = 0;
290 gethostname(hostname, SIZEOF(hostname) - 1);
291 hostname[SIZEOF(hostname) - 1] = '\0';
293 if (atexit(killtickets) < 0)
294 error("could not setup krb4 exit handler: %s", strerror(errno));
297 * [XXX] It could be argued that if KRBTKFILE is set outside of amanda,
298 * that it's value should be used instead of us setting one up.
299 * This file also needs to be removed so that no extra tickets are
302 snprintf(tktfile, SIZEOF(tktfile), "/tmp/tkt%ld-%ld.amanda",
303 (long)getuid(), (long)getpid());
304 ticketfilename = stralloc(tktfile);
305 unlink(ticketfilename);
306 krb_set_tkt_string(ticketfilename);
307 #if defined(HAVE_PUTENV)
309 char *tkt_env = stralloc2("KRBTKFILE=", ticketfilename);
313 setenv("KRBTKFILE", ticketfile, 1);
317 dgram_bind(&netfd, &port);
321 * Get a ticket granting ticket and stuff it in the cache
326 char realm[REALM_SZ];
329 strncpy(realm, krb_realmofhost(hostname), SIZEOF(realm) - 1);
330 realm[SIZEOF(realm) - 1] = '\0';
332 rc = krb_get_svc_in_tkt(SERVER_HOST_PRINCIPLE, SERVER_HOST_INSTANCE,
333 realm, "krbtgt", realm, TICKET_LIFETIME, SERVER_HOST_KEY_FILE);
336 error("could not get krbtgt for %s.%s@%s from %s: %s",
337 SERVER_HOST_PRINCIPLE, SERVER_HOST_INSTANCE, realm,
338 SERVER_HOST_KEY_FILE, krb_err_txt[rc]);
341 krb_set_lifetime(TICKET_LIFETIME);
346 * krb4 version of a security handle allocator. Logically sets
347 * up a network "connection".
351 const char *hostname,
352 char * (*conf_fn)(char *, void *),
353 void (*fn)(void *, security_handle_t *, security_status_t),
357 struct krb4_handle *kh;
363 assert(hostname != NULL);
366 * Make sure we're initted
370 kh = alloc(SIZEOF(*kh));
371 security_handleinit(&kh->sech, &krb4_security_driver);
373 if ((he = gethostbyname(hostname)) == NULL) {
374 security_seterror(&kh->sech,
375 "%s: could not resolve hostname", hostname);
376 (*fn)(arg, &kh->sech, S_ERROR);
379 if ((se = getservbyname(KAMANDA_SERVICE_NAME, "udp")) == NULL)
380 port = (int)KAMANDA_SERVICE_DEFAULT;
382 port = ntohs(se->s_port);
383 snprintf(handle, SIZEOF(handle), "%ld", (long)time(NULL));
384 inithandle(kh, he, (int)port, handle);
385 (*fn)(arg, &kh->sech, S_OK);
389 * Setup to handle new incoming connections
393 const struct security_driver *driver,
396 void (*fn)(security_handle_t *, pkt_t *))
400 * Make sure we're initted
405 * We assume that in and out both point to the same socket
407 dgram_socket(&netfd, in);
410 * Assign the function and return. When they call recvpkt later,
411 * the recvpkt callback will call this function when it discovers
412 * new incoming connections
416 if (ev_netfd == NULL)
417 ev_netfd = event_register((event_id_t)netfd.socket, EV_READFD,
418 recvpkt_callback, NULL);
422 * Given a hostname and a port, setup a krb4_handle
426 struct krb4_handle *kh,
433 * Get the instance and realm for this host
434 * (krb_realmofhost always returns something)
436 krb4_getinst(he->h_name, kh->inst, SIZEOF(kh->inst));
437 strncpy(kh->realm, krb_realmofhost(he->h_name), SIZEOF(kh->realm) - 1);
438 kh->realm[SIZEOF(kh->realm) - 1] = '\0';
441 * Save a copy of the hostname
443 strncpy(kh->hostname, he->h_name, SIZEOF(kh->hostname) - 1);
444 kh->hostname[SIZEOF(kh->hostname) - 1] = '\0';
447 * We have no checksum or session key at this point
450 memset(kh->session_key, 0, SIZEOF(kh->session_key));
453 * Setup our peer info. We don't do anything with the sequence yet,
454 * so just leave it at 0.
456 kh->peer.sin6_family = (sa_family_t)AF_INET6;
457 kh->peer.sin6_port = (in_port_t)port;
458 kh->peer.sin6_addr = *(struct in6_addr *)he->h_addr;
459 strncpy(kh->proto_handle, handle, SIZEOF(kh->proto_handle) - 1);
460 kh->proto_handle[SIZEOF(kh->proto_handle) - 1] = '\0';
464 kh->ev_timeout = NULL;
468 * frees a handle allocated by the above
475 krb4_recvpkt_cancel(inst);
480 * Transmit a packet. Add security information first.
487 struct krb4_handle *kh = cookie;
493 * Initialize this datagram
498 * Add the header to the packet
500 dgram_cat(&netfd, pkthdr2str(kh, pkt));
503 * Add the security info. This depends on which kind of packet we're
509 * Requests get sent with a ticket embedded in the header. The
510 * checksum is generated from the contents of the body.
512 if (add_ticket(kh, pkt, &netfd) < 0)
517 * Replies get sent with a mutual authenticator added. The
518 * mutual authenticator is the encrypted checksum from the
521 add_mutual_auth(kh, &netfd);
527 * The other types have no security stuff added for krb4.
534 * Add the body, and send it
536 dgram_cat(&netfd, pkt->body);
537 if (dgram_send_addr(&kh->peer, &netfd) != 0) {
538 security_seterror(&kh->sech,
539 "send %s to %s failed: %s", pkt_type2str(pkt->type),
540 kh->hostname, strerror(errno));
547 * Set up to receive a packet asyncronously, and call back when
553 void (*fn)(void *, pkt_t *, security_status_t),
557 struct krb4_handle *kh = cookie;
559 assert(netfd.socket >= 0);
563 * We register one event handler for our network fd which takes
564 * care of all of our async requests. When all async requests
565 * have either been satisfied or cancelled, we unregister our
566 * network event handler.
568 if (ev_netfd == NULL) {
569 assert(handleq.qlength == 0);
570 ev_netfd = event_register((event_id_t)netfd.socket, EV_READFD,
571 recvpkt_callback, NULL);
575 * Multiple recvpkt calls override previous ones
576 * If kh->fn is NULL then it is not in the queue.
580 if (kh->ev_timeout != NULL)
581 event_release(kh->ev_timeout);
583 kh->ev_timeout = NULL;
585 kh->ev_timeout = event_register((event_id_t)timeout, EV_TIME,
586 recvpkt_timeout, kh);
592 * Remove a async receive request from the queue
593 * If it is the last one to be removed, then remove the event handler
594 * for our network fd.
600 struct krb4_handle *kh = cookie;
604 if (kh->fn != NULL) {
609 if (kh->ev_timeout != NULL)
610 event_release(kh->ev_timeout);
611 kh->ev_timeout = NULL;
613 if (handleq.qlength == 0 && accept_fn == NULL &&
615 event_release(ev_netfd);
621 * Create the server end of a stream. For krb4, this means setup a tcp
622 * socket for receiving a connection.
628 struct krb4_handle *kh = h;
629 struct krb4_stream *ks;
633 ks = alloc(SIZEOF(*ks));
634 security_streaminit(&ks->secstr, &krb4_security_driver);
635 ks->socket = stream_server(&ks->port, STREAM_BUFSIZE, STREAM_BUFSIZE, 1);
636 if (ks->socket < 0) {
637 security_seterror(&kh->sech,
638 "can't create server stream: %s", strerror(errno));
643 ks->krb4_handle = kh;
649 * Accept an incoming connection on a stream_server socket
655 struct krb4_stream *ks = s;
656 struct krb4_handle *kh;
659 kh = ks->krb4_handle;
661 assert(ks->socket >= 0);
662 assert(ks->fd == -1);
664 ks->fd = stream_accept(ks->socket, 30, STREAM_BUFSIZE, STREAM_BUFSIZE);
666 security_stream_seterror(&ks->secstr,
667 "can't accept new stream connection: %s", strerror(errno));
674 * Return a connected stream.
681 struct krb4_handle *kh = h;
682 struct krb4_stream *ks;
686 ks = alloc(SIZEOF(*ks));
687 security_streaminit(&ks->secstr, &krb4_security_driver);
688 ks->fd = stream_client(kh->hostname, id, STREAM_BUFSIZE, STREAM_BUFSIZE,
691 security_seterror(&kh->sech,
692 "can't connect stream to %s port %d: %s", kh->hostname, id,
698 ks->socket = -1; /* we're a client */
699 ks->krb4_handle = kh;
705 * Close and unallocate resources for a stream.
711 struct krb4_stream *ks = s;
717 if (ks->socket != -1)
719 krb4_stream_read_cancel(ks);
724 * Authenticate a stream
726 * XXX this whole thing assumes the size of struct timeval is consistent,
727 * which is may not be! We need to extract the network byte order data
728 * into byte arrays and send those.
734 struct krb4_stream *ks = s;
735 struct krb4_handle *kh;
737 struct timeval local, enc;
744 kh = ks->krb4_handle;
746 /* make sure we're open */
749 /* make sure our timeval is what we're expecting, see above */
750 assert(SIZEOF(struct timeval) == 8);
753 * Get the current time, put it in network byte order, encrypt it
754 * and present it to the other side.
756 gettimeofday(&local, &tz);
757 enc.tv_sec = (long)htonl((uint32_t)local.tv_sec);
758 enc.tv_usec = (long)htonl((uint32_t)local.tv_usec);
759 encrypt_data(&enc, SIZEOF(enc), &kh->session_key);
760 if (net_write(fd, &enc, SIZEOF(enc)) < 0) {
761 security_stream_seterror(&ks->secstr,
762 "krb4 stream handshake write error: %s", strerror(errno));
767 * Read back the other side's presentation. Increment the seconds
768 * and useconds by one. Reencrypt, and present to the other side.
769 * Timeout in 10 seconds.
771 if (net_read(fd, &enc, SIZEOF(enc), 60) < 0) {
772 security_stream_seterror(&ks->secstr,
773 "krb4 stream handshake read error: %s", strerror(errno));
776 decrypt_data(&enc, SIZEOF(enc), &kh->session_key);
777 /* XXX do timestamp checking here */
778 enc.tv_sec = (long)htonl(ntohl((uint32_t)enc.tv_sec) + 1);
779 enc.tv_usec =(long)htonl(ntohl((uint32_t)enc.tv_usec) + 1);
780 encrypt_data(&enc, SIZEOF(enc), &kh->session_key);
782 if (net_write(fd, &enc, SIZEOF(enc)) < 0) {
783 security_stream_seterror(&ks->secstr,
784 "krb4 stream handshake write error: %s", strerror(errno));
789 * Read back the other side's processing of our data.
790 * If they incremented it properly, then succeed.
791 * Timeout in 10 seconds.
793 if (net_read(fd, &enc, SIZEOF(enc), 60) < 0) {
794 security_stream_seterror(&ks->secstr,
795 "krb4 stream handshake read error: %s", strerror(errno));
798 decrypt_data(&enc, SIZEOF(enc), &kh->session_key);
799 if ((ntohl((uint32_t)enc.tv_sec) == (uint32_t)(local.tv_sec + 1)) &&
800 (ntohl((uint32_t)enc.tv_usec) == (uint32_t)(local.tv_usec + 1)))
803 security_stream_seterror(&ks->secstr,
804 "krb4 handshake failed: sent %ld,%ld - recv %ld,%ld",
805 (long)(local.tv_sec + 1), (long)(local.tv_usec + 1),
806 (long)ntohl((uint32_t)enc.tv_sec),
807 (long)ntohl((uint32_t)enc.tv_usec));
812 * Returns the stream id for this stream. This is just the local
819 struct krb4_stream *ks = s;
827 * Write a chunk of data to a stream. Blocks until completion.
835 struct krb4_stream *ks = s;
836 struct krb4_handle *kh = ks->krb4_handle;
841 if (net_write(ks->fd, buf, size) < 0) {
842 security_stream_seterror(&ks->secstr,
843 "write error on stream %d: %s", ks->fd, strerror(errno));
850 * Submit a request to read some data. Calls back with the given
851 * function and arg when completed.
856 void (*fn)(void *, void *, ssize_t),
859 struct krb4_stream *ks = s;
864 * Only one read request can be active per stream.
866 if (ks->ev_read != NULL)
867 event_release(ks->ev_read);
869 ks->ev_read = event_register((event_id_t)ks->fd, EV_READFD,
870 stream_read_callback, ks);
876 * Write a chunk of data to a stream. Blocks until completion.
879 krb4_stream_read_sync(
883 struct krb4_stream *ks = s;
885 (void)buf; /* Quiet unused variable warning */
888 if (ks->ev_read != NULL)
889 event_release(ks->ev_read);
891 ks->ev_read = event_register((event_id_t)ks->fd, EV_READFD,
892 stream_read_sync_callback, ks);
893 event_wait(ks->ev_read);
894 return((ssize_t)ks->len);
898 * Callback for krb4_stream_read_sync
901 stream_read_sync_callback(
904 struct krb4_stream *ks = arg;
908 assert(ks->fd != -1);
911 * Remove the event first, and then call the callback.
912 * We remove it first because we don't want to get in their
913 * way if they reschedule it.
915 krb4_stream_read_cancel(ks);
916 n = read(ks->fd, ks->databuf, sizeof(ks->databuf));
918 security_stream_seterror(&ks->secstr,
924 * Cancel a previous stream read request. It's ok if we didn't have a read
928 krb4_stream_read_cancel(
931 struct krb4_stream *ks = s;
935 if (ks->ev_read != NULL) {
936 event_release(ks->ev_read);
942 * Callback for krb4_stream_read
945 stream_read_callback(
948 struct krb4_stream *ks = arg;
952 assert(ks->fd != -1);
955 * Remove the event first, and then call the callback.
956 * We remove it first because we don't want to get in their
957 * way if they reschedule it.
959 krb4_stream_read_cancel(ks);
960 n = read(ks->fd, ks->databuf, SIZEOF(ks->databuf));
962 security_stream_seterror(&ks->secstr,
964 (*ks->fn)(ks->arg, ks->databuf, n);
968 * The callback for recvpkt() for the event handler
969 * Determines if this packet is for this security handle,
970 * and does the real callback if so.
977 struct sockaddr_in6 peer;
980 struct krb4_handle *kh;
982 void (*fn)(void *, pkt_t *, security_status_t);
985 assert(cookie == NULL);
988 * Find the handle that this packet is associated with. We
989 * need to peek at the packet to see what is in it, but we
990 * want to save the actual reading for later.
993 if (dgram_recv(&netfd, 0, &peer) < 0)
995 if (str2pkthdr(netfd.cur, &pkt, handle, SIZEOF(handle), &sequence) < 0)
998 for (kh = handleq_first(); kh != NULL; kh = handleq_next(kh)) {
999 if (strcmp(kh->proto_handle, handle) == 0 &&
1000 cmp_sockaddr(&kh->peer, &peer, 0) == 0) {
1001 kh->sequence = sequence;
1004 * We need to cancel the recvpkt request before calling
1005 * the callback because the callback may reschedule us.
1009 krb4_recvpkt_cancel(kh);
1010 if (recv_security_ok(kh, &pkt) < 0)
1011 (*fn)(arg, NULL, S_ERROR);
1013 (*fn)(arg, &pkt, S_OK);
1018 * If we didn't find a handle, then check for a new incoming packet.
1019 * If no accept handler was setup, then just return.
1021 if (accept_fn == NULL)
1024 he = gethostbyaddr((void *)&peer.sin6_addr, SIZEOF(peer.sin6_addr), AF_INET6);
1027 kh = alloc(SIZEOF(*kh));
1028 security_handleinit(&kh->sech, &krb4_security_driver);
1029 inithandle(kh, he, (int)peer.sin6_port, handle);
1032 * Check the security of the packet. If it is bad, then pass NULL
1033 * to the accept function instead of a packet.
1035 if (recv_security_ok(kh, &pkt) < 0)
1036 (*accept_fn)(&kh->sech, NULL);
1038 (*accept_fn)(&kh->sech, &pkt);
1042 * This is called when a handle times out before receiving a packet.
1048 struct krb4_handle *kh = cookie;
1049 void (*fn)(void *, pkt_t *, security_status_t);
1054 assert(kh->ev_timeout != NULL);
1057 krb4_recvpkt_cancel(kh);
1058 (*fn)(arg, NULL, S_TIMEOUT);
1062 * Add a ticket to the message
1066 struct krb4_handle *kh,
1075 kh->cksum = (long)krb4_cksum(pkt->body);
1076 #if CLIENT_HOST_INSTANCE == HOSTNAME_INSTANCE
1078 * User requested that all instances be based on the target
1081 strncpy(inst, kh->inst, SIZEOF(inst) - 1);
1084 * User requested a fixed instance.
1086 strncpy(inst, CLIENT_HOST_INSTANCE, SIZEOF(inst) - 1);
1088 inst[SIZEOF(inst) - 1] = '\0';
1091 * Get a ticket with the user-defined service and instance,
1092 * and using the checksum of the body of the request packet.
1094 rc = krb_mk_req(&ticket, CLIENT_HOST_PRINCIPLE, inst, kh->realm,
1096 if (rc == NO_TKT_FIL) {
1097 /* It's been kdestroyed. Get a new one and try again */
1099 rc = krb_mk_req(&ticket, CLIENT_HOST_PRINCIPLE, inst, kh->realm,
1103 security_seterror(&kh->sech,
1104 "krb_mk_req failed: %s (%d)", error_message(rc), rc);
1108 * We now have the ticket. Put it into the packet, and send
1111 security = vstralloc("SECURITY TICKET ",
1112 bin2astr(ticket.dat, ticket.length), "\n", NULL);
1113 dgram_cat(msg, security);
1120 * Add the mutual authenticator. This is the checksum from
1125 struct krb4_handle *kh,
1128 union mutual mutual;
1132 assert(msg != NULL);
1133 assert(kh->cksum != 0);
1134 assert(kh->session_key[0] != '\0');
1136 memset(&mutual, 0, SIZEOF(mutual));
1137 mutual.cksum = (unsigned long)htonl((uint32_t)kh->cksum + 1);
1138 encrypt_data(&mutual, SIZEOF(mutual), &kh->session_key);
1140 security = vstralloc("SECURITY MUTUAL-AUTH ",
1141 bin2astr((unsigned char *)mutual.pad,
1142 (int)sizeof(mutual.pad)), "\n", NULL);
1143 dgram_cat(msg, security);
1148 * Check the security of a received packet. Returns negative on error
1149 * or security violation and otherwise returns 0 and fills in the
1154 struct krb4_handle *kh,
1157 char *tok, *security, *body;
1158 unsigned long cksum;
1161 assert(pkt != NULL);
1164 * Set this preemptively before we mangle the body.
1166 security_seterror(&kh->sech,
1167 "bad %s SECURITY line from %s: '%s'", pkt_type2str(pkt->type),
1168 kh->hostname, pkt->body);
1172 * The first part of the body should be the security info. Deal with it.
1173 * We only need to do this on a few packet types.
1175 * First, parse the SECURITY line in the packet, if it exists.
1176 * Increment the cur pointer past it to the data section after
1177 * parsing is finished.
1179 if (strncmp(pkt->body, "SECURITY", SIZEOF("SECURITY") - 1) == 0) {
1180 tok = strtok(pkt->body, " ");
1181 assert(strcmp(tok, "SECURITY") == 0);
1182 /* security info goes until the newline */
1183 security = strtok(NULL, "\n");
1184 body = strtok(NULL, "");
1186 * If the body is f-ked, then try to recover.
1189 if (security != NULL)
1190 body = security + strlen(security) + 2;
1200 * Get a checksum of the non-security parts of the body
1202 cksum = krb4_cksum(body);
1205 * Now deal with the data we did (or didn't) parse above
1207 switch (pkt->type) {
1210 * Request packets have a ticket after the security tag
1211 * Get the ticket, make sure the checksum agrees with the
1212 * checksum of the request we sent.
1214 * Must look like: SECURITY TICKET [ticket str]
1217 /* there must be some security info */
1218 if (security == NULL)
1221 /* second word must be TICKET */
1222 if ((tok = strtok(security, " ")) == NULL)
1224 if (strcmp(tok, "TICKET") != 0) {
1225 security_seterror(&kh->sech,
1226 "REQ SECURITY line parse error, expecting TICKET, got %s", tok);
1230 /* the third word is the encoded ticket */
1231 if ((tok = strtok(NULL, "")) == NULL)
1233 if (check_ticket(kh, pkt, tok, cksum) < 0)
1236 /* we're good to go */
1241 * Reply packets check the mutual authenticator for this host.
1243 * Must look like: SECURITY MUTUAL-AUTH [mutual auth str]
1245 if (security == NULL)
1247 if ((tok = strtok(security, " ")) == NULL)
1249 if (strcmp(tok, "MUTUAL-AUTH") != 0) {
1250 security_seterror(&kh->sech,
1251 "REP SECURITY line parse error, expecting MUTUAL-AUTH, got %s",
1255 if ((tok = strtok(NULL, "")) == NULL)
1257 if (check_mutual_auth(kh, tok) < 0)
1264 * These packets have no security. They should, but such
1265 * is life. We can't change it without breaking compatibility.
1267 * XXX Should we complain if argc > 0? (ie, some security info was
1275 * If there is security info at the front of the packet, we need
1276 * to shift the rest of the data up and nuke it.
1278 if (body != pkt->body)
1279 memmove(pkt->body, body, strlen(body) + 1);
1284 * Check the ticket in a REQ packet for authenticity
1288 struct krb4_handle *kh,
1290 const char * ticket_str,
1291 unsigned long cksum)
1301 assert(pkt != NULL);
1302 assert(ticket_str != NULL);
1304 ticket.length = (int)sizeof(ticket.dat);
1305 astr2bin((unsigned char *)ticket_str, ticket.dat, &ticket.length);
1306 assert(ticket.length > 0);
1308 /* get a copy of the instance into writable memory */
1309 #if CLIENT_HOST_INSTANCE == HOSTNAME_INSTANCE
1310 strncpy(inst, krb_get_phost(hostname), SIZEOF(inst) - 1);
1312 strncpy(inst, CLIENT_HOST_INSTANCE, SIZEOF(inst) - 1);
1314 inst[SIZEOF(inst) - 1] = '\0';
1316 /* get the checksum out of the ticket */
1317 rc = krb_rd_req(&ticket, CLIENT_HOST_PRINCIPLE, inst,
1318 kh->peer.sin6_addr.s_addr, &auth, CLIENT_HOST_KEY_FILE);
1320 security_seterror(&kh->sech,
1321 "krb_rd_req failed for %s: %s (%d)", kh->hostname,
1322 error_message(rc), rc);
1326 /* verify and save the checksum and session key */
1327 if (auth.checksum != cksum) {
1328 security_seterror(&kh->sech,
1329 "krb4 checksum mismatch for %s (remote=%lu, local=%lu)",
1330 kh->hostname, (long)auth.checksum, cksum);
1333 kh->cksum = (unsigned long)cksum;
1334 memcpy(kh->session_key, auth.session, SIZEOF(kh->session_key));
1337 * If FORCE_USERID is set, then we need to specifically
1338 * check the userid we're forcing ourself to. Otherwise,
1339 * just check the login we're currently setuid to.
1342 if ((pwd = getpwnam(CLIENT_LOGIN)) == NULL)
1343 error("error [getpwnam(%s) fails]", CLIENT_LOGIN);
1345 if ((pwd = getpwuid(getuid())) == NULL)
1346 error("error [getpwuid(%d) fails]", getuid());
1349 /* save the username in case it's overwritten */
1350 user = stralloc(pwd->pw_name);
1352 /* check the klogin file */
1353 if (kuserok(&auth, user)) {
1354 security_seterror(&kh->sech,
1355 "access as %s not allowed from %s.%s@%s", user, auth.pname,
1356 auth.pinst, auth.prealm);
1367 * Verify that the packet received is secure by verifying that it has
1368 * the same checksum as our request + 1.
1372 struct krb4_handle *kh,
1373 const char * mutual_auth_str)
1375 union mutual mutual;
1379 assert(mutual_auth_str != NULL);
1380 assert(kh->inst[0] != '\0');
1381 /* we had better have a checksum from a request we sent */
1382 assert(kh->cksum != 0);
1384 /* convert the encoded string into binary data */
1385 len = (int)sizeof(mutual);
1386 astr2bin((unsigned char *)mutual_auth_str, (unsigned char *)&mutual, &len);
1388 /* unencrypt the string using the key in the ticket file */
1389 host2key(kh->hostname, kh->inst, &kh->session_key);
1390 decrypt_data(&mutual, (size_t)len, &kh->session_key);
1391 mutual.cksum = (unsigned long)ntohl((uint32_t)mutual.cksum);
1393 /* the data must be the same as our request cksum + 1 */
1394 if (mutual.cksum != (kh->cksum + 1)) {
1395 security_seterror(&kh->sech,
1396 "krb4 checksum mismatch from %s (remote=%lu, local=%lu)",
1397 kh->hostname, mutual.cksum, kh->cksum + 1);
1405 * Convert a pkt_t into a header string for our packet
1409 const struct krb4_handle * kh,
1412 static char retbuf[256];
1415 assert(pkt != NULL);
1417 snprintf(retbuf, SIZEOF(retbuf), "Amanda %d.%d %s HANDLE %s SEQ %d\n",
1418 VERSION_MAJOR, VERSION_MINOR, pkt_type2str(pkt->type),
1419 kh->proto_handle, kh->sequence);
1421 /* check for truncation. If only we had asprintf()... */
1422 assert(retbuf[strlen(retbuf) - 1] == '\n');
1428 * Parses out the header line in 'str' into the pkt and handle
1429 * Returns negative on parse error.
1433 const char *origstr,
1442 assert(origstr != NULL);
1443 assert(pkt != NULL);
1445 str = stralloc(origstr);
1447 /* "Amanda %d.%d <ACK,NAK,...> HANDLE %s SEQ %d\n" */
1449 /* Read in "Amanda" */
1450 if ((tok = strtok(str, " ")) == NULL || strcmp(tok, "Amanda") != 0)
1453 /* nothing is done with the major/minor numbers currently */
1454 if ((tok = strtok(NULL, " ")) == NULL || strchr(tok, '.') == NULL)
1457 /* Read in the packet type */
1458 if ((tok = strtok(NULL, " ")) == NULL)
1460 pkt_init(pkt, pkt_str2type(tok), "");
1461 if (pkt->type == (pktype_t)-1)
1464 /* Read in "HANDLE" */
1465 if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "HANDLE") != 0)
1468 /* parse the handle */
1469 if ((tok = strtok(NULL, " ")) == NULL)
1471 strncpy(handle, tok, handlesize - 1);
1472 handle[handlesize - 1] = '\0';
1475 if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "SEQ") != 0)
1478 /* parse the sequence number */
1479 if ((tok = strtok(NULL, "\n")) == NULL)
1481 *sequence = atoi(tok);
1483 /* Save the body, if any */
1484 if ((tok = strtok(NULL, "")) != NULL)
1491 #if 0 /* XXX we have no way of passing this back up */
1492 security_seterror(&kh->sech,
1493 "parse error in packet header : '%s'", origstr);
1501 const char *hostname,
1508 strncpy(realm, krb_realmofhost((char *)hostname), SIZEOF(realm) - 1);
1509 realm[SIZEOF(realm) - 1] = '\0';
1510 #if CLIENT_HOST_INSTANCE != HOSTNAME_INSTANCE
1511 inst = CLIENT_HOST_INSTANCE
1513 krb_get_cred(CLIENT_HOST_PRINCIPLE, (char *)inst, realm, &cred);
1514 memcpy(key, cred.session, SIZEOF(des_cblock));
1519 * Convert a chunk of data into a string.
1523 const unsigned char *buf,
1526 static const char tohex[] = "0123456789ABCDEF";
1527 static char *str = NULL;
1529 const unsigned char *p;
1534 * calculate output string len
1535 * We quote everything, so each input byte == 3 output chars, plus
1536 * two more for quotes
1538 slen = ((size_t)len * 3) + 2;
1540 /* allocate string and fill it in */
1543 str = alloc(slen + 1);
1547 for (i = 0; i < len; i++) {
1549 *q++ = tohex[(*p >> 4) & 0xF];
1550 *q++ = tohex[*p & 0xF];
1556 /* make sure we didn't overrun our allocated buffer */
1557 assert((size_t)(q - str) == slen);
1563 * Convert an encoded string into a block of data bytes
1567 const unsigned char *astr,
1568 unsigned char * buf,
1571 const unsigned char *p;
1573 #define fromhex(h) (isdigit((int)h) ? (h) - '0' : (h) - 'A' + 10)
1576 * Skip leading quote, if any
1582 * Run through the string. Anything starting with a $ is a three
1583 * char representation of this byte. Everything else is literal.
1585 for (p = astr, q = buf; *p != '"' && *p != '\0'; ) {
1589 *q++ = (fromhex(p[1]) << 4) + fromhex(p[2]);
1592 if ((int)(q - buf) >= *lenp)
1598 static unsigned long
1604 memset(seed, 0, SIZEOF(seed));
1606 * The first arg is an unsigned char * in some krb4 implementations,
1607 * and in others, it's a des_cblock *. Just make it void here
1608 * to shut them all up.
1610 return (quad_cksum((void *)str, NULL, (long)strlen(str), 1, &seed));
1621 * Copy the first part of the hostname up to the first '.' into
1622 * the buffer provided. Leave room for a NULL.
1624 while (size > 1 && *hname != '\0' && *hname != '.') {
1625 *inst++ = isupper((int)*hname) ? tolower((int)*hname) : *hname;
1638 des_key_schedule sched;
1641 * First arg is des_cblock * in some places, and just des_cblock in
1642 * others. Since des_cblock is a char array, they are equivalent.
1643 * Just cast it to void * to keep both compilers quiet. typedefing
1644 * arrays should be outlawed.
1646 des_key_sched((void *)key, sched);
1647 des_pcbc_encrypt(data, data, (long)length, sched, key, DES_ENCRYPT);
1657 des_key_schedule sched;
1659 des_key_sched((void *)key, sched);
1660 des_pcbc_encrypt(data, data, (long)length, sched, key, DES_DECRYPT);
1664 * like write(), but always writes out the entire buffer.
1672 const char *buf = vbuf; /* so we can do ptr arith */
1676 n = write(fd, buf, size);
1686 * Like read(), but waits until the entire buffer has been filled.
1695 char *buf = vbuf; /* ptr arith */
1698 SELECT_ARG_TYPE readfds;
1703 FD_SET(fd, &readfds);
1704 tv.tv_sec = timeout;
1706 switch (select(fd + 1, &readfds, NULL, NULL, &tv)) {
1713 assert(FD_ISSET(fd, &readfds));
1719 n = read(fd, buf, size);
1722 /* we only tolerate so many eof's */
1723 if (n == 0 && ++neof > 1024) {
1734 /* -------------------------- */
1735 /* debug routines */
1740 const unsigned char * buf,
1745 dbprintf(("%s:", str));
1746 for(i=0;i<len;i++) {
1747 if(i%25 == 0) dbprintf(("\n"));
1748 dbprintf((" %02X", buf[i]));
1758 dbprintf(("%s: length %d chk %lX\n", str, tktp->length, tktp->mbz));
1759 print_hex("ticket data", tktp->dat, tktp->length);
1767 printf("\nAuth Data:\n");
1768 printf(" Principal \"%s\" Instance \"%s\" Realm \"%s\"\n",
1769 authp->pname, authp->pinst, authp->prealm);
1770 printf(" cksum %d life %d keylen %ld\n", authp->checksum,
1771 authp->life, SIZEOF(authp->session));
1772 print_hex("session key", authp->session, SIZEOF(authp->session));
1780 printf("\nCredentials:\n");
1781 printf(" service \"%s\" instance \"%s\" realm \"%s\" life %d kvno %d\n",
1782 credp->service, credp->instance, credp->realm, credp->lifetime,
1784 print_hex("session key", credp->session, SIZEOF(credp->session));
1785 print_hex("ticket", credp->ticket_st.dat, credp->ticket_st.length);
1792 void krb4_security_dummy(void);
1795 krb4_security_dummy(void)
1799 #endif /* KRB4_SECURITY */