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