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.9.2.1 2006/04/11 11:11:16 martinea Exp $
30 * krb4-security.c - helper functions for kerberos v4 security.
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 */
63 int krb_set_lifetime P((int));
64 int kuserok P((AUTH_DAT *, char *));
67 * This is the private handle data
70 security_handle_t sech; /* MUST be first */
71 struct sockaddr_in peer; /* host on other side */
72 char hostname[MAX_HOSTNAME_LENGTH+1]; /* human form of above */
73 char proto_handle[32]; /* protocol handle for this req */
74 int sequence; /* last sequence number we received */
75 char inst[INST_SZ]; /* krb4 instance form of above */
76 char realm[REALM_SZ]; /* krb4 realm of this host */
77 unsigned long cksum; /* cksum of the req packet we sent */
78 des_cblock session_key; /* session key */
81 * The rest is used for the async recvpkt/recvpkt_cancel
84 void (*fn) P((void *, pkt_t *, security_status_t));
85 /* func to call when packet recvd */
86 void *arg; /* argument to pass function */
87 event_handle_t *ev_timeout; /* timeout handle for recv */
88 TAILQ_ENTRY(krb4_handle) tq; /* queue handle */
92 * This is the internal security_stream data for krb4.
95 security_stream_t secstr; /* MUST be first */
96 struct krb4_handle *krb4_handle; /* pointer into above */
97 int fd; /* io file descriptor */
98 int port; /* local port this is bound to */
99 int socket; /* fd for server-side accepts */
100 event_handle_t *ev_read; /* read event handle */
101 char databuf[MAX_TAPE_BLOCK_BYTES]; /* read buffer */
102 void (*fn) P((void *, void *, int)); /* read event fn */
103 void *arg; /* arg for previous */
107 * This is the tcp stream buffer size
109 #define STREAM_BUFSIZE (MAX_TAPE_BLOCK_BYTES * 2)
112 * Interface functions
114 static void krb4_connect P((const char *,
115 char *(*)(char *, void *),
116 void (*)(void *, security_handle_t *, security_status_t), void *));
117 static void krb4_accept P((int, int, void (*)(security_handle_t *, pkt_t *)));
118 static void krb4_close P((void *));
119 static int krb4_sendpkt P((void *, pkt_t *));
120 static void krb4_recvpkt P((void *,
121 void (*)(void *, pkt_t *, security_status_t), void *, int));
122 static void krb4_recvpkt_cancel P((void *));
124 static void *krb4_stream_server P((void *));
125 static int krb4_stream_accept P((void *));
126 static void *krb4_stream_client P((void *, int));
127 static void krb4_stream_close P((void *));
128 static int krb4_stream_auth P((void *));
129 static int krb4_stream_id P((void *));
130 static int krb4_stream_write P((void *, const void *, size_t));
131 static void krb4_stream_read P((void *, void (*)(void *, void *, int),
133 static void krb4_stream_read_cancel P((void *));
137 * This is our interface to the outside world.
139 const security_driver_t krb4_security_driver = {
155 krb4_stream_read_cancel,
159 * Cache the local hostname
161 static char hostname[MAX_HOSTNAME_LENGTH+1];
164 * This is the dgram_t that we use to send and recv protocol packets
165 * over the net. There is only one per process, so it lives globally
168 static dgram_t netfd;
171 * This is a queue of outstanding async requests
174 TAILQ_HEAD(, krb4_handle) tailq;
177 TAILQ_HEAD_INITIALIZER(handleq.tailq), 0
181 * Macros to add or remove krb4_handles from the above queue
183 #define handleq_add(kh) do { \
184 assert(handleq.qlength == 0 ? TAILQ_FIRST(&handleq.tailq) == NULL : 1); \
185 TAILQ_INSERT_TAIL(&handleq.tailq, kh, tq); \
189 #define handleq_remove(kh) do { \
190 assert(handleq.qlength > 0); \
191 assert(TAILQ_FIRST(&handleq.tailq) != NULL); \
192 TAILQ_REMOVE(&handleq.tailq, kh, tq); \
194 assert((handleq.qlength == 0) ^ (TAILQ_FIRST(&handleq.tailq) != NULL)); \
197 #define handleq_first() TAILQ_FIRST(&handleq.tailq)
198 #define handleq_next(kh) TAILQ_NEXT(kh, tq)
202 * This is the event manager's handle for our netfd
204 static event_handle_t *ev_netfd;
207 * This is a function that should be called if a new security_handle_t is
208 * created. If NULL, no new handles are created.
209 * It is passed the new handle and the received pkt
211 static void (*accept_fn) P((security_handle_t *, pkt_t *));
215 * This is a structure used in encoding the cksum in a mutual-auth
216 * transaction. The checksum is placed in here first before encryption
217 * because encryption requires at least 8 bytes of data, and an unsigned
218 * long on most machines (32 bit ones) is 4 bytes.
228 static unsigned long krb4_cksum P((const char *));
229 static void krb4_getinst P((const char *, char *, size_t));
230 static void host2key P((const char *, const char *, des_cblock *));
231 static void init P((void));
232 static void inithandle P((struct krb4_handle *, struct hostent *, int,
234 static void get_tgt P((void));
235 static void killtickets P((void));
236 static void recvpkt_callback P((void *));
237 static void recvpkt_timeout P((void *));
238 static int recv_security_ok P((struct krb4_handle *, pkt_t *));
239 static void stream_read_callback P((void *));
240 static int net_write P((int, const void *, size_t));
241 static int net_read P((int, void *, size_t, int));
243 static int add_ticket P((struct krb4_handle *, const pkt_t *, dgram_t *));
244 static void add_mutual_auth P((struct krb4_handle *, dgram_t *));
245 static int check_ticket P((struct krb4_handle *, const pkt_t *,
246 const char *, unsigned long));
247 static int check_mutual_auth P((struct krb4_handle *, const char *));
249 static const char *pkthdr2str P((const struct krb4_handle *, const pkt_t *));
250 static int str2pkthdr P((const char *, pkt_t *, char *, size_t, int *));
252 static const char *bin2astr P((const unsigned char *, int));
253 static void astr2bin P((const char *, unsigned char *, int *));
255 static void encrypt_data P((void *, int, des_cblock *));
256 static void decrypt_data P((void *, int, des_cblock *));
258 #define HOSTNAME_INSTANCE inst
260 static char *ticketfilename = NULL;
265 if (ticketfilename != NULL)
266 unlink(ticketfilename);
267 amfree(ticketfilename);
271 * Setup some things about krb4. This should only be called once.
278 static int beenhere = 0;
284 gethostname(hostname, sizeof(hostname) - 1);
285 hostname[sizeof(hostname) - 1] = '\0';
287 if (atexit(killtickets) < 0)
288 error("could not setup krb4 exit handler: %s", strerror(errno));
291 * [XXX] It could be argued that if KRBTKFILE is set outside of amanda,
292 * that it's value should be used instead of us setting one up.
293 * This file also needs to be removed so that no extra tickets are
296 snprintf(tktfile, sizeof(tktfile), "/tmp/tkt%ld-%ld.amanda",
297 (long)getuid(), (long)getpid());
298 ticketfilename = stralloc(tktfile);
299 unlink(ticketfilename);
300 krb_set_tkt_string(ticketfilename);
301 #if defined(HAVE_PUTENV)
303 char *tkt_env = stralloc2("KRBTKFILE=", ticketfilename);
307 setenv("KRBTKFILE", ticketfile, 1);
311 dgram_bind(&netfd, &port);
315 * Get a ticket granting ticket and stuff it in the cache
320 char realm[REALM_SZ];
323 strncpy(realm, krb_realmofhost(hostname), sizeof(realm) - 1);
324 realm[sizeof(realm) - 1] = '\0';
326 rc = krb_get_svc_in_tkt(SERVER_HOST_PRINCIPLE, SERVER_HOST_INSTANCE,
327 realm, "krbtgt", realm, TICKET_LIFETIME, SERVER_HOST_KEY_FILE);
330 error("could not get krbtgt for %s.%s@%s from %s: %s",
331 SERVER_HOST_PRINCIPLE, SERVER_HOST_INSTANCE, realm,
332 SERVER_HOST_KEY_FILE, krb_err_txt[rc]);
335 krb_set_lifetime(TICKET_LIFETIME);
340 * krb4 version of a security handle allocator. Logically sets
341 * up a network "connection".
344 krb4_connect(hostname, conf_fn, fn, arg)
345 const char *hostname;
346 char *(*conf_fn) P((char *, void *));
347 void (*fn) P((void *, security_handle_t *, security_status_t));
350 struct krb4_handle *kh;
356 assert(hostname != NULL);
359 * Make sure we're initted
363 kh = alloc(sizeof(*kh));
364 security_handleinit(&kh->sech, &krb4_security_driver);
366 if ((he = gethostbyname(hostname)) == NULL) {
367 security_seterror(&kh->sech,
368 "%s: could not resolve hostname", hostname);
369 (*fn)(arg, &kh->sech, S_ERROR);
372 if ((se = getservbyname(KAMANDA_SERVICE_NAME, "udp")) == NULL)
373 port = htons(KAMANDA_SERVICE_DEFAULT);
376 snprintf(handle, sizeof(handle), "%ld", (long)time(NULL));
377 inithandle(kh, he, port, handle);
378 (*fn)(arg, &kh->sech, S_OK);
382 * Setup to handle new incoming connections
385 krb4_accept(in, out, fn)
387 void (*fn) P((security_handle_t *, pkt_t *));
391 * Make sure we're initted
396 * We assume that in and out both point to the same socket
398 dgram_socket(&netfd, in);
401 * Assign the function and return. When they call recvpkt later,
402 * the recvpkt callback will call this function when it discovers
403 * new incoming connections
407 if (ev_netfd == NULL)
408 ev_netfd = event_register(netfd.socket, EV_READFD,
409 recvpkt_callback, NULL);
413 * Given a hostname and a port, setup a krb4_handle
416 inithandle(kh, he, port, handle)
417 struct krb4_handle *kh;
424 * Get the instance and realm for this host
425 * (krb_realmofhost always returns something)
427 krb4_getinst(he->h_name, kh->inst, sizeof(kh->inst));
428 strncpy(kh->realm, krb_realmofhost(he->h_name), sizeof(kh->realm) - 1);
429 kh->realm[sizeof(kh->realm) - 1] = '\0';
432 * Save a copy of the hostname
434 strncpy(kh->hostname, he->h_name, sizeof(kh->hostname) - 1);
435 kh->hostname[sizeof(kh->hostname) - 1] = '\0';
438 * We have no checksum or session key at this point
441 memset(kh->session_key, 0, sizeof(kh->session_key));
444 * Setup our peer info. We don't do anything with the sequence yet,
445 * so just leave it at 0.
447 kh->peer.sin_family = AF_INET;
448 kh->peer.sin_port = port;
449 kh->peer.sin_addr = *(struct in_addr *)he->h_addr;
450 strncpy(kh->proto_handle, handle, sizeof(kh->proto_handle) - 1);
451 kh->proto_handle[sizeof(kh->proto_handle) - 1] = '\0';
455 kh->ev_timeout = NULL;
459 * frees a handle allocated by the above
466 krb4_recvpkt_cancel(inst);
471 * Transmit a packet. Add security information first.
474 krb4_sendpkt(cookie, pkt)
478 struct krb4_handle *kh = cookie;
484 * Initialize this datagram
489 * Add the header to the packet
491 dgram_cat(&netfd, pkthdr2str(kh, pkt));
494 * Add the security info. This depends on which kind of packet we're
500 * Requests get sent with a ticket embedded in the header. The
501 * checksum is generated from the contents of the body.
503 if (add_ticket(kh, pkt, &netfd) < 0)
508 * Replies get sent with a mutual authenticator added. The
509 * mutual authenticator is the encrypted checksum from the
512 add_mutual_auth(kh, &netfd);
518 * The other types have no security stuff added for krb4.
525 * Add the body, and send it
527 dgram_cat(&netfd, pkt->body);
528 if (dgram_send_addr(kh->peer, &netfd) != 0) {
529 security_seterror(&kh->sech,
530 "send %s to %s failed: %s", pkt_type2str(pkt->type),
531 kh->hostname, strerror(errno));
538 * Set up to receive a packet asyncronously, and call back when
542 krb4_recvpkt(cookie, fn, arg, timeout)
544 void (*fn) P((void *, pkt_t *, security_status_t));
547 struct krb4_handle *kh = cookie;
549 assert(netfd.socket >= 0);
553 * We register one event handler for our network fd which takes
554 * care of all of our async requests. When all async requests
555 * have either been satisfied or cancelled, we unregister our
556 * network event handler.
558 if (ev_netfd == NULL) {
559 assert(handleq.qlength == 0);
560 ev_netfd = event_register(netfd.socket, EV_READFD,
561 recvpkt_callback, NULL);
565 * Multiple recvpkt calls override previous ones
566 * If kh->fn is NULL then it is not in the queue.
570 if (kh->ev_timeout != NULL)
571 event_release(kh->ev_timeout);
573 kh->ev_timeout = NULL;
575 kh->ev_timeout = event_register(timeout, EV_TIME, recvpkt_timeout, kh);
581 * Remove a async receive request from the queue
582 * If it is the last one to be removed, then remove the event handler
583 * for our network fd.
586 krb4_recvpkt_cancel(cookie)
589 struct krb4_handle *kh = cookie;
593 if (kh->fn != NULL) {
598 if (kh->ev_timeout != NULL)
599 event_release(kh->ev_timeout);
600 kh->ev_timeout = NULL;
602 if (handleq.qlength == 0 && accept_fn == NULL &&
604 event_release(ev_netfd);
610 * Create the server end of a stream. For krb4, this means setup a tcp
611 * socket for receiving a connection.
614 krb4_stream_server(h)
617 struct krb4_handle *kh = h;
618 struct krb4_stream *ks;
622 ks = alloc(sizeof(*ks));
623 security_streaminit(&ks->secstr, &krb4_security_driver);
624 ks->socket = stream_server(&ks->port, STREAM_BUFSIZE, STREAM_BUFSIZE);
625 if (ks->socket < 0) {
626 security_seterror(&kh->sech,
627 "can't create server stream: %s", strerror(errno));
632 ks->krb4_handle = kh;
638 * Accept an incoming connection on a stream_server socket
641 krb4_stream_accept(s)
644 struct krb4_stream *ks = s;
645 struct krb4_handle *kh;
648 kh = ks->krb4_handle;
650 assert(ks->socket >= 0);
651 assert(ks->fd == -1);
653 ks->fd = stream_accept(ks->socket, 30, -1, -1);
655 security_stream_seterror(&ks->secstr,
656 "can't accept new stream connection: %s", strerror(errno));
663 * Return a connected stream.
666 krb4_stream_client(h, id)
670 struct krb4_handle *kh = h;
671 struct krb4_stream *ks;
676 security_seterror(&kh->sech,
677 "%d: invalid security stream id", id);
681 ks = alloc(sizeof(*ks));
682 security_streaminit(&ks->secstr, &krb4_security_driver);
683 ks->fd = stream_client(kh->hostname, id, STREAM_BUFSIZE, STREAM_BUFSIZE,
686 security_seterror(&kh->sech,
687 "can't connect stream to %s port %d: %s", kh->hostname, id,
693 ks->socket = -1; /* we're a client */
694 ks->krb4_handle = kh;
700 * Close and unallocate resources for a stream.
706 struct krb4_stream *ks = s;
712 if (ks->socket != -1)
714 krb4_stream_read_cancel(ks);
719 * Authenticate a stream
721 * XXX this whole thing assumes the size of struct timeval is consistent,
722 * which is may not be! We need to extract the network byte order data
723 * into byte arrays and send those.
729 struct krb4_stream *ks = s;
730 struct krb4_handle *kh;
732 struct timeval local, enc;
739 kh = ks->krb4_handle;
741 /* make sure we're open */
744 /* make sure our timeval is what we're expecting, see above */
745 assert(sizeof(struct timeval) == 8);
748 * Get the current time, put it in network byte order, encrypt it
749 * and present it to the other side.
751 gettimeofday(&local, &tz);
752 enc.tv_sec = htonl(local.tv_sec);
753 enc.tv_usec = htonl(local.tv_usec);
754 encrypt_data(&enc, sizeof(enc), &kh->session_key);
755 if (net_write(fd, &enc, sizeof(enc)) < 0) {
756 security_stream_seterror(&ks->secstr,
757 "krb4 stream handshake write error: %s", strerror(errno));
762 * Read back the other side's presentation. Increment the seconds
763 * and useconds by one. Reencrypt, and present to the other side.
764 * Timeout in 10 seconds.
766 if (net_read(fd, &enc, sizeof(enc), 60) < 0) {
767 security_stream_seterror(&ks->secstr,
768 "krb4 stream handshake read error: %s", strerror(errno));
771 decrypt_data(&enc, sizeof(enc), &kh->session_key);
772 /* XXX do timestamp checking here */
773 enc.tv_sec = htonl(ntohl(enc.tv_sec) + 1);
774 enc.tv_usec = htonl(ntohl(enc.tv_usec) + 1);
775 encrypt_data(&enc, sizeof(enc), &kh->session_key);
777 if (net_write(fd, &enc, sizeof(enc)) < 0) {
778 security_stream_seterror(&ks->secstr,
779 "krb4 stream handshake write error: %s", strerror(errno));
784 * Read back the other side's processing of our data.
785 * If they incremented it properly, then succeed.
786 * Timeout in 10 seconds.
788 if (net_read(fd, &enc, sizeof(enc), 60) < 0) {
789 security_stream_seterror(&ks->secstr,
790 "krb4 stream handshake read error: %s", strerror(errno));
793 decrypt_data(&enc, sizeof(enc), &kh->session_key);
794 if (ntohl(enc.tv_sec) == local.tv_sec + 1 &&
795 ntohl(enc.tv_usec) == local.tv_usec + 1)
798 security_stream_seterror(&ks->secstr,
799 "krb4 handshake failed: sent %ld,%ld - recv %ld,%ld",
800 (long)(local.tv_sec + 1), (long)(local.tv_usec + 1),
801 (long)ntohl(enc.tv_sec), (long)ntohl(enc.tv_usec));
806 * Returns the stream id for this stream. This is just the local
813 struct krb4_stream *ks = s;
821 * Write a chunk of data to a stream. Blocks until completion.
824 krb4_stream_write(s, buf, size)
829 struct krb4_stream *ks = s;
830 struct krb4_handle *kh = ks->krb4_handle;
835 if (net_write(ks->fd, buf, size) < 0) {
836 security_stream_seterror(&ks->secstr,
837 "write error on stream %d: %s", ks->fd, strerror(errno));
844 * Submit a request to read some data. Calls back with the given
845 * function and arg when completed.
848 krb4_stream_read(s, fn, arg)
850 void (*fn) P((void *, void *, int));
852 struct krb4_stream *ks = s;
857 * Only one read request can be active per stream.
859 if (ks->ev_read != NULL)
860 event_release(ks->ev_read);
862 ks->ev_read = event_register(ks->fd, EV_READFD, stream_read_callback, ks);
868 * Cancel a previous stream read request. It's ok if we didn't have a read
872 krb4_stream_read_cancel(s)
875 struct krb4_stream *ks = s;
879 if (ks->ev_read != NULL) {
880 event_release(ks->ev_read);
886 * Callback for krb4_stream_read
889 stream_read_callback(arg)
892 struct krb4_stream *ks = arg;
896 assert(ks->fd != -1);
899 * Remove the event first, and then call the callback.
900 * We remove it first because we don't want to get in their
901 * way if they reschedule it.
903 krb4_stream_read_cancel(ks);
904 n = read(ks->fd, ks->databuf, sizeof(ks->databuf));
906 security_stream_seterror(&ks->secstr,
908 (*ks->fn)(ks->arg, ks->databuf, n);
912 * The callback for recvpkt() for the event handler
913 * Determines if this packet is for this security handle,
914 * and does the real callback if so.
917 recvpkt_callback(cookie)
921 struct sockaddr_in peer;
924 struct krb4_handle *kh;
926 void (*fn) P((void *, pkt_t *, security_status_t));
929 assert(cookie == NULL);
932 * Find the handle that this packet is associated with. We
933 * need to peek at the packet to see what is in it, but we
934 * want to save the actual reading for later.
937 if (dgram_recv(&netfd, 0, &peer) < 0)
939 if (str2pkthdr(netfd.cur, &pkt, handle, sizeof(handle), &sequence) < 0)
942 for (kh = handleq_first(); kh != NULL; kh = handleq_next(kh)) {
943 if (strcmp(kh->proto_handle, handle) == 0 &&
944 memcmp(&kh->peer.sin_addr, &peer.sin_addr,
945 sizeof(peer.sin_addr)) == 0 &&
946 kh->peer.sin_port == peer.sin_port) {
947 kh->sequence = sequence;
950 * We need to cancel the recvpkt request before calling
951 * the callback because the callback may reschedule us.
955 krb4_recvpkt_cancel(kh);
956 if (recv_security_ok(kh, &pkt) < 0)
957 (*fn)(arg, NULL, S_ERROR);
959 (*fn)(arg, &pkt, S_OK);
964 * If we didn't find a handle, then check for a new incoming packet.
965 * If no accept handler was setup, then just return.
967 if (accept_fn == NULL)
970 he = gethostbyaddr((void *)&peer.sin_addr, sizeof(peer.sin_addr), AF_INET);
973 kh = alloc(sizeof(*kh));
974 security_handleinit(&kh->sech, &krb4_security_driver);
975 inithandle(kh, he, peer.sin_port, handle);
978 * Check the security of the packet. If it is bad, then pass NULL
979 * to the accept function instead of a packet.
981 if (recv_security_ok(kh, &pkt) < 0)
982 (*accept_fn)(&kh->sech, NULL);
984 (*accept_fn)(&kh->sech, &pkt);
988 * This is called when a handle times out before receiving a packet.
991 recvpkt_timeout(cookie)
994 struct krb4_handle *kh = cookie;
995 void (*fn) P((void *, pkt_t *, security_status_t));
1000 assert(kh->ev_timeout != NULL);
1003 krb4_recvpkt_cancel(kh);
1004 (*fn)(arg, NULL, S_TIMEOUT);
1008 * Add a ticket to the message
1011 add_ticket(kh, pkt, msg)
1012 struct krb4_handle *kh;
1021 kh->cksum = krb4_cksum(pkt->body);
1022 #if CLIENT_HOST_INSTANCE == HOSTNAME_INSTANCE
1024 * User requested that all instances be based on the target
1027 strncpy(inst, kh->inst, sizeof(inst) - 1);
1030 * User requested a fixed instance.
1032 strncpy(inst, CLIENT_HOST_INSTANCE, sizeof(inst) - 1);
1034 inst[sizeof(inst) - 1] = '\0';
1037 * Get a ticket with the user-defined service and instance,
1038 * and using the checksum of the body of the request packet.
1040 rc = krb_mk_req(&ticket, CLIENT_HOST_PRINCIPLE, inst, kh->realm,
1042 if (rc == NO_TKT_FIL) {
1043 /* It's been kdestroyed. Get a new one and try again */
1045 rc = krb_mk_req(&ticket, CLIENT_HOST_PRINCIPLE, inst, kh->realm,
1049 security_seterror(&kh->sech,
1050 "krb_mk_req failed: %s (%d)", error_message(rc), rc);
1054 * We now have the ticket. Put it into the packet, and send
1057 security = vstralloc("SECURITY TICKET ",
1058 bin2astr(ticket.dat, ticket.length), "\n", NULL);
1059 dgram_cat(msg, security);
1066 * Add the mutual authenticator. This is the checksum from
1070 add_mutual_auth(kh, msg)
1071 struct krb4_handle *kh;
1074 union mutual mutual;
1078 assert(msg != NULL);
1079 assert(kh->cksum != 0);
1080 assert(kh->session_key[0] != '\0');
1082 memset(&mutual, 0, sizeof(mutual));
1083 mutual.cksum = htonl(kh->cksum + 1);
1084 encrypt_data(&mutual, sizeof(mutual), &kh->session_key);
1086 security = vstralloc("SECURITY MUTUAL-AUTH ",
1087 bin2astr(mutual.pad, sizeof(mutual.pad)), "\n", NULL);
1088 dgram_cat(msg, security);
1093 * Check the security of a received packet. Returns negative on error
1094 * or security violation and otherwise returns 0 and fills in the
1098 recv_security_ok(kh, pkt)
1099 struct krb4_handle *kh;
1102 char *tok, *security, *body;
1103 unsigned long cksum;
1106 assert(pkt != NULL);
1109 * Set this preemptively before we mangle the body.
1111 security_seterror(&kh->sech,
1112 "bad %s SECURITY line from %s: '%s'", pkt_type2str(pkt->type),
1113 kh->hostname, pkt->body);
1117 * The first part of the body should be the security info. Deal with it.
1118 * We only need to do this on a few packet types.
1120 * First, parse the SECURITY line in the packet, if it exists.
1121 * Increment the cur pointer past it to the data section after
1122 * parsing is finished.
1124 if (strncmp(pkt->body, "SECURITY", sizeof("SECURITY") - 1) == 0) {
1125 tok = strtok(pkt->body, " ");
1126 assert(strcmp(tok, "SECURITY") == 0);
1127 /* security info goes until the newline */
1128 security = strtok(NULL, "\n");
1129 body = strtok(NULL, "");
1131 * If the body is f-ked, then try to recover.
1134 if (security != NULL)
1135 body = security + strlen(security) + 2;
1145 * Get a checksum of the non-security parts of the body
1147 cksum = krb4_cksum(body);
1150 * Now deal with the data we did (or didn't) parse above
1152 switch (pkt->type) {
1155 * Request packets have a ticket after the security tag
1156 * Get the ticket, make sure the checksum agrees with the
1157 * checksum of the request we sent.
1159 * Must look like: SECURITY TICKET [ticket str]
1162 /* there must be some security info */
1163 if (security == NULL)
1166 /* second word must be TICKET */
1167 if ((tok = strtok(security, " ")) == NULL)
1169 if (strcmp(tok, "TICKET") != 0) {
1170 security_seterror(&kh->sech,
1171 "REQ SECURITY line parse error, expecting TICKET, got %s", tok);
1175 /* the third word is the encoded ticket */
1176 if ((tok = strtok(NULL, "")) == NULL)
1178 if (check_ticket(kh, pkt, tok, cksum) < 0)
1181 /* we're good to go */
1186 * Reply packets check the mutual authenticator for this host.
1188 * Must look like: SECURITY MUTUAL-AUTH [mutual auth str]
1190 if (security == NULL)
1192 if ((tok = strtok(security, " ")) == NULL)
1194 if (strcmp(tok, "MUTUAL-AUTH") != 0) {
1195 security_seterror(&kh->sech,
1196 "REP SECURITY line parse error, expecting MUTUAL-AUTH, got %s",
1200 if ((tok = strtok(NULL, "")) == NULL)
1202 if (check_mutual_auth(kh, tok) < 0)
1209 * These packets have no security. They should, but such
1210 * is life. We can't change it without breaking compatibility.
1212 * XXX Should we complain if argc > 0? (ie, some security info was
1220 * If there is security info at the front of the packet, we need
1221 * to shift the rest of the data up and nuke it.
1223 if (body != pkt->body)
1224 memmove(pkt->body, body, strlen(body) + 1);
1229 * Check the ticket in a REQ packet for authenticity
1232 check_ticket(kh, pkt, ticket_str, cksum)
1233 struct krb4_handle *kh;
1235 const char *ticket_str;
1236 unsigned long cksum;
1246 assert(pkt != NULL);
1247 assert(ticket_str != NULL);
1249 ticket.length = sizeof(ticket.dat);
1250 astr2bin(ticket_str, ticket.dat, &ticket.length);
1251 assert(ticket.length > 0);
1253 /* get a copy of the instance into writable memory */
1254 #if CLIENT_HOST_INSTANCE == HOSTNAME_INSTANCE
1255 strncpy(inst, krb_get_phost(hostname), sizeof(inst) - 1);
1257 strncpy(inst, CLIENT_HOST_INSTANCE, sizeof(inst) - 1);
1259 inst[sizeof(inst) - 1] = '\0';
1261 /* get the checksum out of the ticket */
1262 rc = krb_rd_req(&ticket, CLIENT_HOST_PRINCIPLE, inst,
1263 kh->peer.sin_addr.s_addr, &auth, CLIENT_HOST_KEY_FILE);
1265 security_seterror(&kh->sech,
1266 "krb_rd_req failed for %s: %s (%d)", kh->hostname,
1267 error_message(rc), rc);
1271 /* verify and save the checksum and session key */
1272 if (auth.checksum != cksum) {
1273 security_seterror(&kh->sech,
1274 "krb4 checksum mismatch for %s (remote=%lu, local=%lu)",
1275 kh->hostname, (long)auth.checksum, cksum);
1279 memcpy(kh->session_key, auth.session, sizeof(kh->session_key));
1282 * If FORCE_USERID is set, then we need to specifically
1283 * check the userid we're forcing ourself to. Otherwise,
1284 * just check the login we're currently setuid to.
1287 if ((pwd = getpwnam(CLIENT_LOGIN)) == NULL)
1288 error("error [getpwnam(%s) fails]", CLIENT_LOGIN);
1290 if ((pwd = getpwuid(getuid())) == NULL)
1291 error("error [getpwuid(%d) fails]", getuid());
1294 /* save the username in case it's overwritten */
1295 user = stralloc(pwd->pw_name);
1297 /* check the klogin file */
1298 if (kuserok(&auth, user)) {
1299 security_seterror(&kh->sech,
1300 "access as %s not allowed from %s.%s@%s", user, auth.pname,
1301 auth.pinst, auth.prealm);
1312 * Verify that the packet received is secure by verifying that it has
1313 * the same checksum as our request + 1.
1316 check_mutual_auth(kh, mutual_auth_str)
1317 struct krb4_handle *kh;
1318 const char *mutual_auth_str;
1320 union mutual mutual;
1324 assert(mutual_auth_str != NULL);
1325 assert(kh->inst[0] != '\0');
1326 /* we had better have a checksum from a request we sent */
1327 assert(kh->cksum != 0);
1329 /* convert the encoded string into binary data */
1330 len = sizeof(mutual);
1331 astr2bin(mutual_auth_str, (unsigned char *)&mutual, &len);
1333 /* unencrypt the string using the key in the ticket file */
1334 host2key(kh->hostname, kh->inst, &kh->session_key);
1335 decrypt_data(&mutual, len, &kh->session_key);
1336 mutual.cksum = ntohl(mutual.cksum);
1338 /* the data must be the same as our request cksum + 1 */
1339 if (mutual.cksum != kh->cksum + 1) {
1340 security_seterror(&kh->sech,
1341 "krb4 checksum mismatch from %s (remote=%lu, local=%lu)",
1342 kh->hostname, mutual.cksum, kh->cksum + 1);
1350 * Convert a pkt_t into a header string for our packet
1354 const struct krb4_handle *kh;
1357 static char retbuf[256];
1360 assert(pkt != NULL);
1362 snprintf(retbuf, sizeof(retbuf), "Amanda %d.%d %s HANDLE %s SEQ %d\n",
1363 VERSION_MAJOR, VERSION_MINOR, pkt_type2str(pkt->type),
1364 kh->proto_handle, kh->sequence);
1366 /* check for truncation. If only we had asprintf()... */
1367 assert(retbuf[strlen(retbuf) - 1] == '\n');
1373 * Parses out the header line in 'str' into the pkt and handle
1374 * Returns negative on parse error.
1377 str2pkthdr(origstr, pkt, handle, handlesize, sequence)
1378 const char *origstr;
1387 assert(origstr != NULL);
1388 assert(pkt != NULL);
1390 str = stralloc(origstr);
1392 /* "Amanda %d.%d <ACK,NAK,...> HANDLE %s SEQ %d\n" */
1394 /* Read in "Amanda" */
1395 if ((tok = strtok(str, " ")) == NULL || strcmp(tok, "Amanda") != 0)
1398 /* nothing is done with the major/minor numbers currently */
1399 if ((tok = strtok(NULL, " ")) == NULL || strchr(tok, '.') == NULL)
1402 /* Read in the packet type */
1403 if ((tok = strtok(NULL, " ")) == NULL)
1405 pkt_init(pkt, pkt_str2type(tok), "");
1406 if (pkt->type == (pktype_t)-1)
1409 /* Read in "HANDLE" */
1410 if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "HANDLE") != 0)
1413 /* parse the handle */
1414 if ((tok = strtok(NULL, " ")) == NULL)
1416 strncpy(handle, tok, handlesize - 1);
1417 handle[handlesize - 1] = '\0';
1420 if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "SEQ") != 0)
1423 /* parse the sequence number */
1424 if ((tok = strtok(NULL, "\n")) == NULL)
1426 *sequence = atoi(tok);
1428 /* Save the body, if any */
1429 if ((tok = strtok(NULL, "")) != NULL)
1436 #if 0 /* XXX we have no way of passing this back up */
1437 security_seterror(&kh->sech,
1438 "parse error in packet header : '%s'", origstr);
1445 host2key(hostname, inst, key)
1446 const char *hostname, *inst;
1452 strncpy(realm, krb_realmofhost((char *)hostname), sizeof(realm) - 1);
1453 realm[sizeof(realm) - 1] = '\0';
1454 #if CLIENT_HOST_INSTANCE != HOSTNAME_INSTANCE
1455 inst = CLIENT_HOST_INSTANCE
1457 krb_get_cred(CLIENT_HOST_PRINCIPLE, (char *)inst, realm, &cred);
1458 memcpy(key, cred.session, sizeof(des_cblock));
1463 * Convert a chunk of data into a string.
1467 const unsigned char *buf;
1470 static const char tohex[] = "0123456789ABCDEF";
1471 static char *str = NULL;
1473 const unsigned char *p;
1477 * calculate output string len
1478 * We quote everything, so each input byte == 3 output chars, plus
1479 * two more for quotes
1481 slen = (len * 3) + 2;
1483 /* allocate string and fill it in */
1486 str = alloc(slen + 1);
1490 for (i = 0; i < len; i++) {
1492 *q++ = tohex[(*p >> 4) & 0xF];
1493 *q++ = tohex[*p & 0xF];
1499 /* make sure we didn't overrun our allocated buffer */
1500 assert(q - str == slen);
1506 * Convert an encoded string into a block of data bytes
1509 astr2bin(astr, buf, lenp)
1516 #define fromhex(h) (isdigit((int)h) ? (h) - '0' : (h) - 'A' + 10)
1519 * Skip leading quote, if any
1525 * Run through the string. Anything starting with a $ is a three
1526 * char representation of this byte. Everything else is literal.
1528 for (p = astr, q = buf; *p != '"' && *p != '\0'; ) {
1532 *q++ = (fromhex(p[1]) << 4) + fromhex(p[2]);
1535 if (q - buf >= *lenp)
1541 static unsigned long
1547 memset(seed, 0, sizeof(seed));
1549 * The first arg is an unsigned char * in some krb4 implementations,
1550 * and in others, it's a des_cblock *. Just make it void here
1551 * to shut them all up.
1553 return (quad_cksum((void *)str, NULL, strlen(str), 1, &seed));
1557 krb4_getinst(hname, inst, size)
1564 * Copy the first part of the hostname up to the first '.' into
1565 * the buffer provided. Leave room for a NULL.
1567 while (size > 1 && *hname != '\0' && *hname != '.') {
1568 *inst++ = isupper((int)*hname) ? tolower((int)*hname) : *hname;
1576 encrypt_data(data, length, key)
1581 des_key_schedule sched;
1584 * First arg is des_cblock * in some places, and just des_cblock in
1585 * others. Since des_cblock is a char array, they are equivalent.
1586 * Just cast it to void * to keep both compilers quiet. typedefing
1587 * arrays should be outlawed.
1589 des_key_sched((void *)key, sched);
1590 des_pcbc_encrypt(data, data, length, sched, key, DES_ENCRYPT);
1595 decrypt_data(data, length, key)
1600 des_key_schedule sched;
1602 des_key_sched((void *)key, sched);
1603 des_pcbc_encrypt(data, data, length, sched, key, DES_DECRYPT);
1607 * like write(), but always writes out the entire buffer.
1610 net_write(fd, vbuf, size)
1615 const char *buf = vbuf; /* so we can do ptr arith */
1619 n = write(fd, buf, size);
1629 * Like read(), but waits until the entire buffer has been filled.
1632 net_read(fd, vbuf, size, timeout)
1638 char *buf = vbuf; /* ptr arith */
1645 FD_SET(fd, &readfds);
1646 tv.tv_sec = timeout;
1648 switch (select(fd + 1, &readfds, NULL, NULL, &tv)) {
1655 assert(FD_ISSET(fd, &readfds));
1661 n = read(fd, buf, size);
1664 /* we only tolerate so many eof's */
1665 if (n == 0 && ++neof > 1024) {
1676 /* -------------------------- */
1677 /* debug routines */
1680 print_hex(str,buf,len)
1682 const unsigned char *buf;
1687 dbprintf(("%s:", str));
1688 for(i=0;i<len;i++) {
1689 if(i%25 == 0) dbprintf(("\n"));
1690 dbprintf((" %02X", buf[i]));
1696 print_ticket(str, tktp)
1700 dbprintf(("%s: length %d chk %lX\n", str, tktp->length, tktp->mbz));
1701 print_hex("ticket data", tktp->dat, tktp->length);
1709 printf("\nAuth Data:\n");
1710 printf(" Principal \"%s\" Instance \"%s\" Realm \"%s\"\n",
1711 authp->pname, authp->pinst, authp->prealm);
1712 printf(" cksum %d life %d keylen %ld\n", authp->checksum,
1713 authp->life, sizeof(authp->session));
1714 print_hex("session key", authp->session, sizeof(authp->session));
1719 print_credentials(credp)
1722 printf("\nCredentials:\n");
1723 printf(" service \"%s\" instance \"%s\" realm \"%s\" life %d kvno %d\n",
1724 credp->service, credp->instance, credp->realm, credp->lifetime,
1726 print_hex("session key", credp->session, sizeof(credp->session));
1727 print_hex("ticket", credp->ticket_st.dat, credp->ticket_st.length);
1733 void krb4_security_dummy (void) {}
1734 #endif /* KRB4_SECURITY */