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