Imported Upstream version 2.5.1
[debian/amanda] / common-src / krb4-security.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1993,1999 University of Maryland
4  * All Rights Reserved.
5  *
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.
15  *
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.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26
27 /*
28  * $Id: krb4-security.c,v 1.18 2006/07/13 03:22:20 paddy_s Exp $
29  *
30  * krb4-security.c - helper functions for kerberos v4 security.
31  */
32
33 #include "config.h"
34 #ifdef KRB4_SECURITY
35
36 #include <des.h>
37 #include <krb.h>
38
39 #include "amanda.h"
40 #include "dgram.h"
41 #include "event.h"
42 #include "packet.h"
43 #include "queue.h"
44 #include "security.h"
45 #include "security-util.h"
46 #include "protocol.h"
47 #include "stream.h"
48 #include "version.h"
49
50
51 /*
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
54  * way
55  */
56 #ifndef HAVE_ATEXIT
57 #ifdef HAVE_ON_EXIT
58 #define atexit(func)    on_exit(func, 0)
59 #else
60 #define atexit(func)    (you must to resolve lack of atexit)
61 #endif  /* HAVE_ON_EXIT */
62 #endif  /* ! HAVE_ATEXIT */
63
64 int krb_set_lifetime(int);
65 int kuserok(AUTH_DAT *, char *);
66
67 /*
68  * This is the private handle data
69  */
70 struct krb4_handle {
71     security_handle_t sech;     /* MUST be first */
72     struct sockaddr_in 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 */
80
81     /*
82      * The rest is used for the async recvpkt/recvpkt_cancel
83      * interface.
84      */
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 */
90 };
91
92 /*
93  * This is the internal security_stream data for krb4.
94  */
95 struct krb4_stream {
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 */
103     int len;                            /* */
104     void (*fn)(void *, void *, ssize_t);/* read event fn */
105     void *arg;                          /* arg for previous */
106 };
107
108 /*
109  * This is the tcp stream buffer size
110  */
111 #define STREAM_BUFSIZE  (MAX_TAPE_BLOCK_BYTES * 2)
112
113 /*
114  * Interface functions
115  */
116 static void     krb4_connect(const char *, char *(*)(char *, void *),  
117                         void (*)(void *, security_handle_t *, security_status_t),
118                         void *, void *);
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),
123                         void *, int);
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 *);
135
136
137 /*
138  * This is our interface to the outside world.
139  */
140 const security_driver_t krb4_security_driver = {
141     "KRB4",
142     krb4_connect,
143     krb4_accept,
144     krb4_close,
145     krb4_sendpkt,
146     krb4_recvpkt,
147     krb4_recvpkt_cancel,
148     krb4_stream_server,
149     krb4_stream_accept,
150     krb4_stream_client,
151     krb4_stream_close,
152     krb4_stream_auth,
153     krb4_stream_id,
154     krb4_stream_write,
155     krb4_stream_read,
156     krb4_stream_read_sync,
157     krb4_stream_read_cancel,
158     sec_close_connection_none,
159 };
160
161 /*
162  * Cache the local hostname
163  */
164 static char hostname[MAX_HOSTNAME_LENGTH+1];
165
166 /*
167  * This is the dgram_t that we use to send and recv protocol packets
168  * over the net.  There is only one per process, so it lives globally
169  * here.
170  */
171 static dgram_t netfd;
172
173 /*
174  * This is a queue of outstanding async requests
175  */
176 static struct {
177     TAILQ_HEAD(, krb4_handle) tailq;
178     int qlength;
179 } handleq = {
180     TAILQ_HEAD_INITIALIZER(handleq.tailq), 0
181 };
182
183 /*
184  * Macros to add or remove krb4_handles from the above queue
185  */
186 #define handleq_add(kh) do {                    \
187     assert(handleq.qlength == 0 ? TAILQ_FIRST(&handleq.tailq) == NULL : 1); \
188     TAILQ_INSERT_TAIL(&handleq.tailq, kh, tq);  \
189     handleq.qlength++;                          \
190 } while (0)
191
192 #define handleq_remove(kh)      do {                    \
193     assert(handleq.qlength > 0);                        \
194     assert(TAILQ_FIRST(&handleq.tailq) != NULL);        \
195     TAILQ_REMOVE(&handleq.tailq, kh, tq);               \
196     handleq.qlength--;                                  \
197     assert((handleq.qlength == 0) ^ (TAILQ_FIRST(&handleq.tailq) != NULL)); \
198 } while (0)
199
200 #define handleq_first()         TAILQ_FIRST(&handleq.tailq)
201 #define handleq_next(kh)        TAILQ_NEXT(kh, tq)
202
203
204 /*
205  * This is the event manager's handle for our netfd
206  */
207 static event_handle_t *ev_netfd;
208
209 /*
210  * This is a function that should be called if a new security_handle_t is
211  * created.  If NULL, no new handles are created.
212  * It is passed the new handle and the received pkt
213  */
214 static void (*accept_fn)(security_handle_t *, pkt_t *);
215
216
217 /*
218  * This is a structure used in encoding the cksum in a mutual-auth
219  * transaction.  The checksum is placed in here first before encryption
220  * because encryption requires at least 8 bytes of data, and an unsigned
221  * long on most machines (32 bit ones) is 4 bytes.
222  */
223 union mutual {
224     char pad[8];
225     long cksum;
226 };
227
228 /*
229  * Private functions
230  */
231 static unsigned long krb4_cksum(const char *);
232 static void krb4_getinst(const char *, char *, size_t);
233 static void host2key(const char *, const char *, des_cblock *);
234 static void init(void);
235 static void inithandle(struct krb4_handle *, struct hostent *, int,
236     const char *);
237 static void get_tgt(void);
238 static void killtickets(void);
239 static void recvpkt_callback(void *);
240 static void recvpkt_timeout(void *);
241 static int recv_security_ok(struct krb4_handle *, pkt_t *);
242 static void stream_read_callback(void *);
243 static void stream_read_sync_callback(void *);
244 static int net_write(int, const void *, size_t);
245 static int net_read(int, void *, size_t, int);
246
247 static int add_ticket(struct krb4_handle *, const pkt_t *, dgram_t *);
248 static void add_mutual_auth(struct krb4_handle *, dgram_t *);
249 static int check_ticket(struct krb4_handle *, const pkt_t *,
250     const char *, unsigned long);
251 static int check_mutual_auth(struct krb4_handle *, const char *);
252
253 static const char *pkthdr2str(const struct krb4_handle *, const pkt_t *);
254 static int str2pkthdr(const char *, pkt_t *, char *, size_t, int *);
255
256 static const char *bin2astr(const unsigned char *, int);
257 static void astr2bin(const unsigned char *, unsigned char *, int *);
258
259 static void encrypt_data(void *, size_t, des_cblock *);
260 static void decrypt_data(void *, size_t, des_cblock *);
261
262 #define HOSTNAME_INSTANCE inst
263
264 static char *ticketfilename = NULL;
265
266 static void
267 killtickets(void)
268 {
269     if (ticketfilename != NULL)
270         unlink(ticketfilename);
271     amfree(ticketfilename);
272 }
273
274 /*
275  * Setup some things about krb4.  This should only be called once.
276  */
277 static void
278 init(void)
279 {
280     char tktfile[256];
281     int port;
282     static int beenhere = 0;
283
284     if (beenhere)
285         return;
286     beenhere = 1;
287
288     gethostname(hostname, SIZEOF(hostname) - 1);
289     hostname[SIZEOF(hostname) - 1] = '\0';
290
291     if (atexit(killtickets) < 0)
292         error("could not setup krb4 exit handler: %s", strerror(errno));
293
294     /*
295      * [XXX] It could be argued that if KRBTKFILE is set outside of amanda,
296      * that it's value should be used instead of us setting one up.
297      * This file also needs to be removed so that no extra tickets are
298      * hanging around.
299      */
300     snprintf(tktfile, SIZEOF(tktfile), "/tmp/tkt%ld-%ld.amanda",
301         (long)getuid(), (long)getpid());
302     ticketfilename = stralloc(tktfile);
303     unlink(ticketfilename);
304     krb_set_tkt_string(ticketfilename);
305 #if defined(HAVE_PUTENV)
306     {
307         char *tkt_env = stralloc2("KRBTKFILE=", ticketfilename);
308         putenv(tkt_env);
309     }
310 #else
311     setenv("KRBTKFILE", ticketfile, 1);
312 #endif
313
314     dgram_zero(&netfd);
315     dgram_bind(&netfd, &port);
316 }
317
318 /*
319  * Get a ticket granting ticket and stuff it in the cache
320  */
321 static void
322 get_tgt(void)
323 {
324     char realm[REALM_SZ];
325     int rc;
326
327     strncpy(realm, krb_realmofhost(hostname), SIZEOF(realm) - 1);
328     realm[SIZEOF(realm) - 1] = '\0';
329
330     rc = krb_get_svc_in_tkt(SERVER_HOST_PRINCIPLE, SERVER_HOST_INSTANCE,
331         realm, "krbtgt", realm, TICKET_LIFETIME, SERVER_HOST_KEY_FILE);
332
333     if (rc != 0) {
334         error("could not get krbtgt for %s.%s@%s from %s: %s",
335             SERVER_HOST_PRINCIPLE, SERVER_HOST_INSTANCE, realm,
336             SERVER_HOST_KEY_FILE, krb_err_txt[rc]);
337     }
338
339     krb_set_lifetime(TICKET_LIFETIME);
340 }
341
342
343 /*
344  * krb4 version of a security handle allocator.  Logically sets
345  * up a network "connection".
346  */
347 static void
348 krb4_connect(
349     const char *hostname,
350     char *      (*conf_fn)(char *, void *),
351     void        (*fn)(void *, security_handle_t *, security_status_t),
352     void *      arg,
353     void *      datap)
354 {
355     struct krb4_handle *kh;
356     char handle[32];
357     struct servent *se;
358     struct hostent *he;
359     int port;
360
361     assert(hostname != NULL);
362
363     /*
364      * Make sure we're initted
365      */
366     init();
367
368     kh = alloc(SIZEOF(*kh));
369     security_handleinit(&kh->sech, &krb4_security_driver);
370
371     if ((he = gethostbyname(hostname)) == NULL) {
372         security_seterror(&kh->sech,
373             "%s: could not resolve hostname", hostname);
374         (*fn)(arg, &kh->sech, S_ERROR);
375         return;
376     }
377     if ((se = getservbyname(KAMANDA_SERVICE_NAME, "udp")) == NULL)
378         port = (int)htons(KAMANDA_SERVICE_DEFAULT);
379     else
380         port = se->s_port;
381     snprintf(handle, SIZEOF(handle), "%ld", (long)time(NULL));
382     inithandle(kh, he, (int)port, handle);
383     (*fn)(arg, &kh->sech, S_OK);
384 }
385
386 /*
387  * Setup to handle new incoming connections
388  */
389 static void
390 krb4_accept(
391     const struct security_driver *driver,
392     int         in,
393     int         out,
394     void        (*fn)(security_handle_t *, pkt_t *))
395 {
396
397     /*
398      * Make sure we're initted
399      */
400     init();
401
402     /*
403      * We assume that in and out both point to the same socket
404      */
405     dgram_socket(&netfd, in);
406
407     /*
408      * Assign the function and return.  When they call recvpkt later,
409      * the recvpkt callback will call this function when it discovers
410      * new incoming connections
411      */
412     accept_fn = fn;
413
414     if (ev_netfd == NULL)
415         ev_netfd = event_register((event_id_t)netfd.socket, EV_READFD,
416                             recvpkt_callback, NULL);
417 }
418
419 /*
420  * Given a hostname and a port, setup a krb4_handle
421  */
422 static void
423 inithandle(
424     struct krb4_handle *kh,
425     struct hostent *    he,
426     int                 port,
427     const char *        handle)
428 {
429
430     /*
431      * Get the instance and realm for this host
432      * (krb_realmofhost always returns something)
433      */
434     krb4_getinst(he->h_name, kh->inst, SIZEOF(kh->inst));
435     strncpy(kh->realm, krb_realmofhost(he->h_name), SIZEOF(kh->realm) - 1);
436     kh->realm[SIZEOF(kh->realm) - 1] = '\0';
437
438     /*
439      * Save a copy of the hostname
440      */
441     strncpy(kh->hostname, he->h_name, SIZEOF(kh->hostname) - 1);
442     kh->hostname[SIZEOF(kh->hostname) - 1] = '\0';
443
444     /*
445      * We have no checksum or session key at this point
446      */
447     kh->cksum = 0;
448     memset(kh->session_key, 0, SIZEOF(kh->session_key));
449
450     /*
451      * Setup our peer info.  We don't do anything with the sequence yet,
452      * so just leave it at 0.
453      */
454     kh->peer.sin_family = (sa_family_t)AF_INET;
455     kh->peer.sin_port = (in_port_t)port;
456     kh->peer.sin_addr = *(struct in_addr *)he->h_addr;
457     strncpy(kh->proto_handle, handle, SIZEOF(kh->proto_handle) - 1);
458     kh->proto_handle[SIZEOF(kh->proto_handle) - 1] = '\0';
459     kh->sequence = 0;
460     kh->fn = NULL;
461     kh->arg = NULL;
462     kh->ev_timeout = NULL;
463 }
464
465 /*
466  * frees a handle allocated by the above
467  */
468 static void
469 krb4_close(
470     void *      inst)
471 {
472
473     krb4_recvpkt_cancel(inst);
474     amfree(inst);
475 }
476
477 /*
478  * Transmit a packet.  Add security information first.
479  */
480 static ssize_t
481 krb4_sendpkt(
482     void *      cookie,
483     pkt_t *     pkt)
484 {
485     struct krb4_handle *kh = cookie;
486
487     assert(kh != NULL);
488     assert(pkt != NULL);
489
490     /*
491      * Initialize this datagram
492      */
493     dgram_zero(&netfd);
494
495     /*
496      * Add the header to the packet
497      */
498     dgram_cat(&netfd, pkthdr2str(kh, pkt));
499
500     /*
501      * Add the security info.  This depends on which kind of packet we're
502      * sending.
503      */
504     switch (pkt->type) {
505     case P_REQ:
506         /*
507          * Requests get sent with a ticket embedded in the header.  The
508          * checksum is generated from the contents of the body.
509          */
510         if (add_ticket(kh, pkt, &netfd) < 0)
511             return (-1);
512         break;
513     case P_REP:
514         /*
515          * Replies get sent with a mutual authenticator added.  The
516          * mutual authenticator is the encrypted checksum from the
517          * ticket + 1
518          */
519         add_mutual_auth(kh, &netfd);
520         break;
521     case P_ACK:
522     case P_NAK:
523     default:
524         /*
525          * The other types have no security stuff added for krb4.
526          * Shamefull.
527          */
528         break;
529     }
530
531     /*
532      * Add the body, and send it
533      */
534     dgram_cat(&netfd, pkt->body);
535     if (dgram_send_addr(kh->peer, &netfd) != 0) {
536         security_seterror(&kh->sech,
537             "send %s to %s failed: %s", pkt_type2str(pkt->type),
538             kh->hostname, strerror(errno));
539         return (-1);
540     }
541     return (0);
542 }
543
544 /*
545  * Set up to receive a packet asyncronously, and call back when
546  * it has been read.
547  */
548 static void
549 krb4_recvpkt(
550     void *      cookie,
551     void        (*fn)(void *, pkt_t *, security_status_t),
552     void *      arg,
553     int         timeout)
554 {
555     struct krb4_handle *kh = cookie;
556
557     assert(netfd.socket >= 0);
558     assert(kh != NULL);
559
560     /*
561      * We register one event handler for our network fd which takes
562      * care of all of our async requests.  When all async requests
563      * have either been satisfied or cancelled, we unregister our
564      * network event handler.
565      */
566     if (ev_netfd == NULL) {
567         assert(handleq.qlength == 0);
568         ev_netfd = event_register((event_id_t)netfd.socket, EV_READFD,
569                             recvpkt_callback, NULL);
570     }
571
572     /*
573      * Multiple recvpkt calls override previous ones
574      * If kh->fn is NULL then it is not in the queue.
575      */
576     if (kh->fn == NULL)
577         handleq_add(kh);
578     if (kh->ev_timeout != NULL)
579         event_release(kh->ev_timeout);
580     if (timeout < 0)
581         kh->ev_timeout = NULL;
582     else
583         kh->ev_timeout = event_register((event_id_t)timeout, EV_TIME,
584                                 recvpkt_timeout, kh);
585     kh->fn = fn;
586     kh->arg = arg;
587 }
588
589 /*
590  * Remove a async receive request from the queue
591  * If it is the last one to be removed, then remove the event handler
592  * for our network fd.
593  */
594 static void
595 krb4_recvpkt_cancel(
596     void *      cookie)
597 {
598     struct krb4_handle *kh = cookie;
599
600     assert(kh != NULL);
601
602     if (kh->fn != NULL) {
603         handleq_remove(kh);
604         kh->fn = NULL;
605         kh->arg = NULL;
606     }
607     if (kh->ev_timeout != NULL)
608         event_release(kh->ev_timeout);
609     kh->ev_timeout = NULL;
610
611     if (handleq.qlength == 0 && accept_fn == NULL &&
612         ev_netfd != NULL) {
613         event_release(ev_netfd);
614         ev_netfd = NULL;
615     }
616 }
617
618 /*
619  * Create the server end of a stream.  For krb4, this means setup a tcp
620  * socket for receiving a connection.
621  */
622 static void *
623 krb4_stream_server(
624     void *      h)
625 {
626     struct krb4_handle *kh = h;
627     struct krb4_stream *ks;
628
629     assert(kh != NULL);
630
631     ks = alloc(SIZEOF(*ks));
632     security_streaminit(&ks->secstr, &krb4_security_driver);
633     ks->socket = stream_server(&ks->port, STREAM_BUFSIZE, STREAM_BUFSIZE, 1);
634     if (ks->socket < 0) {
635         security_seterror(&kh->sech,
636             "can't create server stream: %s", strerror(errno));
637         amfree(ks);
638         return (NULL);
639     }
640     ks->fd = -1;
641     ks->krb4_handle = kh;
642     ks->ev_read = NULL;
643     return (ks);
644 }
645
646 /*
647  * Accept an incoming connection on a stream_server socket
648  */
649 static int
650 krb4_stream_accept(
651     void *      s)
652 {
653     struct krb4_stream *ks = s;
654     struct krb4_handle *kh;
655
656     assert(ks != NULL);
657     kh = ks->krb4_handle;
658     assert(kh != NULL);
659     assert(ks->socket >= 0);
660     assert(ks->fd == -1);
661
662     ks->fd = stream_accept(ks->socket, 30, STREAM_BUFSIZE, STREAM_BUFSIZE);
663     if (ks->fd < 0) {
664         security_stream_seterror(&ks->secstr,
665             "can't accept new stream connection: %s", strerror(errno));
666         return (-1);
667     }
668     return (0);
669 }
670
671 /*
672  * Return a connected stream.
673  */
674 static void *
675 krb4_stream_client(
676     void *      h,
677     int         id)
678 {
679     struct krb4_handle *kh = h;
680     struct krb4_stream *ks;
681
682     assert(kh != NULL);
683
684     ks = alloc(SIZEOF(*ks));
685     security_streaminit(&ks->secstr, &krb4_security_driver);
686     ks->fd = stream_client(kh->hostname, id, STREAM_BUFSIZE, STREAM_BUFSIZE,
687         &ks->port, 0);
688     if (ks->fd < 0) {
689         security_seterror(&kh->sech,
690             "can't connect stream to %s port %d: %s", kh->hostname, id,
691             strerror(errno));
692         amfree(ks);
693         return (NULL);
694     }
695
696     ks->socket = -1;    /* we're a client */
697     ks->krb4_handle = kh;
698     ks->ev_read = NULL;
699     return (ks);
700 }
701
702 /*
703  * Close and unallocate resources for a stream.
704  */
705 static void
706 krb4_stream_close(
707     void *      s)
708 {
709     struct krb4_stream *ks = s;
710
711     assert(ks != NULL);
712
713     if (ks->fd != -1)
714         aclose(ks->fd);
715     if (ks->socket != -1)
716         aclose(ks->socket);
717     krb4_stream_read_cancel(ks);
718     amfree(ks);
719 }
720
721 /*
722  * Authenticate a stream
723  *
724  * XXX this whole thing assumes the size of struct timeval is consistent,
725  * which is may not be!  We need to extract the network byte order data
726  * into byte arrays and send those.
727  */
728 static int
729 krb4_stream_auth(
730     void *      s)
731 {
732     struct krb4_stream *ks = s;
733     struct krb4_handle *kh;
734     int fd;
735     struct timeval local, enc;
736     struct timezone tz;
737
738     assert(ks != NULL);
739     assert(ks->fd >= 0);
740
741     fd = ks->fd;
742     kh = ks->krb4_handle;
743
744     /* make sure we're open */
745     assert(fd >= 0);
746
747     /* make sure our timeval is what we're expecting, see above */
748     assert(SIZEOF(struct timeval) == 8);
749
750     /*
751      * Get the current time, put it in network byte order, encrypt it
752      * and present it to the other side.
753      */
754     gettimeofday(&local, &tz);
755     enc.tv_sec = (long)htonl((uint32_t)local.tv_sec);
756     enc.tv_usec = (long)htonl((uint32_t)local.tv_usec);
757     encrypt_data(&enc, SIZEOF(enc), &kh->session_key);
758     if (net_write(fd, &enc, SIZEOF(enc)) < 0) {
759         security_stream_seterror(&ks->secstr,
760             "krb4 stream handshake write error: %s", strerror(errno));
761         return (-1);
762     }
763
764     /*
765      * Read back the other side's presentation.  Increment the seconds
766      * and useconds by one.  Reencrypt, and present to the other side.
767      * Timeout in 10 seconds.
768      */
769     if (net_read(fd, &enc, SIZEOF(enc), 60) < 0) {
770         security_stream_seterror(&ks->secstr,
771             "krb4 stream handshake read error: %s", strerror(errno));
772         return (-1);
773     }
774     decrypt_data(&enc, SIZEOF(enc), &kh->session_key);
775     /* XXX do timestamp checking here */
776     enc.tv_sec = (long)htonl(ntohl((uint32_t)enc.tv_sec) + 1);
777     enc.tv_usec =(long)htonl(ntohl((uint32_t)enc.tv_usec) + 1);
778     encrypt_data(&enc, SIZEOF(enc), &kh->session_key);
779
780     if (net_write(fd, &enc, SIZEOF(enc)) < 0) {
781         security_stream_seterror(&ks->secstr,
782             "krb4 stream handshake write error: %s", strerror(errno));
783         return (-1);
784     }
785
786     /*
787      * Read back the other side's processing of our data.
788      * If they incremented it properly, then succeed.
789      * Timeout in 10 seconds.
790      */
791     if (net_read(fd, &enc, SIZEOF(enc), 60) < 0) {
792         security_stream_seterror(&ks->secstr,
793             "krb4 stream handshake read error: %s", strerror(errno));
794         return (-1);
795     }
796     decrypt_data(&enc, SIZEOF(enc), &kh->session_key);
797     if ((ntohl((uint32_t)enc.tv_sec)  == (uint32_t)(local.tv_sec + 1)) &&
798         (ntohl((uint32_t)enc.tv_usec) == (uint32_t)(local.tv_usec + 1)))
799             return (0);
800
801     security_stream_seterror(&ks->secstr,
802         "krb4 handshake failed: sent %ld,%ld - recv %ld,%ld",
803             (long)(local.tv_sec + 1), (long)(local.tv_usec + 1),
804             (long)ntohl((uint32_t)enc.tv_sec),
805             (long)ntohl((uint32_t)enc.tv_usec));
806     return (-1);
807 }
808
809 /*
810  * Returns the stream id for this stream.  This is just the local
811  * port.
812  */
813 static int
814 krb4_stream_id(
815     void *      s)
816 {
817     struct krb4_stream *ks = s;
818
819     assert(ks != NULL);
820
821     return (ks->port);
822 }
823
824 /*
825  * Write a chunk of data to a stream.  Blocks until completion.
826  */
827 static int
828 krb4_stream_write(
829     void *      s,
830     const void *buf,
831     size_t      size)
832 {
833     struct krb4_stream *ks = s;
834     struct krb4_handle *kh = ks->krb4_handle;
835
836     assert(ks != NULL);
837     assert(kh != NULL);
838
839     if (net_write(ks->fd, buf, size) < 0) {
840         security_stream_seterror(&ks->secstr,
841             "write error on stream %d: %s", ks->fd, strerror(errno));
842         return (-1);
843     }
844     return (0);
845 }
846
847 /*
848  * Submit a request to read some data.  Calls back with the given
849  * function and arg when completed.
850  */
851 static void
852 krb4_stream_read(
853     void *      s,
854     void        (*fn)(void *, void *, ssize_t),
855     void *      arg)
856 {
857     struct krb4_stream *ks = s;
858
859     assert(ks != NULL);
860
861     /*
862      * Only one read request can be active per stream.
863      */
864     if (ks->ev_read != NULL)
865         event_release(ks->ev_read);
866
867     ks->ev_read = event_register((event_id_t)ks->fd, EV_READFD,
868                         stream_read_callback, ks);
869     ks->fn = fn;
870     ks->arg = arg;
871 }
872
873 /*
874  * Write a chunk of data to a stream.  Blocks until completion.
875  */
876 static ssize_t
877 krb4_stream_read_sync(
878     void *      s,
879     void **     buf)
880 {
881     struct krb4_stream *ks = s;
882
883     (void)buf;  /* Quiet unused variable warning */
884     assert(ks != NULL);
885
886     if (ks->ev_read != NULL)
887         event_release(ks->ev_read);
888
889     ks->ev_read = event_register((event_id_t)ks->fd, EV_READFD,
890                         stream_read_sync_callback, ks);
891     event_wait(ks->ev_read);
892     return((ssize_t)ks->len);
893 }
894
895 /*
896  * Callback for krb4_stream_read_sync
897  */
898 static void
899 stream_read_sync_callback(
900     void *      arg)
901 {
902     struct krb4_stream *ks = arg;
903     ssize_t n;
904
905     assert(ks != NULL);
906     assert(ks->fd != -1);
907
908     /*
909      * Remove the event first, and then call the callback.
910      * We remove it first because we don't want to get in their
911      * way if they reschedule it.
912      */
913     krb4_stream_read_cancel(ks);
914     n = read(ks->fd, ks->databuf, sizeof(ks->databuf));
915     if (n < 0)
916         security_stream_seterror(&ks->secstr,
917             strerror(errno));
918     ks->len = (int)n;
919 }
920
921 /*
922  * Cancel a previous stream read request.  It's ok if we didn't have a read
923  * scheduled.
924  */
925 static void
926 krb4_stream_read_cancel(
927     void *      s)
928 {
929     struct krb4_stream *ks = s;
930
931     assert(ks != NULL);
932
933     if (ks->ev_read != NULL) {
934         event_release(ks->ev_read);
935         ks->ev_read = NULL;
936     }
937 }
938
939 /*
940  * Callback for krb4_stream_read
941  */
942 static void
943 stream_read_callback(
944     void *      arg)
945 {
946     struct krb4_stream *ks = arg;
947     ssize_t n;
948
949     assert(ks != NULL);
950     assert(ks->fd != -1);
951
952     /*
953      * Remove the event first, and then call the callback.
954      * We remove it first because we don't want to get in their
955      * way if they reschedule it.
956      */
957     krb4_stream_read_cancel(ks);
958     n = read(ks->fd, ks->databuf, SIZEOF(ks->databuf));
959     if (n < 0)
960         security_stream_seterror(&ks->secstr,
961             strerror(errno));
962     (*ks->fn)(ks->arg, ks->databuf, n);
963 }
964
965 /*
966  * The callback for recvpkt() for the event handler
967  * Determines if this packet is for this security handle,
968  * and does the real callback if so.
969  */
970 static void
971 recvpkt_callback(
972     void *      cookie)
973 {
974     char handle[32];
975     struct sockaddr_in peer;
976     pkt_t pkt;
977     int sequence;
978     struct krb4_handle *kh;
979     struct hostent *he;
980     void (*fn)(void *, pkt_t *, security_status_t);
981     void *arg;
982
983     assert(cookie == NULL);
984
985     /*
986      * Find the handle that this packet is associated with.  We
987      * need to peek at the packet to see what is in it, but we
988      * want to save the actual reading for later.
989      */
990     dgram_zero(&netfd);
991     if (dgram_recv(&netfd, 0, &peer) < 0)
992         return;
993     if (str2pkthdr(netfd.cur, &pkt, handle, SIZEOF(handle), &sequence) < 0)
994         return;
995
996     for (kh = handleq_first(); kh != NULL; kh = handleq_next(kh)) {
997         if (strcmp(kh->proto_handle, handle) == 0 &&
998             memcmp(&kh->peer.sin_addr, &peer.sin_addr,
999             SIZEOF(peer.sin_addr)) == 0 &&
1000             kh->peer.sin_port == peer.sin_port) {
1001             kh->sequence = sequence;
1002
1003             /*
1004              * We need to cancel the recvpkt request before calling
1005              * the callback because the callback may reschedule us.
1006              */
1007             fn = kh->fn;
1008             arg = kh->arg;
1009             krb4_recvpkt_cancel(kh);
1010             if (recv_security_ok(kh, &pkt) < 0)
1011                 (*fn)(arg, NULL, S_ERROR);
1012             else
1013                 (*fn)(arg, &pkt, S_OK);
1014             return;
1015         }
1016     }
1017     /*
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.
1020      */
1021     if (accept_fn == NULL)
1022         return;
1023
1024     he = gethostbyaddr((void *)&peer.sin_addr, SIZEOF(peer.sin_addr), AF_INET);
1025     if (he == NULL)
1026         return;
1027     kh = alloc(SIZEOF(*kh));
1028     security_handleinit(&kh->sech, &krb4_security_driver);
1029     inithandle(kh, he, (int)peer.sin_port, handle);
1030
1031     /*
1032      * Check the security of the packet.  If it is bad, then pass NULL
1033      * to the accept function instead of a packet.
1034      */
1035     if (recv_security_ok(kh, &pkt) < 0)
1036         (*accept_fn)(&kh->sech, NULL);
1037     else
1038         (*accept_fn)(&kh->sech, &pkt);
1039 }
1040
1041 /*
1042  * This is called when a handle times out before receiving a packet.
1043  */
1044 static void
1045 recvpkt_timeout(
1046     void *      cookie)
1047 {
1048     struct krb4_handle *kh = cookie;
1049     void (*fn)(void *, pkt_t *, security_status_t);
1050     void *arg;
1051
1052     assert(kh != NULL);
1053
1054     assert(kh->ev_timeout != NULL);
1055     fn = kh->fn;
1056     arg = kh->arg;
1057     krb4_recvpkt_cancel(kh);
1058     (*fn)(arg, NULL, S_TIMEOUT);
1059 }
1060
1061 /*
1062  * Add a ticket to the message
1063  */
1064 static int
1065 add_ticket(
1066     struct krb4_handle *kh,
1067     const pkt_t *       pkt,
1068     dgram_t *           msg)
1069 {
1070     char inst[INST_SZ];
1071     KTEXT_ST ticket;
1072     char *security;
1073     int rc;
1074
1075     kh->cksum = (long)krb4_cksum(pkt->body);
1076 #if CLIENT_HOST_INSTANCE == HOSTNAME_INSTANCE
1077     /*
1078      * User requested that all instances be based on the target
1079      * hostname.
1080      */
1081     strncpy(inst, kh->inst, SIZEOF(inst) - 1);
1082 #else
1083     /*
1084      * User requested a fixed instance.
1085      */
1086     strncpy(inst, CLIENT_HOST_INSTANCE, SIZEOF(inst) - 1);
1087 #endif
1088     inst[SIZEOF(inst) - 1] = '\0';
1089
1090     /*
1091      * Get a ticket with the user-defined service and instance,
1092      * and using the checksum of the body of the request packet.
1093      */
1094     rc = krb_mk_req(&ticket, CLIENT_HOST_PRINCIPLE, inst, kh->realm,
1095         kh->cksum);
1096     if (rc == NO_TKT_FIL) {
1097         /* It's been kdestroyed.  Get a new one and try again */
1098         get_tgt();
1099         rc = krb_mk_req(&ticket, CLIENT_HOST_PRINCIPLE, inst, kh->realm,
1100             kh->cksum);
1101     }
1102     if (rc != 0) {
1103         security_seterror(&kh->sech,
1104             "krb_mk_req failed: %s (%d)", error_message(rc), rc);
1105         return (-1);
1106     }
1107     /*
1108      * We now have the ticket.  Put it into the packet, and send
1109      * it.
1110      */
1111     security = vstralloc("SECURITY TICKET ",
1112         bin2astr(ticket.dat, ticket.length), "\n", NULL);
1113     dgram_cat(msg, security);
1114     amfree(security);
1115
1116     return (0);
1117 }
1118
1119 /*
1120  * Add the mutual authenticator.  This is the checksum from
1121  * the req, + 1
1122  */
1123 static void
1124 add_mutual_auth(
1125     struct krb4_handle *kh,
1126     dgram_t *           msg)
1127 {
1128     union mutual mutual;
1129     char *security;
1130
1131     assert(kh != NULL);
1132     assert(msg != NULL);
1133     assert(kh->cksum != 0);
1134     assert(kh->session_key[0] != '\0');
1135
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);
1139
1140     security = vstralloc("SECURITY MUTUAL-AUTH ",
1141         bin2astr((unsigned char *)mutual.pad,
1142                         (int)sizeof(mutual.pad)), "\n", NULL);
1143     dgram_cat(msg, security);
1144     amfree(security);
1145 }
1146
1147 /*
1148  * Check the security of a received packet.  Returns negative on error
1149  * or security violation and otherwise returns 0 and fills in the
1150  * passed packet.
1151  */
1152 static int
1153 recv_security_ok(
1154     struct krb4_handle *kh,
1155     pkt_t *             pkt)
1156 {
1157     char *tok, *security, *body;
1158     unsigned long cksum;
1159
1160     assert(kh != NULL);
1161     assert(pkt != NULL);
1162
1163     /*
1164      * Set this preemptively before we mangle the body.
1165      */
1166     security_seterror(&kh->sech,
1167         "bad %s SECURITY line from %s: '%s'", pkt_type2str(pkt->type),
1168         kh->hostname, pkt->body);
1169
1170
1171     /*
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.
1174      *
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.
1178      */
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, "");
1185         /*
1186          * If the body is f-ked, then try to recover.
1187          */
1188         if (body == NULL) {
1189             if (security != NULL)
1190                 body = security + strlen(security) + 2;
1191             else
1192                 body = pkt->body;
1193         }
1194     } else {
1195         security = NULL;
1196         body = pkt->body;
1197     }
1198
1199     /*
1200      * Get a checksum of the non-security parts of the body
1201      */
1202     cksum = krb4_cksum(body);
1203
1204     /*
1205      * Now deal with the data we did (or didn't) parse above
1206      */
1207     switch (pkt->type) {
1208     case P_REQ:
1209         /*
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.
1213          *
1214          * Must look like: SECURITY TICKET [ticket str]
1215          */
1216
1217         /* there must be some security info */
1218         if (security == NULL)
1219             return (-1);
1220
1221         /* second word must be TICKET */
1222         if ((tok = strtok(security, " ")) == NULL)
1223             return (-1);
1224         if (strcmp(tok, "TICKET") != 0) {
1225             security_seterror(&kh->sech,
1226                 "REQ SECURITY line parse error, expecting TICKET, got %s", tok);
1227             return (-1);
1228         }
1229
1230         /* the third word is the encoded ticket */
1231         if ((tok = strtok(NULL, "")) == NULL)
1232             return (-1);
1233         if (check_ticket(kh, pkt, tok, cksum) < 0)
1234             return (-1);
1235
1236         /* we're good to go */
1237         break;
1238
1239     case P_REP:
1240         /*
1241          * Reply packets check the mutual authenticator for this host.
1242          *
1243          * Must look like: SECURITY MUTUAL-AUTH [mutual auth str]
1244          */
1245         if (security == NULL)
1246             return (-1);
1247         if ((tok = strtok(security, " ")) == NULL)
1248             return (-1);
1249         if (strcmp(tok, "MUTUAL-AUTH") != 0)  {
1250             security_seterror(&kh->sech,
1251                 "REP SECURITY line parse error, expecting MUTUAL-AUTH, got %s",
1252                 tok);
1253             return (-1);
1254         }
1255         if ((tok = strtok(NULL, "")) == NULL)
1256             return (-1);
1257         if (check_mutual_auth(kh, tok) < 0)
1258             return (-1);
1259
1260     case P_ACK:
1261     case P_NAK:
1262     default:
1263         /*
1264          * These packets have no security.  They should, but such
1265          * is life.  We can't change it without breaking compatibility.
1266          *
1267          * XXX Should we complain if argc > 0? (ie, some security info was
1268          * sent?)
1269          */
1270         break;
1271
1272     }
1273
1274     /*
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.
1277      */
1278     if (body != pkt->body)
1279         memmove(pkt->body, body, strlen(body) + 1);
1280     return (0);
1281 }
1282
1283 /*
1284  * Check the ticket in a REQ packet for authenticity
1285  */
1286 static int
1287 check_ticket(
1288     struct krb4_handle *kh,
1289     const pkt_t *       pkt,
1290     const char *        ticket_str,
1291     unsigned long       cksum)
1292 {
1293     char inst[INST_SZ];
1294     KTEXT_ST ticket;
1295     AUTH_DAT auth;
1296     struct passwd *pwd;
1297     char *user;
1298     int rc;
1299
1300     assert(kh != NULL);
1301     assert(pkt != NULL);
1302     assert(ticket_str != NULL);
1303
1304     ticket.length = (int)sizeof(ticket.dat);
1305     astr2bin((unsigned char *)ticket_str, ticket.dat, &ticket.length);
1306     assert(ticket.length > 0);
1307
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);
1311 #else
1312     strncpy(inst, CLIENT_HOST_INSTANCE, SIZEOF(inst) - 1);
1313 #endif
1314     inst[SIZEOF(inst) - 1] = '\0';
1315
1316     /* get the checksum out of the ticket */
1317     rc = krb_rd_req(&ticket, CLIENT_HOST_PRINCIPLE, inst,
1318         kh->peer.sin_addr.s_addr, &auth, CLIENT_HOST_KEY_FILE);
1319     if (rc != 0) {
1320         security_seterror(&kh->sech,
1321             "krb_rd_req failed for %s: %s (%d)", kh->hostname,
1322             error_message(rc), rc);
1323         return (-1);
1324     }
1325
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);
1331         return (-1);
1332     }
1333     kh->cksum = (unsigned long)cksum;
1334     memcpy(kh->session_key, auth.session, SIZEOF(kh->session_key));
1335
1336     /*
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.
1340      */
1341 #ifdef FORCE_USERID
1342     if ((pwd = getpwnam(CLIENT_LOGIN)) == NULL)
1343         error("error [getpwnam(%s) fails]", CLIENT_LOGIN);
1344 #else
1345     if ((pwd = getpwuid(getuid())) == NULL)
1346         error("error  [getpwuid(%d) fails]", getuid());
1347 #endif
1348
1349     /* save the username in case it's overwritten */
1350     user = stralloc(pwd->pw_name);
1351
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);
1357         amfree(user);
1358         return (-1);
1359     }
1360     amfree(user);
1361
1362     /* it's good */
1363     return (0);
1364 }
1365
1366 /*
1367  * Verify that the packet received is secure by verifying that it has
1368  * the same checksum as our request + 1.
1369  */
1370 static int
1371 check_mutual_auth(
1372     struct krb4_handle *kh,
1373     const char *        mutual_auth_str)
1374 {
1375     union mutual mutual;
1376     int len;
1377
1378     assert(kh != NULL);
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);
1383
1384     /* convert the encoded string into binary data */
1385     len = (int)sizeof(mutual);
1386     astr2bin((unsigned char *)mutual_auth_str, (unsigned char *)&mutual, &len);
1387
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);
1392
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);
1398         return (-1);
1399     }
1400
1401     return (0);
1402 }
1403
1404 /*
1405  * Convert a pkt_t into a header string for our packet
1406  */
1407 static const char *
1408 pkthdr2str(
1409     const struct krb4_handle *  kh,
1410     const pkt_t *               pkt)
1411 {
1412     static char retbuf[256];
1413
1414     assert(kh != NULL);
1415     assert(pkt != NULL);
1416
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);
1420
1421     /* check for truncation.  If only we had asprintf()... */
1422     assert(retbuf[strlen(retbuf) - 1] == '\n');
1423
1424     return (retbuf);
1425 }
1426
1427 /*
1428  * Parses out the header line in 'str' into the pkt and handle
1429  * Returns negative on parse error.
1430  */
1431 static int
1432 str2pkthdr(
1433     const char *origstr,
1434     pkt_t *     pkt,
1435     char *      handle,
1436     size_t      handlesize,
1437     int *       sequence)
1438 {
1439     char *str;
1440     const char *tok;
1441
1442     assert(origstr != NULL);
1443     assert(pkt != NULL);
1444
1445     str = stralloc(origstr);
1446
1447     /* "Amanda %d.%d <ACK,NAK,...> HANDLE %s SEQ %d\n" */
1448
1449     /* Read in "Amanda" */
1450     if ((tok = strtok(str, " ")) == NULL || strcmp(tok, "Amanda") != 0)
1451         goto parse_error;
1452
1453     /* nothing is done with the major/minor numbers currently */
1454     if ((tok = strtok(NULL, " ")) == NULL || strchr(tok, '.') == NULL)
1455         goto parse_error;
1456
1457     /* Read in the packet type */
1458     if ((tok = strtok(NULL, " ")) == NULL)
1459         goto parse_error;
1460     pkt_init(pkt, pkt_str2type(tok), "");
1461     if (pkt->type == (pktype_t)-1)
1462         goto parse_error;
1463
1464     /* Read in "HANDLE" */
1465     if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "HANDLE") != 0)
1466         goto parse_error;
1467
1468     /* parse the handle */
1469     if ((tok = strtok(NULL, " ")) == NULL)
1470         goto parse_error;
1471     strncpy(handle, tok, handlesize - 1);
1472     handle[handlesize - 1] = '\0';
1473
1474     /* Read in "SEQ" */
1475     if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "SEQ") != 0)
1476         goto parse_error;
1477
1478     /* parse the sequence number */
1479     if ((tok = strtok(NULL, "\n")) == NULL)
1480         goto parse_error;
1481     *sequence = atoi(tok);
1482
1483     /* Save the body, if any */
1484     if ((tok = strtok(NULL, "")) != NULL)
1485         pkt_cat(pkt, tok);
1486
1487     amfree(str);
1488     return (0);
1489
1490 parse_error:
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);
1494 #endif
1495     amfree(str);
1496     return (-1);
1497 }
1498
1499 static void
1500 host2key(
1501     const char *hostname,
1502     const char *inst,
1503     des_cblock *key)
1504 {
1505     char realm[256];
1506     CREDENTIALS cred;
1507
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
1512 #endif
1513     krb_get_cred(CLIENT_HOST_PRINCIPLE, (char *)inst, realm, &cred);
1514     memcpy(key, cred.session, SIZEOF(des_cblock));
1515 }
1516
1517
1518 /*
1519  * Convert a chunk of data into a string.
1520  */
1521 static const char *
1522 bin2astr(
1523     const unsigned char *buf,
1524     int                 len)
1525 {
1526     static const char tohex[] = "0123456789ABCDEF";
1527     static char *str = NULL;
1528     char *q;
1529     const unsigned char *p;
1530     size_t slen;
1531     int i;
1532
1533     /*
1534      * calculate output string len
1535      * We quote everything, so each input byte == 3 output chars, plus
1536      * two more for quotes
1537      */
1538     slen = ((size_t)len * 3) + 2;
1539
1540     /* allocate string and fill it in */
1541     if (str != NULL)
1542         amfree(str);
1543     str = alloc(slen + 1);
1544     p = buf;
1545     q = str;
1546     *q++ = '"';
1547     for (i = 0; i < len; i++) {
1548         *q++ = '$';
1549         *q++ = tohex[(*p >> 4) & 0xF];
1550         *q++ = tohex[*p & 0xF];
1551         p++;
1552     }
1553     *q++ = '"';
1554     *q = '\0';
1555
1556     /* make sure we didn't overrun our allocated buffer */
1557     assert((size_t)(q - str) == slen);
1558
1559     return (str);
1560 }
1561
1562 /*
1563  * Convert an encoded string into a block of data bytes
1564  */
1565 static void
1566 astr2bin(
1567     const unsigned char *astr,
1568     unsigned char *     buf,
1569     int *               lenp)
1570 {
1571     const unsigned char *p;
1572     unsigned char *q;
1573 #define fromhex(h)      (isdigit((int)h) ? (h) - '0' : (h) - 'A' + 10)
1574
1575     /*
1576      * Skip leading quote, if any
1577      */
1578     if (*astr == '"')
1579         astr++;
1580
1581     /*
1582      * Run through the string.  Anything starting with a $ is a three
1583      * char representation of this byte.  Everything else is literal.
1584      */
1585     for (p = astr, q = buf; *p != '"' && *p != '\0'; ) {
1586         if (*p != '$') {
1587             *q++ = *p++;
1588         } else {
1589             *q++ = (fromhex(p[1]) << 4) + fromhex(p[2]);
1590              p += 3;
1591         }
1592         if ((int)(q - buf) >= *lenp)
1593             break;
1594     }
1595     *lenp = q - buf;
1596 }
1597
1598 static unsigned long
1599 krb4_cksum(
1600     const char *str)
1601 {
1602     des_cblock seed;
1603
1604     memset(seed, 0, SIZEOF(seed));
1605     /*
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.
1609      */
1610     return (quad_cksum((void *)str, NULL, (long)strlen(str), 1, &seed));
1611 }
1612
1613 static void
1614 krb4_getinst(
1615     const char *hname,
1616     char *      inst,
1617     size_t      size)
1618 {
1619
1620     /*
1621      * Copy the first part of the hostname up to the first '.' into
1622      * the buffer provided.  Leave room for a NULL.
1623      */
1624     while (size > 1 && *hname != '\0' && *hname != '.') {
1625         *inst++ = isupper((int)*hname) ? tolower((int)*hname) : *hname;
1626         hname++;
1627         size--;
1628     }
1629     *inst = '\0';
1630 }
1631
1632 static void
1633 encrypt_data(
1634     void *      data,
1635     size_t      length,
1636     des_cblock *key)
1637 {
1638     des_key_schedule sched;
1639
1640     /*
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.
1645      */
1646     des_key_sched((void *)key, sched);
1647     des_pcbc_encrypt(data, data, (long)length, sched, key, DES_ENCRYPT);
1648 }
1649
1650
1651 static void
1652 decrypt_data(
1653     void *      data,
1654     size_t      length,
1655     des_cblock *key)
1656 {
1657     des_key_schedule sched;
1658
1659     des_key_sched((void *)key, sched);
1660     des_pcbc_encrypt(data, data, (long)length, sched, key, DES_DECRYPT);
1661 }
1662
1663 /*
1664  * like write(), but always writes out the entire buffer.
1665  */
1666 static int
1667 net_write(
1668     int         fd,
1669     const void *vbuf,
1670     size_t      size)
1671 {
1672     const char *buf = vbuf;     /* so we can do ptr arith */
1673     ssize_t n;
1674
1675     while (size > 0) {
1676         n = write(fd, buf, size);
1677         if (n < 0)
1678             return (-1);
1679         buf += n;
1680         size -= n;
1681     }
1682     return (0);
1683 }
1684
1685 /*
1686  * Like read(), but waits until the entire buffer has been filled.
1687  */
1688 static int
1689 net_read(
1690     int         fd,
1691     void *      vbuf,
1692     size_t      size,
1693     int         timeout)
1694 {
1695     char *buf = vbuf;   /* ptr arith */
1696     ssize_t n;
1697     int neof = 0;
1698     fd_set readfds;
1699     struct timeval tv;
1700
1701     while (size > 0) {
1702         FD_ZERO(&readfds);
1703         FD_SET(fd, &readfds);
1704         tv.tv_sec = timeout;
1705         tv.tv_usec = 0;
1706         switch (select(fd + 1, &readfds, NULL, NULL, &tv)) {
1707         case 0:
1708             errno = ETIMEDOUT;
1709             /* FALLTHROUGH */
1710         case -1:
1711             return (-1);
1712         case 1:
1713             assert(FD_ISSET(fd, &readfds));
1714             break;
1715         default:
1716             assert(0);
1717             break;
1718         }
1719         n = read(fd, buf, size);
1720         if (n < 0)
1721             return (-1);
1722         /* we only tolerate so many eof's */
1723         if (n == 0 && ++neof > 1024) {
1724             errno = EIO;
1725             return (-1);
1726         }
1727         buf += n;
1728         size -= n;
1729     }
1730     return (0);
1731 }
1732
1733 #if 0
1734 /* -------------------------- */
1735 /* debug routines */
1736
1737 static void
1738 print_hex(
1739     const char *                str,
1740     const unsigned char *       buf,
1741     size_t                      len)
1742 {
1743     int i;
1744
1745     dbprintf(("%s:", str));
1746     for(i=0;i<len;i++) {
1747         if(i%25 == 0) dbprintf(("\n"));
1748         dbprintf((" %02X", buf[i]));
1749     }
1750     dbprintf(("\n"));
1751 }
1752
1753 static void
1754 print_ticket(
1755     const char *str,
1756     KTEXT       tktp)
1757 {
1758     dbprintf(("%s: length %d chk %lX\n", str, tktp->length, tktp->mbz));
1759     print_hex("ticket data", tktp->dat, tktp->length);
1760     fflush(stdout);
1761 }
1762
1763 static void
1764 print_auth(
1765     AUTH_DAT *authp)
1766 {
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));
1773     fflush(stdout);
1774 }
1775
1776 static void
1777 print_credentials(
1778     CREDENTIALS *credp)
1779 {
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,
1783            credp->kvno);
1784     print_hex("session key", credp->session, SIZEOF(credp->session));
1785     print_hex("ticket", credp->ticket_st.dat, credp->ticket_st.length);
1786     fflush(stdout);
1787 }
1788 #endif
1789
1790 #else
1791
1792 void krb4_security_dummy(void);
1793
1794 void
1795 krb4_security_dummy(void)
1796 {
1797 }
1798
1799 #endif /* KRB4_SECURITY */