Imported Upstream version 2.5.2p1
[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_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 */
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     NULL,
160     NULL
161 };
162
163 /*
164  * Cache the local hostname
165  */
166 static char hostname[MAX_HOSTNAME_LENGTH+1];
167
168 /*
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
171  * here.
172  */
173 static dgram_t netfd;
174
175 /*
176  * This is a queue of outstanding async requests
177  */
178 static struct {
179     TAILQ_HEAD(, krb4_handle) tailq;
180     int qlength;
181 } handleq = {
182     TAILQ_HEAD_INITIALIZER(handleq.tailq), 0
183 };
184
185 /*
186  * Macros to add or remove krb4_handles from the above queue
187  */
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);  \
191     handleq.qlength++;                          \
192 } while (0)
193
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);               \
198     handleq.qlength--;                                  \
199     assert((handleq.qlength == 0) ^ (TAILQ_FIRST(&handleq.tailq) != NULL)); \
200 } while (0)
201
202 #define handleq_first()         TAILQ_FIRST(&handleq.tailq)
203 #define handleq_next(kh)        TAILQ_NEXT(kh, tq)
204
205
206 /*
207  * This is the event manager's handle for our netfd
208  */
209 static event_handle_t *ev_netfd;
210
211 /*
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
215  */
216 static void (*accept_fn)(security_handle_t *, pkt_t *);
217
218
219 /*
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.
224  */
225 union mutual {
226     char pad[8];
227     long cksum;
228 };
229
230 /*
231  * Private functions
232  */
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,
238     const char *);
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);
248
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 *);
254
255 static const char *pkthdr2str(const struct krb4_handle *, const pkt_t *);
256 static int str2pkthdr(const char *, pkt_t *, char *, size_t, int *);
257
258 static const char *bin2astr(const unsigned char *, int);
259 static void astr2bin(const unsigned char *, unsigned char *, int *);
260
261 static void encrypt_data(void *, size_t, des_cblock *);
262 static void decrypt_data(void *, size_t, des_cblock *);
263
264 #define HOSTNAME_INSTANCE inst
265
266 static char *ticketfilename = NULL;
267
268 static void
269 killtickets(void)
270 {
271     if (ticketfilename != NULL)
272         unlink(ticketfilename);
273     amfree(ticketfilename);
274 }
275
276 /*
277  * Setup some things about krb4.  This should only be called once.
278  */
279 static void
280 init(void)
281 {
282     char tktfile[256];
283     int port;
284     static int beenhere = 0;
285
286     if (beenhere)
287         return;
288     beenhere = 1;
289
290     gethostname(hostname, SIZEOF(hostname) - 1);
291     hostname[SIZEOF(hostname) - 1] = '\0';
292
293     if (atexit(killtickets) < 0)
294         error("could not setup krb4 exit handler: %s", strerror(errno));
295
296     /*
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
300      * hanging around.
301      */
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)
308     {
309         char *tkt_env = stralloc2("KRBTKFILE=", ticketfilename);
310         putenv(tkt_env);
311     }
312 #else
313     setenv("KRBTKFILE", ticketfile, 1);
314 #endif
315
316     dgram_zero(&netfd);
317     dgram_bind(&netfd, &port);
318 }
319
320 /*
321  * Get a ticket granting ticket and stuff it in the cache
322  */
323 static void
324 get_tgt(void)
325 {
326     char realm[REALM_SZ];
327     int rc;
328
329     strncpy(realm, krb_realmofhost(hostname), SIZEOF(realm) - 1);
330     realm[SIZEOF(realm) - 1] = '\0';
331
332     rc = krb_get_svc_in_tkt(SERVER_HOST_PRINCIPLE, SERVER_HOST_INSTANCE,
333         realm, "krbtgt", realm, TICKET_LIFETIME, SERVER_HOST_KEY_FILE);
334
335     if (rc != 0) {
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]);
339     }
340
341     krb_set_lifetime(TICKET_LIFETIME);
342 }
343
344
345 /*
346  * krb4 version of a security handle allocator.  Logically sets
347  * up a network "connection".
348  */
349 static void
350 krb4_connect(
351     const char *hostname,
352     char *      (*conf_fn)(char *, void *),
353     void        (*fn)(void *, security_handle_t *, security_status_t),
354     void *      arg,
355     void *      datap)
356 {
357     struct krb4_handle *kh;
358     char handle[32];
359     struct servent *se;
360     struct hostent *he;
361     int port;
362
363     assert(hostname != NULL);
364
365     /*
366      * Make sure we're initted
367      */
368     init();
369
370     kh = alloc(SIZEOF(*kh));
371     security_handleinit(&kh->sech, &krb4_security_driver);
372
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);
377         return;
378     }
379     if ((se = getservbyname(KAMANDA_SERVICE_NAME, "udp")) == NULL)
380         port = (int)KAMANDA_SERVICE_DEFAULT;
381     else
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);
386 }
387
388 /*
389  * Setup to handle new incoming connections
390  */
391 static void
392 krb4_accept(
393     const struct security_driver *driver,
394     int         in,
395     int         out,
396     void        (*fn)(security_handle_t *, pkt_t *))
397 {
398
399     /*
400      * Make sure we're initted
401      */
402     init();
403
404     /*
405      * We assume that in and out both point to the same socket
406      */
407     dgram_socket(&netfd, in);
408
409     /*
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
413      */
414     accept_fn = fn;
415
416     if (ev_netfd == NULL)
417         ev_netfd = event_register((event_id_t)netfd.socket, EV_READFD,
418                             recvpkt_callback, NULL);
419 }
420
421 /*
422  * Given a hostname and a port, setup a krb4_handle
423  */
424 static void
425 inithandle(
426     struct krb4_handle *kh,
427     struct hostent *    he,
428     int                 port,
429     const char *        handle)
430 {
431
432     /*
433      * Get the instance and realm for this host
434      * (krb_realmofhost always returns something)
435      */
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';
439
440     /*
441      * Save a copy of the hostname
442      */
443     strncpy(kh->hostname, he->h_name, SIZEOF(kh->hostname) - 1);
444     kh->hostname[SIZEOF(kh->hostname) - 1] = '\0';
445
446     /*
447      * We have no checksum or session key at this point
448      */
449     kh->cksum = 0;
450     memset(kh->session_key, 0, SIZEOF(kh->session_key));
451
452     /*
453      * Setup our peer info.  We don't do anything with the sequence yet,
454      * so just leave it at 0.
455      */
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';
461     kh->sequence = 0;
462     kh->fn = NULL;
463     kh->arg = NULL;
464     kh->ev_timeout = NULL;
465 }
466
467 /*
468  * frees a handle allocated by the above
469  */
470 static void
471 krb4_close(
472     void *      inst)
473 {
474
475     krb4_recvpkt_cancel(inst);
476     amfree(inst);
477 }
478
479 /*
480  * Transmit a packet.  Add security information first.
481  */
482 static ssize_t
483 krb4_sendpkt(
484     void *      cookie,
485     pkt_t *     pkt)
486 {
487     struct krb4_handle *kh = cookie;
488
489     assert(kh != NULL);
490     assert(pkt != NULL);
491
492     /*
493      * Initialize this datagram
494      */
495     dgram_zero(&netfd);
496
497     /*
498      * Add the header to the packet
499      */
500     dgram_cat(&netfd, pkthdr2str(kh, pkt));
501
502     /*
503      * Add the security info.  This depends on which kind of packet we're
504      * sending.
505      */
506     switch (pkt->type) {
507     case P_REQ:
508         /*
509          * Requests get sent with a ticket embedded in the header.  The
510          * checksum is generated from the contents of the body.
511          */
512         if (add_ticket(kh, pkt, &netfd) < 0)
513             return (-1);
514         break;
515     case P_REP:
516         /*
517          * Replies get sent with a mutual authenticator added.  The
518          * mutual authenticator is the encrypted checksum from the
519          * ticket + 1
520          */
521         add_mutual_auth(kh, &netfd);
522         break;
523     case P_ACK:
524     case P_NAK:
525     default:
526         /*
527          * The other types have no security stuff added for krb4.
528          * Shamefull.
529          */
530         break;
531     }
532
533     /*
534      * Add the body, and send it
535      */
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));
541         return (-1);
542     }
543     return (0);
544 }
545
546 /*
547  * Set up to receive a packet asyncronously, and call back when
548  * it has been read.
549  */
550 static void
551 krb4_recvpkt(
552     void *      cookie,
553     void        (*fn)(void *, pkt_t *, security_status_t),
554     void *      arg,
555     int         timeout)
556 {
557     struct krb4_handle *kh = cookie;
558
559     assert(netfd.socket >= 0);
560     assert(kh != NULL);
561
562     /*
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.
567      */
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);
572     }
573
574     /*
575      * Multiple recvpkt calls override previous ones
576      * If kh->fn is NULL then it is not in the queue.
577      */
578     if (kh->fn == NULL)
579         handleq_add(kh);
580     if (kh->ev_timeout != NULL)
581         event_release(kh->ev_timeout);
582     if (timeout < 0)
583         kh->ev_timeout = NULL;
584     else
585         kh->ev_timeout = event_register((event_id_t)timeout, EV_TIME,
586                                 recvpkt_timeout, kh);
587     kh->fn = fn;
588     kh->arg = arg;
589 }
590
591 /*
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.
595  */
596 static void
597 krb4_recvpkt_cancel(
598     void *      cookie)
599 {
600     struct krb4_handle *kh = cookie;
601
602     assert(kh != NULL);
603
604     if (kh->fn != NULL) {
605         handleq_remove(kh);
606         kh->fn = NULL;
607         kh->arg = NULL;
608     }
609     if (kh->ev_timeout != NULL)
610         event_release(kh->ev_timeout);
611     kh->ev_timeout = NULL;
612
613     if (handleq.qlength == 0 && accept_fn == NULL &&
614         ev_netfd != NULL) {
615         event_release(ev_netfd);
616         ev_netfd = NULL;
617     }
618 }
619
620 /*
621  * Create the server end of a stream.  For krb4, this means setup a tcp
622  * socket for receiving a connection.
623  */
624 static void *
625 krb4_stream_server(
626     void *      h)
627 {
628     struct krb4_handle *kh = h;
629     struct krb4_stream *ks;
630
631     assert(kh != NULL);
632
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));
639         amfree(ks);
640         return (NULL);
641     }
642     ks->fd = -1;
643     ks->krb4_handle = kh;
644     ks->ev_read = NULL;
645     return (ks);
646 }
647
648 /*
649  * Accept an incoming connection on a stream_server socket
650  */
651 static int
652 krb4_stream_accept(
653     void *      s)
654 {
655     struct krb4_stream *ks = s;
656     struct krb4_handle *kh;
657
658     assert(ks != NULL);
659     kh = ks->krb4_handle;
660     assert(kh != NULL);
661     assert(ks->socket >= 0);
662     assert(ks->fd == -1);
663
664     ks->fd = stream_accept(ks->socket, 30, STREAM_BUFSIZE, STREAM_BUFSIZE);
665     if (ks->fd < 0) {
666         security_stream_seterror(&ks->secstr,
667             "can't accept new stream connection: %s", strerror(errno));
668         return (-1);
669     }
670     return (0);
671 }
672
673 /*
674  * Return a connected stream.
675  */
676 static void *
677 krb4_stream_client(
678     void *      h,
679     int         id)
680 {
681     struct krb4_handle *kh = h;
682     struct krb4_stream *ks;
683
684     assert(kh != NULL);
685
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,
689         &ks->port, 0);
690     if (ks->fd < 0) {
691         security_seterror(&kh->sech,
692             "can't connect stream to %s port %d: %s", kh->hostname, id,
693             strerror(errno));
694         amfree(ks);
695         return (NULL);
696     }
697
698     ks->socket = -1;    /* we're a client */
699     ks->krb4_handle = kh;
700     ks->ev_read = NULL;
701     return (ks);
702 }
703
704 /*
705  * Close and unallocate resources for a stream.
706  */
707 static void
708 krb4_stream_close(
709     void *      s)
710 {
711     struct krb4_stream *ks = s;
712
713     assert(ks != NULL);
714
715     if (ks->fd != -1)
716         aclose(ks->fd);
717     if (ks->socket != -1)
718         aclose(ks->socket);
719     krb4_stream_read_cancel(ks);
720     amfree(ks);
721 }
722
723 /*
724  * Authenticate a stream
725  *
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.
729  */
730 static int
731 krb4_stream_auth(
732     void *      s)
733 {
734     struct krb4_stream *ks = s;
735     struct krb4_handle *kh;
736     int fd;
737     struct timeval local, enc;
738     struct timezone tz;
739
740     assert(ks != NULL);
741     assert(ks->fd >= 0);
742
743     fd = ks->fd;
744     kh = ks->krb4_handle;
745
746     /* make sure we're open */
747     assert(fd >= 0);
748
749     /* make sure our timeval is what we're expecting, see above */
750     assert(SIZEOF(struct timeval) == 8);
751
752     /*
753      * Get the current time, put it in network byte order, encrypt it
754      * and present it to the other side.
755      */
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));
763         return (-1);
764     }
765
766     /*
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.
770      */
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));
774         return (-1);
775     }
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);
781
782     if (net_write(fd, &enc, SIZEOF(enc)) < 0) {
783         security_stream_seterror(&ks->secstr,
784             "krb4 stream handshake write error: %s", strerror(errno));
785         return (-1);
786     }
787
788     /*
789      * Read back the other side's processing of our data.
790      * If they incremented it properly, then succeed.
791      * Timeout in 10 seconds.
792      */
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));
796         return (-1);
797     }
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)))
801             return (0);
802
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));
808     return (-1);
809 }
810
811 /*
812  * Returns the stream id for this stream.  This is just the local
813  * port.
814  */
815 static int
816 krb4_stream_id(
817     void *      s)
818 {
819     struct krb4_stream *ks = s;
820
821     assert(ks != NULL);
822
823     return (ks->port);
824 }
825
826 /*
827  * Write a chunk of data to a stream.  Blocks until completion.
828  */
829 static int
830 krb4_stream_write(
831     void *      s,
832     const void *buf,
833     size_t      size)
834 {
835     struct krb4_stream *ks = s;
836     struct krb4_handle *kh = ks->krb4_handle;
837
838     assert(ks != NULL);
839     assert(kh != NULL);
840
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));
844         return (-1);
845     }
846     return (0);
847 }
848
849 /*
850  * Submit a request to read some data.  Calls back with the given
851  * function and arg when completed.
852  */
853 static void
854 krb4_stream_read(
855     void *      s,
856     void        (*fn)(void *, void *, ssize_t),
857     void *      arg)
858 {
859     struct krb4_stream *ks = s;
860
861     assert(ks != NULL);
862
863     /*
864      * Only one read request can be active per stream.
865      */
866     if (ks->ev_read != NULL)
867         event_release(ks->ev_read);
868
869     ks->ev_read = event_register((event_id_t)ks->fd, EV_READFD,
870                         stream_read_callback, ks);
871     ks->fn = fn;
872     ks->arg = arg;
873 }
874
875 /*
876  * Write a chunk of data to a stream.  Blocks until completion.
877  */
878 static ssize_t
879 krb4_stream_read_sync(
880     void *      s,
881     void **     buf)
882 {
883     struct krb4_stream *ks = s;
884
885     (void)buf;  /* Quiet unused variable warning */
886     assert(ks != NULL);
887
888     if (ks->ev_read != NULL)
889         event_release(ks->ev_read);
890
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);
895 }
896
897 /*
898  * Callback for krb4_stream_read_sync
899  */
900 static void
901 stream_read_sync_callback(
902     void *      arg)
903 {
904     struct krb4_stream *ks = arg;
905     ssize_t n;
906
907     assert(ks != NULL);
908     assert(ks->fd != -1);
909
910     /*
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.
914      */
915     krb4_stream_read_cancel(ks);
916     n = read(ks->fd, ks->databuf, sizeof(ks->databuf));
917     if (n < 0)
918         security_stream_seterror(&ks->secstr,
919             strerror(errno));
920     ks->len = (int)n;
921 }
922
923 /*
924  * Cancel a previous stream read request.  It's ok if we didn't have a read
925  * scheduled.
926  */
927 static void
928 krb4_stream_read_cancel(
929     void *      s)
930 {
931     struct krb4_stream *ks = s;
932
933     assert(ks != NULL);
934
935     if (ks->ev_read != NULL) {
936         event_release(ks->ev_read);
937         ks->ev_read = NULL;
938     }
939 }
940
941 /*
942  * Callback for krb4_stream_read
943  */
944 static void
945 stream_read_callback(
946     void *      arg)
947 {
948     struct krb4_stream *ks = arg;
949     ssize_t n;
950
951     assert(ks != NULL);
952     assert(ks->fd != -1);
953
954     /*
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.
958      */
959     krb4_stream_read_cancel(ks);
960     n = read(ks->fd, ks->databuf, SIZEOF(ks->databuf));
961     if (n < 0)
962         security_stream_seterror(&ks->secstr,
963             strerror(errno));
964     (*ks->fn)(ks->arg, ks->databuf, n);
965 }
966
967 /*
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.
971  */
972 static void
973 recvpkt_callback(
974     void *      cookie)
975 {
976     char handle[32];
977     struct sockaddr_in6 peer;
978     pkt_t pkt;
979     int sequence;
980     struct krb4_handle *kh;
981     struct hostent *he;
982     void (*fn)(void *, pkt_t *, security_status_t);
983     void *arg;
984
985     assert(cookie == NULL);
986
987     /*
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.
991      */
992     dgram_zero(&netfd);
993     if (dgram_recv(&netfd, 0, &peer) < 0)
994         return;
995     if (str2pkthdr(netfd.cur, &pkt, handle, SIZEOF(handle), &sequence) < 0)
996         return;
997
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;
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.sin6_addr, SIZEOF(peer.sin6_addr), AF_INET6);
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.sin6_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.sin6_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     SELECT_ARG_TYPE 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 */