e1e78a0ca0138748c28a1930ebda8e5d0f823550
[debian/amanda] / common-src / krb5-security.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 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: krb5-security.c,v 1.12 2006/02/21 04:13:55 ktill Exp $
29  *
30  * krb5-security.c - kerberos V5 security module
31  */
32
33 #include "config.h"
34 #ifdef KRB5_SECURITY
35 #include "amanda.h"
36 #include "arglist.h"
37 #include "event.h"
38 #include "packet.h"
39 #include "queue.h"
40 #include "security.h"
41 #include "stream.h"
42 #include "version.h"
43
44 #define BROKEN_MEMORY_CCACHE
45
46 #ifdef BROKEN_MEMORY_CCACHE
47 /*
48  * If you don't have atexit() or on_exit(), you could just consider
49  * making atexit() empty and clean up your ticket files some other
50  * way
51  */
52 #ifndef HAVE_ATEXIT
53 #ifdef HAVE_ON_EXIT
54 #define atexit(func)    on_exit(func, 0)
55 #else
56 #define atexit(func)    (you must to resolve lack of atexit)
57 #endif  /* HAVE_ON_EXIT */
58 #endif  /* ! HAVE_ATEXIT */
59 #endif
60
61 #ifndef KRB5_HEIMDAL_INCLUDES
62 #include <gssapi/gssapi_generic.h>
63 #else
64 #include <gssapi/gssapi.h>
65 #endif
66 #include <krb5.h>
67
68 #ifndef KRB5_ENV_CCNAME
69 #define KRB5_ENV_CCNAME "KRB5CCNAME"
70 #endif
71
72 /*#define       KRB5_DEBUG*/
73
74 #ifdef KRB5_DEBUG
75 #define k5printf(x)     dbprintf(x)
76 #else
77 #define k5printf(x)
78 #endif
79
80 /*
81  * consider undefining when kdestroy() is fixed.  The current version does
82  * not work under krb5-1.2.4 in rh7.3, perhaps others.
83  */
84 #define KDESTROY_VIA_UNLINK     1
85
86 /*
87  * Define this if you want all network traffic encrypted.  This will
88  * extract a serious performance hit.
89  *
90  * It would be nice if we could do this on a filesystem-by-filesystem basis.
91  */
92 /*#define       AMANDA_KRB5_ENCRYPT*/
93
94 /*
95  * Where the keytab lives, if defined.  Otherwise it expects something in the
96  * config file.
97  */
98 /* #define AMANDA_KEYTAB        "/.amanda-v5-keytab" */
99
100 /*
101  * The name of the principal we authenticate with, if defined.  Otherwise
102  * it expects something in the config file.
103  */
104 /* #define      AMANDA_PRINCIPAL        "service/amanda" */
105
106 /*
107  * The lifetime of our tickets in seconds.  This may or may not need to be
108  * configurable.
109  */
110 #define AMANDA_TKT_LIFETIME     (12*60*60)
111
112 /*
113  * The name of the service in /etc/services.  This probably shouldn't be
114  * configurable.
115  */
116 #define AMANDA_KRB5_SERVICE_NAME        "k5amanda"
117
118 /*
119  * The default port to use if above entry in /etc/services doesn't exist
120  */
121 #define AMANDA_KRB5_DEFAULT_PORT        10082
122
123 /*
124  * The timeout in seconds for each step of the GSS negotiation phase
125  */
126 #define GSS_TIMEOUT                     30
127
128 /*
129  * The largest buffer we can send/receive.
130  */
131 #define AMANDA_MAX_TOK_SIZE             (MAX_TAPE_BLOCK_BYTES * 4)
132
133 /*
134  * Magic values for krb5_conn->handle
135  */
136 #define H_EOF   -1              /* this connection has been shut down */
137
138 /*
139  * This is the tcp stream buffer size
140  */
141 #define KRB5_STREAM_BUFSIZE     (MAX_TAPE_BLOCK_BYTES * 2)
142
143 /*
144  * This is the max number of outgoing connections we can have at once.
145  * planner/amcheck/etc will open a bunch of connections as it tries
146  * to contact everything.  We need to limit this to avoid blowing
147  * the max number of open file descriptors a process can have.
148  */
149 #define AMANDA_KRB5_MAXCONN     40
150
151 /*
152  * This is a frame read off of the connection.  Each frame has an
153  * associated handle and a gss_buffer which contains a len,value pair.
154  */
155 struct krb5_frame {
156     int handle;                         /* proto handle */
157     gss_buffer_desc tok;                /* token */
158     TAILQ_ENTRY(krb5_frame) tq;         /* queue handle */
159 };
160
161 /*
162  * This is a krb5 connection to a host.  We should only have
163  * one connection per host.
164  */
165 struct krb5_conn {
166     int fd;                                     /* tcp connection */
167     struct {                                    /* buffer read() calls */
168         char buf[KRB5_STREAM_BUFSIZE];          /* buffer */
169         size_t left;                    /* unread data */
170         ssize_t size;                   /* size of last read */
171     } readbuf;
172     enum { unauthed, authed } state;
173     event_handle_t *ev_read;                    /* read (EV_READFD) handle */
174     int ev_read_refcnt;                         /* number of readers */
175     char hostname[MAX_HOSTNAME_LENGTH+1];       /* human form of above */
176     char *errmsg;                               /* error passed up */
177     gss_ctx_id_t gss_context;                   /* GSSAPI context */
178     int refcnt;                                 /* number of handles using */
179     TAILQ_HEAD(, krb5_frame) frameq;            /* queue of read frames */
180     TAILQ_ENTRY(krb5_conn) tq;                  /* queue handle */
181 };
182
183
184 struct krb5_stream;
185
186 /*
187  * This is the private handle data.
188  */
189 struct krb5_handle {
190     security_handle_t sech;             /* MUST be first */
191     char *hostname;                     /* ptr to kc->hostname */
192     struct krb5_stream *ks;             /* virtual stream we xmit over */
193
194     union {
195         void (*recvpkt) P((void *, pkt_t *, security_status_t));
196                                         /* func to call when packet recvd */
197         void (*connect) P((void *, security_handle_t *, security_status_t));
198                                         /* func to call when connected */
199     } fn;
200     void *arg;                          /* argument to pass function */
201     event_handle_t *ev_wait;            /* wait handle for connects */
202     char *(*conf_fn) P((char *, void *)); /* used to get config info */
203     event_handle_t *ev_timeout;         /* timeout handle for recv */
204 };
205
206 /*
207  * This is the internal security_stream data for krb5.
208  */
209 struct krb5_stream {
210     security_stream_t secstr;           /* MUST be first */
211     struct krb5_conn *kc;               /* physical connection */
212     int handle;                         /* protocol handle */
213     event_handle_t *ev_read;            /* read (EV_WAIT) event handle */
214     void (*fn) P((void *, void *, int));        /* read event fn */
215     void *arg;                          /* arg for previous */
216 };
217
218 /*
219  * Interface functions
220  */
221 static int krb5_sendpkt P((void *, pkt_t *));
222 static int krb5_stream_accept P((void *));
223 static int krb5_stream_auth P((void *));
224 static int krb5_stream_id P((void *));
225 static int krb5_stream_write P((void *, const void *, size_t));
226 static void *krb5_stream_client P((void *, int));
227 static void *krb5_stream_server P((void *));
228 static void krb5_accept P((int, int,
229     void (*)(security_handle_t *, pkt_t *)));
230 static void krb5_close P((void *));
231 static void krb5_connect P((const char *,
232     char *(*)(char *, void *),
233     void (*)(void *, security_handle_t *, security_status_t), void *));
234 static void krb5_recvpkt P((void *,
235     void (*)(void *, pkt_t *, security_status_t), void *, int));
236 static void krb5_recvpkt_cancel P((void *));
237 static void krb5_stream_close P((void *));
238 static void krb5_stream_read P((void *, void (*)(void *, void *, int),
239     void *));
240 static void krb5_stream_read_cancel P((void *));
241
242 /*
243  * This is our interface to the outside world.
244  */
245 const security_driver_t krb5_security_driver = {
246     "krb5",
247     krb5_connect,
248     krb5_accept,
249     krb5_close,
250     krb5_sendpkt,
251     krb5_recvpkt,
252     krb5_recvpkt_cancel,
253     krb5_stream_server,
254     krb5_stream_accept,
255     krb5_stream_client,
256     krb5_stream_close,
257     krb5_stream_auth,
258     krb5_stream_id,
259     krb5_stream_write,
260     krb5_stream_read,
261     krb5_stream_read_cancel,
262 };
263
264 /*
265  * Cache the local hostname
266  */
267 static char hostname[MAX_HOSTNAME_LENGTH+1];
268
269 /*
270  * This is a queue of open connections
271  */
272 static struct {
273     TAILQ_HEAD(, krb5_conn) tailq;
274     int qlength;
275 } connq = {
276     TAILQ_HEAD_INITIALIZER(connq.tailq), 0
277 };
278 #define connq_first()           TAILQ_FIRST(&connq.tailq)
279 #define connq_next(kc)          TAILQ_NEXT(kc, tq)
280 #define connq_append(kc)        do {                                    \
281     TAILQ_INSERT_TAIL(&connq.tailq, kc, tq);                            \
282     connq.qlength++;                                                    \
283 } while (0)
284 #define connq_remove(kc)        do {                                    \
285     assert(connq.qlength > 0);                                          \
286     TAILQ_REMOVE(&connq.tailq, kc, tq);                                 \
287     connq.qlength--;                                                    \
288 } while (0)
289
290 static int newhandle = 1;
291
292 /*
293  * This is a function that should be called if a new security_handle_t is
294  * created.  If NULL, no new handles are created.
295  * It is passed the new handle and the received pkt
296  */
297 static void (*accept_fn) P((security_handle_t *, pkt_t *));
298
299 /*
300  * Local functions
301  */
302 static void init P((void));
303 #ifdef BROKEN_MEMORY_CCACHE
304 static void cleanup P((void));
305 #endif
306 static const char *get_tgt P((char *, char *));
307 static void open_callback P((void *));
308 static void connect_callback P((void *));
309 static void connect_timeout P((void *));
310 static int send_token P((struct krb5_conn *, int, const gss_buffer_desc *));
311 static int recv_token P((struct krb5_conn *, int *, gss_buffer_desc *, int));
312 static void recvpkt_callback P((void *, void *, ssize_t));
313 static void recvpkt_timeout P((void *));
314 static void stream_read_callback P((void *));
315 static int gss_server P((struct krb5_conn *));
316 static int gss_client P((struct krb5_handle *));
317 static const char *gss_error P((OM_uint32, OM_uint32));
318
319 #ifdef AMANDA_KRB5_ENCRYPT
320 static int kdecrypt P((struct krb5_stream *, gss_buffer_desc *,
321     gss_buffer_desc *));
322 static int kencrypt P((struct krb5_stream *, gss_buffer_desc *,
323     gss_buffer_desc *));
324 #endif
325 static struct krb5_conn *conn_get P((const char *));
326 static void conn_put P((struct krb5_conn *));
327 static void conn_read P((struct krb5_conn *));
328 static void conn_read_cancel P((struct krb5_conn *));
329 static void conn_read_callback P((void *));
330 static int conn_run_frameq P((struct krb5_conn *, struct krb5_stream *));
331 static int net_writev P((int, struct iovec *, int));
332 static ssize_t net_read P((struct krb5_conn *, void *, size_t, int));
333 static int net_read_fillbuf P((struct krb5_conn *, int));
334 static char *krb5_checkuser(char *, char *, char *);
335 static void parse_pkt P((pkt_t *, const void *, size_t));
336
337
338 /*
339  * krb5 version of a security handle allocator.  Logically sets
340  * up a network "connection".
341  */
342 static void
343 krb5_connect(hostname, conf_fn, fn, arg)
344     const char *hostname;
345     char *(*conf_fn) P((char *, void *));
346     void (*fn) P((void *, security_handle_t *, security_status_t));
347     void *arg;
348 {
349     struct krb5_handle *kh;
350     struct hostent *he;
351     struct servent *se;
352     int port, fd;
353     const char *err;
354     char *keytab_name = NULL;
355     char *principal_name = NULL;
356
357     assert(hostname != NULL);
358
359     k5printf(("krb5_connect: %s\n", hostname));
360
361     /*
362      * Make sure we're initted
363      */
364     init();
365
366     kh = alloc(sizeof(*kh));
367     security_handleinit(&kh->sech, &krb5_security_driver);
368     kh->hostname = NULL;
369     kh->ks = NULL;
370     kh->ev_wait = NULL;
371     kh->ev_timeout = NULL;
372
373 #ifdef AMANDA_KEYTAB
374     keytab_name = AMANDA_KEYTAB;
375 #else
376     if(conf_fn) {
377         keytab_name = conf_fn("krb5keytab", arg);
378     }
379 #endif
380 #ifdef AMANDA_PRINCIPAL
381     principal_name = AMANDA_PRINCIPAL;
382 #else
383     if(conf_fn) {
384         principal_name = conf_fn("krb5principal", arg);
385     }
386 #endif
387
388     if ((err = get_tgt(keytab_name, principal_name)) != NULL) {
389         security_seterror(&kh->sech, "%s: could not get TGT: %s",
390             hostname, err);
391         (*fn)(arg, &kh->sech, S_ERROR);
392         return;
393     }
394
395     if ((he = gethostbyname(hostname)) == NULL) {
396         security_seterror(&kh->sech,
397             "%s: could not resolve hostname", hostname);
398         (*fn)(arg, &kh->sech, S_ERROR);
399         return;
400     }
401     kh->fn.connect = fn;
402     kh->conf_fn = conf_fn;
403     kh->arg = arg;
404     kh->hostname = stralloc(he->h_name);
405     kh->ks = krb5_stream_client(kh, newhandle++);
406
407     if (kh->ks == NULL)
408         goto error;
409
410     fd = kh->ks->kc->fd;
411
412     if (fd < 0) {
413         /*
414          * We need to open a new connection.  See if we have too
415          * many connections open.
416          */
417         if (connq.qlength > AMANDA_KRB5_MAXCONN) {
418             k5printf(("krb5_connect: too many conections (%d), delaying %s\n",
419                 connq.qlength, kh->hostname));
420             krb5_stream_close(kh->ks);
421             kh->ev_wait = event_register((event_id_t)open_callback,
422                 EV_WAIT, open_callback, kh);
423             return;
424         }
425
426         if ((se = getservbyname(AMANDA_KRB5_SERVICE_NAME, "tcp")) == NULL)
427             port = htons(AMANDA_KRB5_DEFAULT_PORT);
428         else
429             port = se->s_port;
430
431         /*
432          * Get a non-blocking socket.
433          */
434         fd = stream_client(kh->hostname, ntohs(port), KRB5_STREAM_BUFSIZE,
435             KRB5_STREAM_BUFSIZE, NULL, 1);
436         if (fd < 0) {
437             security_seterror(&kh->sech,
438                 "can't connect to %s:%d: %s", hostname, ntohs(port),
439                 strerror(errno));
440             goto error;
441         }
442         kh->ks->kc->fd = fd;
443     }
444     /*
445      * The socket will be opened async so hosts that are down won't
446      * block everything.  We need to register a write event
447      * so we will know when the socket comes alive.
448      * We also register a timeout.
449      */
450     kh->ev_wait = event_register(fd, EV_WRITEFD, connect_callback, kh);
451     kh->ev_timeout = event_register(GSS_TIMEOUT, EV_TIME, connect_timeout, kh);
452
453     return;
454
455 error:
456     (*fn)(arg, &kh->sech, S_ERROR);
457 }
458
459 /*
460  * Called when there are not too many connections open such that
461  * we can open more.
462  */
463 static void
464 open_callback(cookie)
465     void *cookie;
466 {
467     struct krb5_handle *kh = cookie;
468
469     event_release(kh->ev_wait);
470
471     k5printf(("krb5: open_callback: possible connections available, retry %s\n",
472         kh->hostname));
473     krb5_connect(kh->hostname, kh->conf_fn, kh->fn.connect, kh->arg);
474     amfree(kh->hostname);
475     amfree(kh);
476 }
477
478 /*
479  * Called when a tcp connection is finished connecting and is ready
480  * to be authenticated.
481  */
482 static void
483 connect_callback(cookie)
484     void *cookie;
485 {
486     struct krb5_handle *kh = cookie;
487
488     event_release(kh->ev_wait);
489     kh->ev_wait = NULL;
490     event_release(kh->ev_timeout);
491     kh->ev_timeout = NULL;
492
493     if (kh->ks->kc->state == unauthed) {
494         if (gss_client(kh) < 0) {
495             (*kh->fn.connect)(kh->arg, &kh->sech, S_ERROR);
496             return;
497         }
498         kh->ks->kc->state = authed;
499     }
500     assert(kh->ks->kc->gss_context != GSS_C_NO_CONTEXT);
501
502     (*kh->fn.connect)(kh->arg, &kh->sech, S_OK);
503 }
504
505 /*
506  * Called if a connection times out before completion.
507  */
508 static void
509 connect_timeout(cookie)
510     void *cookie;
511 {
512     struct krb5_handle *kh = cookie;
513
514     event_release(kh->ev_wait);
515     kh->ev_wait = NULL;
516     event_release(kh->ev_timeout);
517     kh->ev_timeout = NULL;
518
519     (*kh->fn.connect)(kh->arg, &kh->sech, S_TIMEOUT);
520 }
521
522 /*
523  * Setup to handle new incoming connections
524  */
525 static void
526 krb5_accept(in, out, fn)
527     int in, out;
528     void (*fn) P((security_handle_t *, pkt_t *));
529 {
530     struct sockaddr_in sin;
531     size_t len;
532     struct krb5_conn *kc;
533     struct hostent *he;
534
535     /*
536      * Make sure we're initted
537      */
538     init();
539
540     len = sizeof(sin);
541     if (getpeername(in, (struct sockaddr *)&sin, &len) < 0)
542         return;
543     he = gethostbyaddr((void *)&sin.sin_addr, sizeof(sin.sin_addr), AF_INET);
544     if (he == NULL)
545         return;
546
547     kc = conn_get(he->h_name);
548     kc->fd = in;
549     if (gss_server(kc) < 0)
550         error("gss_server failed: %s\n", kc->errmsg);
551     kc->state = authed;
552     accept_fn = fn;
553     conn_read(kc);
554 }
555
556 /*
557  * Locate an existing connection to the given host, or create a new,
558  * unconnected entry if none exists.  The caller is expected to check
559  * for the lack of a connection (kc->fd == -1) and set one up.
560  */
561 static struct krb5_conn *
562 conn_get(hostname)
563     const char *hostname;
564 {
565     struct krb5_conn *kc;
566
567     k5printf(("krb5: conn_get: %s\n", hostname));
568
569     for (kc = connq_first(); kc != NULL; kc = connq_next(kc)) {
570         if (strcasecmp(hostname, kc->hostname) == 0)
571             break;
572     }
573
574     if (kc != NULL) {
575         kc->refcnt++;
576         k5printf(("krb5: conn_get: exists, refcnt to %s is now %d\n",
577             kc->hostname, kc->refcnt));
578         return (kc);
579     }
580
581     k5printf(("krb5: conn_get: creating new handle\n"));
582     /*
583      * We can't be creating a new handle if we are the client
584      */
585     assert(accept_fn == NULL);
586     kc = alloc(sizeof(*kc));
587     kc->fd = -1;
588     kc->readbuf.left = 0;
589     kc->readbuf.size = 0;
590     kc->state = unauthed;
591     kc->ev_read = NULL;
592     strncpy(kc->hostname, hostname, sizeof(kc->hostname) - 1);
593     kc->hostname[sizeof(kc->hostname) - 1] = '\0';
594     kc->errmsg = NULL;
595     kc->gss_context = GSS_C_NO_CONTEXT;
596     /*
597      * [XXX] this is set to 2 in order to force the connection to stay
598      * open and process more protocol requests.  (basically consistant
599      * with bsd-security.c, and theoretically krb4-security.c.   This
600      * needs to be addressed in a cleaner way.
601      */
602     kc->refcnt = 2;
603     TAILQ_INIT(&kc->frameq);
604     connq_append(kc);
605     return (kc);
606 }
607
608 /*
609  * Delete a reference to a connection, and close it if it is the last
610  * reference.
611  */
612 static void
613 conn_put(kc)
614     struct krb5_conn *kc;
615 {
616     OM_uint32 min_stat;
617     struct krb5_frame *kf;
618
619     assert(kc->refcnt > 0);
620     if (--kc->refcnt > 0) {
621         k5printf(("krb5: conn_put: decrementing refcnt for %s to %d\n",
622             kc->hostname, kc->refcnt));
623         return;
624     }
625     k5printf(("krb5: conn_put: closing connection to %s\n", kc->hostname));
626     if (kc->fd != -1)
627         aclose(kc->fd);
628     if (kc->ev_read != NULL)
629         event_release(kc->ev_read);
630     if (kc->errmsg != NULL)
631         amfree(kc->errmsg);
632     gss_delete_sec_context(&min_stat, &kc->gss_context, GSS_C_NO_BUFFER);
633     while ((kf = TAILQ_FIRST(&kc->frameq)) != NULL) {
634         TAILQ_REMOVE(&kc->frameq, kf, tq);
635         if (kf->tok.value != NULL)
636             amfree(kf->tok.value);
637         amfree(kf);
638     }
639     connq_remove(kc);
640     amfree(kc);
641     /* signal that a connection is available */
642     event_wakeup((event_id_t)open_callback);
643 }
644
645 /*
646  * Turn on read events for a conn.  Or, increase a refcnt if we are
647  * already receiving read events.
648  */
649 static void
650 conn_read(kc)
651     struct krb5_conn *kc;
652 {
653
654     if (kc->ev_read != NULL) {
655         kc->ev_read_refcnt++;
656         k5printf(("krb5: conn_read: incremented refcnt to %d for %s\n",
657             kc->ev_read_refcnt, kc->hostname));
658         return;
659     }
660     k5printf(("krb5: conn_read registering event handler for %s\n",
661         kc->hostname));
662     kc->ev_read = event_register(kc->fd, EV_READFD, conn_read_callback, kc);
663     kc->ev_read_refcnt = 1;
664 }
665
666 static void
667 conn_read_cancel(kc)
668     struct krb5_conn *kc;
669 {
670
671     if (--kc->ev_read_refcnt > 0) {
672         k5printf(("krb5: conn_read_cancel: decremented refcnt to %d for %s\n",
673             kc->ev_read_refcnt, kc->hostname));
674         return;
675     }
676     k5printf(("krb5: conn_read_cancel: releasing event handler for %s\n",
677         kc->hostname));
678     event_release(kc->ev_read);
679     kc->ev_read = NULL;
680 }
681
682 /*
683  * frees a handle allocated by the above
684  */
685 static void
686 krb5_close(inst)
687     void *inst;
688 {
689     struct krb5_handle *kh = inst;
690
691     assert(kh != NULL);
692
693     k5printf(("krb5: closing handle to %s\n", kh->hostname));
694
695     if (kh->ks != NULL) {
696         /* This may be null if we get here on an error */
697         krb5_recvpkt_cancel(kh);
698         security_stream_close(&kh->ks->secstr);
699     }
700     amfree(kh->hostname);
701     amfree(kh);
702 }
703
704 /*
705  * Transmit a packet.  Encrypt first.
706  */
707 static int
708 krb5_sendpkt(cookie, pkt)
709     void *cookie;
710     pkt_t *pkt;
711 {
712     struct krb5_handle *kh = cookie;
713     gss_buffer_desc tok;
714     int rval;
715     unsigned char c, *buf;
716
717     assert(kh != NULL);
718     assert(pkt != NULL);
719
720     k5printf(("krb5: sendpkt: enter\n"));
721
722     if (pkt->body[0] == '\0') {
723         c = (unsigned char)pkt->type;
724         tok.length = 1;
725         tok.value = &c;
726     } else {
727         tok.length = strlen(pkt->body) + 2;
728         tok.value = alloc(tok.length);
729         buf = tok.value;
730         *buf++ = (unsigned char)pkt->type;
731         strncpy(buf, pkt->body, tok.length - 2);
732         buf[tok.length - 2] = '\0';
733     }
734
735     k5printf(("krb5: sendpkt: %s (%d) pkt_t (len %d) contains:\n\n\"%s\"\n\n",
736         pkt_type2str(pkt->type), pkt->type, strlen(pkt->body), pkt->body));
737
738     rval = krb5_stream_write(kh->ks, tok.value, tok.length);
739     if (rval < 0)
740         security_seterror(&kh->sech, security_stream_geterror(&kh->ks->secstr));
741     if (tok.length > 1)
742         amfree(tok.value);
743     return (rval);
744 }
745
746 /*
747  * Set up to receive a packet asyncronously, and call back when
748  * it has been read.
749  */
750 static void
751 krb5_recvpkt(cookie, fn, arg, timeout)
752     void *cookie, *arg;
753     void (*fn) P((void *, pkt_t *, security_status_t));
754     int timeout;
755 {
756     struct krb5_handle *kh = cookie;
757
758     assert(kh != NULL);
759
760     k5printf(("krb5: recvpkt registered for %s\n", kh->hostname));
761
762     /*
763      * Reset any pending timeout on this handle
764      */
765     if (kh->ev_timeout != NULL)
766         event_release(kh->ev_timeout);
767
768     /*
769      * Negative timeouts mean no timeout
770      */
771     if (timeout < 0)
772         kh->ev_timeout = NULL;
773     else
774         kh->ev_timeout = event_register(timeout, EV_TIME, recvpkt_timeout, kh);
775
776     kh->fn.recvpkt = fn;
777     kh->arg = arg;
778     krb5_stream_read(kh->ks, recvpkt_callback, kh);
779 }
780
781 /*
782  * Remove a async receive request from the queue
783  */
784 static void
785 krb5_recvpkt_cancel(cookie)
786     void *cookie;
787 {
788     struct krb5_handle *kh = cookie;
789
790     k5printf(("krb5: cancelling recvpkt for %s\n", kh->hostname));
791
792     assert(kh != NULL);
793
794     krb5_stream_read_cancel(kh->ks);
795     if (kh->ev_timeout != NULL) {
796         event_release(kh->ev_timeout);
797         kh->ev_timeout = NULL;
798     }
799 }
800
801 /*
802  * This is called when a handle is woken up because data read off of the
803  * net is for it.
804  */
805 static void
806 recvpkt_callback(cookie, buf, bufsize)
807     void *cookie, *buf;
808     ssize_t bufsize;
809 {
810     pkt_t pkt;
811     struct krb5_handle *kh = cookie;
812
813     assert(kh != NULL);
814
815     /*
816      * We need to cancel the recvpkt request before calling
817      * the callback because the callback may reschedule us.
818      */
819     krb5_recvpkt_cancel(kh);
820
821     switch (bufsize) {
822     case 0:
823         security_seterror(&kh->sech,
824             "EOF on read from %s", kh->hostname);
825         (*kh->fn.recvpkt)(kh->arg, NULL, S_ERROR);
826         return;
827     case -1:
828         security_seterror(&kh->sech, security_stream_geterror(&kh->ks->secstr));
829         (*kh->fn.recvpkt)(kh->arg, NULL, S_ERROR);
830         return;
831     default:
832         parse_pkt(&pkt, buf, bufsize);
833         k5printf(("krb5: received %s pkt (%d) from %s, contains:\n\n\"%s\"\n\n",
834             pkt_type2str(pkt.type), pkt.type, kh->hostname, pkt.body));
835         (*kh->fn.recvpkt)(kh->arg, &pkt, S_OK);
836         return;
837     }
838 }
839
840 /*
841  * This is called when a handle times out before receiving a packet.
842  */
843 static void
844 recvpkt_timeout(cookie)
845     void *cookie;
846 {
847     struct krb5_handle *kh = cookie;
848
849     assert(kh != NULL);
850
851     k5printf(("krb5: recvpkt timeout for %s\n", kh->hostname));
852
853     krb5_recvpkt_cancel(kh);
854     (*kh->fn.recvpkt)(kh->arg, NULL, S_TIMEOUT);
855 }
856
857 /*
858  * Create the server end of a stream.  For krb5, this means setup a stream
859  * object and allocate a new handle for it.
860  */
861 static void *
862 krb5_stream_server(h)
863     void *h;
864 {
865     struct krb5_handle *kh = h;
866     struct krb5_stream *ks;
867
868     assert(kh != NULL);
869
870     ks = alloc(sizeof(*ks));
871     security_streaminit(&ks->secstr, &krb5_security_driver);
872     ks->kc = conn_get(kh->hostname);
873     /*
874      * Stream should already be setup!
875      */
876     if (ks->kc->fd < 0) {
877         conn_put(ks->kc);
878         amfree(ks);
879         security_seterror(&kh->sech, "lost connection");
880         return (NULL);
881     }
882     /*
883      * so as not to conflict with the amanda server's handle numbers,
884      * we start at 5000 and work down
885      */
886     ks->handle = 5000 - newhandle++;
887     ks->ev_read = NULL;
888     k5printf(("krb5: stream_server: created stream %d\n", ks->handle));
889     return (ks);
890 }
891
892 /*
893  * Accept an incoming connection on a stream_server socket
894  * Nothing needed for krb5.
895  */
896 static int
897 krb5_stream_accept(s)
898     void *s;
899 {
900
901     return (0);
902 }
903
904 /*
905  * Return a connected stream.  For krb5, this means setup a stream
906  * with the supplied handle.
907  */
908 static void *
909 krb5_stream_client(h, id)
910     void *h;
911     int id;
912 {
913     struct krb5_handle *kh = h;
914     struct krb5_stream *ks;
915
916     assert(kh != NULL);
917
918     if (id <= 0) {
919         security_seterror(&kh->sech,
920             "%d: invalid security stream id", id);
921         return (NULL);
922     }
923
924     ks = alloc(sizeof(*ks));
925     security_streaminit(&ks->secstr, &krb5_security_driver);
926     ks->handle = id;
927     ks->ev_read = NULL;
928     ks->kc = conn_get(kh->hostname);
929
930     k5printf(("krb5: stream_client: connected to stream %d\n", id));
931
932     return (ks);
933 }
934
935 /*
936  * Close and unallocate resources for a stream.
937  */
938 static void
939 krb5_stream_close(s)
940     void *s;
941 {
942     struct krb5_stream *ks = s;
943
944     assert(ks != NULL);
945
946     k5printf(("krb5: stream_close: closing stream %d\n", ks->handle));
947
948     krb5_stream_read_cancel(ks);
949     conn_put(ks->kc);
950     amfree(ks);
951 }
952
953 /*
954  * Authenticate a stream
955  * Nothing needed for krb5.  The tcp connection is authenticated
956  * on startup.
957  */
958 static int
959 krb5_stream_auth(s)
960     void *s;
961 {
962
963     return (0);
964 }
965
966 /*
967  * Returns the stream id for this stream.  This is just the local
968  * port.
969  */
970 static int
971 krb5_stream_id(s)
972     void *s;
973 {
974     struct krb5_stream *ks = s;
975
976     assert(ks != NULL);
977
978     return (ks->handle);
979 }
980
981 /*
982  * Write a chunk of data to a stream.  Blocks until completion.
983  */
984 static int
985 krb5_stream_write(s, buf, size)
986     void *s;
987     const void *buf;
988     size_t size;
989 {
990     struct krb5_stream *ks = s;
991     gss_buffer_desc tok;
992 #ifdef AMANDA_KRB5_ENCRYPT
993     gss_buffer_desc enctok;
994     OM_uint32 min_stat;
995 #endif
996     int rc;
997
998     assert(ks != NULL);
999
1000     k5printf(("krb5: stream_write: writing %d bytes to %s:%d\n", size,
1001         ks->kc->hostname, ks->handle));
1002
1003     tok.length = size;
1004     tok.value = (void *)buf;    /* safe to discard const */
1005 #ifdef AMANDA_KRB5_ENCRYPT
1006     if (kencrypt(ks, &tok, &enctok) < 0)
1007         return (-1);
1008     rc = send_token(ks->kc, ks->handle, &enctok);
1009 #else
1010     rc = send_token(ks->kc, ks->handle, &tok);
1011 #endif
1012     if (rc < 0)
1013         security_stream_seterror(&ks->secstr, ks->kc->errmsg);
1014 #ifdef AMANDA_KRB5_ENCRYPT
1015     gss_release_buffer(&min_stat, &enctok);
1016 #endif
1017     return (rc);
1018 }
1019
1020 /*
1021  * Submit a request to read some data.  Calls back with the given
1022  * function and arg when completed.
1023  */
1024 static void
1025 krb5_stream_read(s, fn, arg)
1026     void *s, *arg;
1027     void (*fn) P((void *, void *, int));
1028 {
1029     struct krb5_stream *ks = s;
1030
1031     assert(ks != NULL);
1032
1033     /*
1034      * Only one read request can be active per stream.
1035      */
1036     ks->fn = fn;
1037     ks->arg = arg;
1038
1039     /*
1040      * First see if there's any queued frames for this stream.
1041      * If so, we're done.
1042      */
1043     if (conn_run_frameq(ks->kc, ks) > 0)
1044         return;
1045
1046     if (ks->ev_read == NULL) {
1047         ks->ev_read = event_register((event_id_t)ks->kc, EV_WAIT,
1048             stream_read_callback, ks);
1049         conn_read(ks->kc);
1050     }
1051 }
1052
1053 /*
1054  * Cancel a previous stream read request.  It's ok if we didn't have a read
1055  * scheduled.
1056  */
1057 static void
1058 krb5_stream_read_cancel(s)
1059     void *s;
1060 {
1061     struct krb5_stream *ks = s;
1062
1063     assert(ks != NULL);
1064
1065     if (ks->ev_read != NULL) {
1066         event_release(ks->ev_read);
1067         ks->ev_read = NULL;
1068         conn_read_cancel(ks->kc);
1069     }
1070 }
1071
1072 /*
1073  * Callback for krb5_stream_read
1074  */
1075 static void
1076 stream_read_callback(arg)
1077     void *arg;
1078 {
1079     struct krb5_stream *ks = arg;
1080
1081     assert(ks != NULL);
1082
1083     k5printf(("krb5: stream_read_callback: handle %d\n", ks->handle));
1084
1085     conn_run_frameq(ks->kc, ks);
1086 }
1087
1088 /*
1089  * Run down a list of queued frames for a krb5_conn, and if we find one
1090  * that matches the passed handle, fire the read event.  Only
1091  * process one frame.
1092  *
1093  * Returns 1 if a frame was found and processed.
1094  */
1095 static int
1096 conn_run_frameq(kc, ks)
1097     struct krb5_conn *kc;
1098     struct krb5_stream *ks;
1099 {
1100     struct krb5_frame *kf, *nextkf;
1101     gss_buffer_desc *enctok, *dectok;
1102 #ifdef AMANDA_KRB5_ENCRYPT
1103     OM_uint32 min_stat;
1104     gss_buffer_desc tok;
1105 #endif
1106
1107     /*
1108      * Iterate through all of the frames in the queue.  If one
1109      * is for us, process it.  If we hit an EOF frame, shut down.
1110      * Stop after processing one frame, because we are only supposed
1111      * to return one read request.
1112      */
1113     for (kf = TAILQ_FIRST(&kc->frameq); kf != NULL; kf = nextkf) {
1114         nextkf = TAILQ_NEXT(kf, tq);
1115
1116         if (kf->handle != ks->handle && kf->handle != H_EOF) {
1117             k5printf(("krb5: conn_frameq_run: not for us (handle %d)\n",
1118                 kf->handle));
1119             continue;
1120         }
1121         /*
1122          * We want all listeners to see the EOF, so never remove it.
1123          * It will get cleaned up when the connection is closed
1124          * in conn_put().
1125          */
1126         if (kf->handle != H_EOF)
1127             TAILQ_REMOVE(&kc->frameq, kf, tq);
1128
1129         /*
1130          * Remove the event first, and then call the callback.
1131          * We remove it first because we don't want to get in their
1132          * way if they reschedule it.
1133          */
1134         krb5_stream_read_cancel(ks);
1135
1136         enctok = &kf->tok;
1137
1138         if (enctok->length == 0) {
1139             assert(kf->handle == H_EOF);
1140             k5printf(("krb5: stream_read_callback: EOF\n"));
1141             (*ks->fn)(ks->arg, NULL, 0);
1142             return (1); /* stop after EOF */
1143         }
1144
1145 #ifdef AMANDA_KRB5_ENCRYPT
1146         dectok = &tok;
1147         if (kdecrypt(ks, enctok, &tok) < 0) {
1148             k5printf(("krb5: stream_read_callback: kdecrypt error\n"));
1149             (*ks->fn)(ks->arg, NULL, -1);
1150         } else
1151 #else
1152         dectok = enctok;
1153 #endif
1154         {
1155             k5printf(("krb5: stream_read_callback: read %d bytes from %s:%d\n",
1156                 dectok->length, ks->kc->hostname, ks->handle));
1157             (*ks->fn)(ks->arg, dectok->value, dectok->length);
1158 #ifdef AMANDA_KRB5_ENCRYPT
1159             gss_release_buffer(&min_stat, dectok);
1160 #endif
1161         }
1162         amfree(enctok->value);
1163         amfree(kf);
1164         return (1);             /* stop after one frame */
1165     }
1166     return (0);
1167 }
1168
1169 /*
1170  * The callback for the netfd for the event handler
1171  * Determines if this packet is for this security handle,
1172  * and does the real callback if so.
1173  */
1174 static void
1175 conn_read_callback(cookie)
1176     void *cookie;
1177 {
1178     struct krb5_conn *kc = cookie;
1179     struct krb5_handle *kh;
1180     struct krb5_frame *kf;
1181     pkt_t pkt;
1182     gss_buffer_desc *dectok;
1183     int rc;
1184 #ifdef AMANDA_KRB5_ENCRYPT
1185     gss_buffer_desc tok;
1186     OM_uint32 min_stat;
1187 #endif
1188
1189     assert(cookie != NULL);
1190
1191     k5printf(("krb5: conn_read_callback\n"));
1192
1193     kf = alloc(sizeof(*kf));
1194     TAILQ_INSERT_TAIL(&kc->frameq, kf, tq);
1195
1196     /* Read the data off the wire.  If we get errors, shut down. */
1197     rc = recv_token(kc, &kf->handle, &kf->tok, 5);
1198     k5printf(("krb5: conn_read_callback: recv_token returned %d handle = %d\n",
1199         rc, kf->handle));
1200     if (rc <= 0) {
1201         kf->tok.value = NULL;
1202         kf->tok.length = 0;
1203         kf->handle = H_EOF;
1204         rc = event_wakeup((event_id_t)kc);
1205         k5printf(("krb5: conn_read_callback: event_wakeup return %d\n", rc));
1206         return;
1207     }
1208
1209     /* If there are events waiting on this handle, we're done */
1210     rc = event_wakeup((event_id_t)kc);
1211     k5printf(("krb5: conn_read_callback: event_wakeup return %d\n", rc));
1212     if (rc > 0)
1213         return;
1214
1215     /*
1216      * If there is no accept fn registered, then just leave the
1217      * packet queued.  The caller may register a function later.
1218      */
1219     if (accept_fn == NULL) {
1220         k5printf(("krb5: no accept_fn so leaving packet queued.\n"));
1221         return;
1222     }
1223
1224     kh = alloc(sizeof(*kh));
1225     security_handleinit(&kh->sech, &krb5_security_driver);
1226     kh->hostname = stralloc(kc->hostname);
1227     kh->ks = krb5_stream_client(kh, kf->handle);
1228     kh->ev_wait = NULL;
1229     kh->ev_timeout = NULL;
1230
1231     TAILQ_REMOVE(&kc->frameq, kf, tq);
1232     k5printf(("krb5: new connection\n"));
1233 #ifdef AMANDA_KRB5_ENCRYPT
1234     dectok = &tok;
1235     rc = kdecrypt(kh->ks, &kf->tok, dectok);
1236 #else
1237     dectok = &kf->tok;
1238 #endif
1239
1240 #ifdef AMANDA_KRB5_ENCRYPT
1241     if (rc < 0) {
1242         security_seterror(&kh->sech, security_geterror(&kh->ks->secstr));
1243         (*accept_fn)(&kh->sech, NULL);
1244     } else
1245 #endif
1246     {
1247         parse_pkt(&pkt, dectok->value, dectok->length);
1248 #ifdef AMANDA_KRB5_ENCRYPT
1249         gss_release_buffer(&min_stat, dectok);
1250 #endif
1251         (*accept_fn)(&kh->sech, &pkt);
1252     }
1253     amfree(kf->tok.value);
1254     amfree(kf);
1255
1256     /*
1257      * We can only accept one connection per process, since we're tcp
1258      * based and run out of inetd.  So, delete our accept reference once
1259      * we've gotten the first connection.
1260      */
1261
1262     /*
1263      * [XXX] actually, the protocol has been changed to have multiple
1264      * requests in one session be possible.  By not resetting accept_fn,
1265      * this will caused them to be properly processed.  this needs to be
1266      * addressed in a much cleaner way.
1267      */
1268     if (accept_fn != NULL)
1269         conn_put(kc);
1270     /* accept_fn = NULL; */
1271 }
1272
1273 /*
1274  * Negotiate a krb5 gss context from the client end.
1275  */
1276 static int
1277 gss_client(kh)
1278     struct krb5_handle *kh;
1279 {
1280     struct krb5_stream *ks = kh->ks;
1281     struct krb5_conn *kc = ks->kc;
1282     gss_buffer_desc send_tok, recv_tok;
1283     OM_uint32 maj_stat, min_stat;
1284     unsigned int ret_flags;
1285     int rc, rval = -1;
1286     gss_name_t gss_name;
1287
1288     k5printf(("gss_client\n"));
1289
1290     send_tok.value = vstralloc("host/", ks->kc->hostname, NULL);
1291     send_tok.length = strlen(send_tok.value) + 1;
1292     maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NULL_OID,
1293         &gss_name);
1294     if (maj_stat != GSS_S_COMPLETE) {
1295         security_seterror(&kh->sech, "can't import name %s: %s",
1296             (char *)send_tok.value, gss_error(maj_stat, min_stat));
1297         amfree(send_tok.value);
1298         return (-1);
1299     }
1300     amfree(send_tok.value);
1301     kc->gss_context = GSS_C_NO_CONTEXT;
1302
1303     /*
1304      * Perform the context-establishement loop.
1305      *
1306      * Every generated token is stored in send_tok which is then
1307      * transmitted to the server; every received token is stored in
1308      * recv_tok (empty on the first pass) to be processed by
1309      * the next call to gss_init_sec_context.
1310      * 
1311      * GSS-API guarantees that send_tok's length will be non-zero
1312      * if and only if the server is expecting another token from us,
1313      * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
1314      * and only if the server has another token to send us.
1315      */
1316
1317     for (recv_tok.length = 0;;) {
1318         min_stat = 0;
1319         maj_stat = gss_init_sec_context(&min_stat,
1320             GSS_C_NO_CREDENTIAL,
1321             &kc->gss_context,
1322             gss_name,
1323             GSS_C_NULL_OID,
1324             GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG,
1325             0, NULL,    /* no channel bindings */
1326             (recv_tok.length == 0 ? GSS_C_NO_BUFFER : &recv_tok),
1327             NULL,       /* ignore mech type */
1328             &send_tok,
1329             &ret_flags,
1330             NULL);      /* ignore time_rec */
1331
1332         if (recv_tok.length != 0) {
1333             amfree(recv_tok.value);
1334             recv_tok.length = 0;
1335         }
1336
1337         if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
1338             security_seterror(&kh->sech,
1339                 "error getting gss context: %s",
1340                 gss_error(maj_stat, min_stat));
1341             goto done;
1342         }
1343
1344         /*
1345          * Send back the response
1346          */
1347         if (send_tok.length != 0 && send_token(kc, ks->handle, &send_tok) < 0) {
1348             security_seterror(&kh->sech, kc->errmsg);
1349             gss_release_buffer(&min_stat, &send_tok);
1350             goto done;
1351         }
1352         gss_release_buffer(&min_stat, &send_tok);
1353
1354         /*
1355          * If we need to continue, then register for more packets
1356          */
1357         if (maj_stat != GSS_S_CONTINUE_NEEDED)
1358             break;
1359
1360         if ((rc = recv_token(kc, NULL, &recv_tok, GSS_TIMEOUT)) <= 0) {
1361             if (rc < 0)
1362                 security_seterror(&kh->sech,
1363                     "recv error in gss loop: %s", kc->errmsg);
1364             else
1365                 security_seterror(&kh->sech, "EOF in gss loop");
1366             goto done;
1367         }
1368     }
1369
1370     rval = 0;
1371 done:
1372     gss_release_name(&min_stat, &gss_name);
1373     return (rval);
1374 }
1375
1376 /*
1377  * Negotiate a krb5 gss context from the server end.
1378  */
1379 static int
1380 gss_server(kc)
1381     struct krb5_conn *kc;
1382 {
1383     OM_uint32 maj_stat, min_stat, ret_flags;
1384     gss_buffer_desc send_tok, recv_tok;
1385     gss_OID doid;
1386     gss_name_t gss_name;
1387     gss_cred_id_t gss_creds;
1388     char *p, *realm, *msg;
1389     uid_t euid;
1390     int rc, rval = -1;
1391     char errbuf[256];
1392
1393     k5printf(("gss_server\n"));
1394
1395     assert(kc != NULL);
1396
1397     /*
1398      * We need to be root while in gss_acquire_cred() to read the host key
1399      * out of the default keytab.  We also need to be root in
1400      * gss_accept_context() thanks to the replay cache code.
1401      */
1402     euid = geteuid();
1403     if (getuid() != 0) {
1404         snprintf(errbuf, sizeof(errbuf),
1405             "real uid is %ld, needs to be 0 to read krb5 host key",
1406             (long)getuid());
1407         goto out;
1408     }
1409     if (seteuid(0) < 0) {
1410         snprintf(errbuf, sizeof(errbuf),
1411             "can't seteuid to uid 0: %s", strerror(errno));
1412         goto out;
1413     }
1414
1415     send_tok.value = vstralloc("host/", hostname, NULL);
1416     send_tok.length = strlen(send_tok.value) + 1;
1417     for (p = send_tok.value; *p != '\0'; p++) {
1418         if (isupper((int)*p))
1419             *p = tolower(*p);
1420     }
1421     maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NULL_OID,
1422         &gss_name);
1423     if (maj_stat != GSS_S_COMPLETE) {
1424         seteuid(euid);
1425         snprintf(errbuf, sizeof(errbuf),
1426             "can't import name %s: %s", (char *)send_tok.value,
1427             gss_error(maj_stat, min_stat));
1428         amfree(send_tok.value);
1429         goto out;
1430     }
1431     amfree(send_tok.value);
1432
1433     maj_stat = gss_acquire_cred(&min_stat, gss_name, 0,
1434         GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &gss_creds, NULL, NULL);
1435     if (maj_stat != GSS_S_COMPLETE) {
1436         snprintf(errbuf, sizeof(errbuf),
1437             "can't acquire creds for host key host/%s: %s", hostname,
1438             gss_error(maj_stat, min_stat));
1439         gss_release_name(&min_stat, &gss_name);
1440         seteuid(euid);
1441         goto out;
1442     }
1443     gss_release_name(&min_stat, &gss_name);
1444
1445     for (recv_tok.length = 0;;) {
1446         if ((rc = recv_token(kc, NULL, &recv_tok, GSS_TIMEOUT)) <= 0) {
1447             if (rc < 0) {
1448                 snprintf(errbuf, sizeof(errbuf),
1449                     "recv error in gss loop: %s", kc->errmsg);
1450                 amfree(kc->errmsg);
1451             } else
1452                 snprintf(errbuf, sizeof(errbuf), "EOF in gss loop");
1453             goto out;
1454         }
1455
1456         maj_stat = gss_accept_sec_context(&min_stat, &kc->gss_context,
1457             gss_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS,
1458             &gss_name, &doid, &send_tok, &ret_flags, NULL, NULL);
1459
1460         if (maj_stat != GSS_S_COMPLETE &&
1461             maj_stat != GSS_S_CONTINUE_NEEDED) {
1462             snprintf(errbuf, sizeof(errbuf),
1463                 "error accepting context: %s", gss_error(maj_stat, min_stat));
1464             amfree(recv_tok.value);
1465             goto out;
1466         }
1467         amfree(recv_tok.value);
1468
1469         if (send_tok.length > 0 && send_token(kc, 0, &send_tok) < 0) {
1470             strncpy(errbuf, kc->errmsg, sizeof(errbuf) - 1);
1471             errbuf[sizeof(errbuf) - 1] = '\0';
1472             amfree(kc->errmsg);
1473             gss_release_buffer(&min_stat, &send_tok);
1474             goto out;
1475         }
1476         gss_release_buffer(&min_stat, &send_tok);
1477
1478
1479         /*
1480          * If we need to get more from the client, then register for
1481          * more packets.
1482          */
1483         if (maj_stat != GSS_S_CONTINUE_NEEDED)
1484             break;
1485     }
1486
1487     maj_stat = gss_display_name(&min_stat, gss_name, &send_tok, &doid);
1488     if (maj_stat != GSS_S_COMPLETE) {
1489         snprintf(errbuf, sizeof(errbuf),
1490             "can't display gss name: %s", gss_error(maj_stat, min_stat));
1491         gss_release_name(&min_stat, &gss_name);
1492         goto out;
1493     }
1494     gss_release_name(&min_stat, &gss_name);
1495
1496     /* get rid of the realm */
1497     if ((p = strchr(send_tok.value, '@')) == NULL) {
1498         snprintf(errbuf, sizeof(errbuf),
1499             "malformed gss name: %s", (char *)send_tok.value);
1500         amfree(send_tok.value);
1501         goto out;
1502     }
1503     *p = '\0';
1504     realm = ++p;
1505
1506     /* 
1507      * If the principal doesn't match, complain
1508      */
1509     if ((msg = krb5_checkuser(kc->hostname, send_tok.value, realm)) != NULL) {
1510         snprintf(errbuf, sizeof(errbuf),
1511             "access not allowed from %s: %s", (char *)send_tok.value, msg);
1512         amfree(send_tok.value);
1513         goto out;
1514     }
1515     amfree(send_tok.value);
1516
1517     rval = 0;
1518 out:
1519     seteuid(euid);
1520     if (rval != 0)
1521         kc->errmsg = stralloc(errbuf);
1522     k5printf(("gss_server returning %d\n", rval));
1523     return (rval);
1524 }
1525
1526 /*
1527  * Setup some things about krb5.  This should only be called once.
1528  */
1529 static void
1530 init()
1531 {
1532     static int beenhere = 0;
1533     struct hostent *he;
1534     char *p;
1535     int krb5_setenv P((const char *, const char *, int));
1536
1537     if (beenhere)
1538         return;
1539     beenhere = 1;
1540
1541 #ifndef BROKEN_MEMORY_CCACHE
1542     krb5_setenv(KRB5_ENV_CCNAME, "MEMORY:amanda_ccache", 1);
1543 #else
1544     /*
1545      * MEMORY ccaches seem buggy and cause a lot of internal heap
1546      * corruption.  malloc has been known to core dump.  This behavior
1547      * has been witnessed in Cygnus' kerbnet 1.2, MIT's krb V 1.0.5 and
1548      * MIT's krb V -current as of 3/17/1999.
1549      *
1550      * We just use a lame ccache scheme with a uid suffix.
1551      */
1552     atexit(cleanup);
1553     {
1554         char ccache[64];
1555         snprintf(ccache, sizeof(ccache), "FILE:/tmp/amanda_ccache.%ld.%ld",
1556             (long)geteuid(), (long)getpid());
1557         krb5_setenv(KRB5_ENV_CCNAME, ccache, 1);
1558     }
1559 #endif
1560
1561     gethostname(hostname, sizeof(hostname) - 1);
1562     hostname[sizeof(hostname) - 1] = '\0';
1563     /*
1564      * In case it isn't fully qualified, do a DNS lookup.
1565      */
1566     if ((he = gethostbyname(hostname)) != NULL)
1567         strncpy(hostname, he->h_name, sizeof(hostname) - 1);
1568
1569     /*
1570      * Lowercase the results.  We assume all host/ principals will be
1571      * lowercased.
1572      */
1573     for (p = hostname; *p != '\0'; p++) {
1574         if (isupper((int)*p))
1575             *p = tolower(*p);
1576     }
1577 }
1578
1579 #ifdef BROKEN_MEMORY_CCACHE
1580 static void
1581 cleanup()
1582 {
1583 #ifdef KDESTROY_VIA_UNLINK
1584     char ccache[64];
1585     snprintf(ccache, sizeof(ccache), "/tmp/amanda_ccache.%ld.%ld",
1586         (long)geteuid(), (long)getpid());
1587     unlink(ccache);
1588 #else
1589     kdestroy();
1590 #endif
1591 }
1592 #endif
1593
1594 /*
1595  * Get a ticket granting ticket and stuff it in the cache
1596  */
1597 static const char *
1598 get_tgt(keytab_name, principal_name)
1599         char *keytab_name, *principal_name;
1600 {
1601     krb5_context context;
1602     krb5_error_code ret;
1603     krb5_principal client = NULL, server = NULL;
1604     krb5_creds creds;
1605     krb5_keytab keytab = NULL;
1606     krb5_ccache ccache;
1607     krb5_timestamp now;
1608     krb5_data tgtname = { 0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME };
1609     static char *error = NULL;
1610
1611     if (error != NULL) {
1612         amfree(error);
1613         error = NULL;
1614     }
1615
1616     if ((ret = krb5_init_context(&context)) != 0) {
1617         error = vstralloc("error initializing krb5 context: ",
1618             error_message(ret), NULL);
1619         return (error);
1620     }
1621
1622     krb5_init_ets(context);
1623
1624     if(!keytab_name) {
1625         error = vstralloc("error  -- no krb5 keytab defined", NULL);
1626         return(error);
1627     }
1628
1629     if(!principal_name) {
1630         error = vstralloc("error  -- no krb5 principal defined", NULL);
1631         return(error);
1632     }
1633
1634     /*
1635      * Resolve keytab file into a keytab object
1636      */
1637     if ((ret = krb5_kt_resolve(context, keytab_name, &keytab)) != 0) {
1638         error = vstralloc("error resolving keytab ", keytab, ": ",
1639             error_message(ret), NULL);
1640         return (error);
1641     }
1642
1643     /*
1644      * Resolve the amanda service held in the keytab into a principal
1645      * object
1646      */
1647     ret = krb5_parse_name(context, principal_name, &client);
1648     if (ret != 0) {
1649         error = vstralloc("error parsing ", principal_name, ": ",
1650             error_message(ret), NULL);
1651         return (error);
1652     }
1653
1654     ret = krb5_build_principal_ext(context, &server,
1655         krb5_princ_realm(context, client)->length,
1656         krb5_princ_realm(context, client)->data,
1657         tgtname.length, tgtname.data,
1658         krb5_princ_realm(context, client)->length,
1659         krb5_princ_realm(context, client)->data,
1660         0);
1661     if (ret != 0) {
1662         error = vstralloc("error while building server name: ",
1663             error_message(ret), NULL);
1664         return (error);
1665     }
1666
1667     ret = krb5_timeofday(context, &now);
1668     if (ret != 0) {
1669         error = vstralloc("error getting time of day: ", error_message(ret),
1670             NULL);
1671         return (error);
1672     }
1673
1674     memset(&creds, 0, sizeof(creds));
1675     creds.times.starttime = 0;
1676     creds.times.endtime = now + AMANDA_TKT_LIFETIME;
1677
1678     creds.client = client;
1679     creds.server = server;
1680
1681     /*
1682      * Get a ticket for the service, using the keytab
1683      */
1684     ret = krb5_get_in_tkt_with_keytab(context, 0, NULL, NULL, NULL,
1685         keytab, 0, &creds, 0);
1686
1687     if (ret != 0) {
1688         error = vstralloc("error getting ticket for ", principal_name,
1689             ": ", error_message(ret), NULL);
1690         goto cleanup2;
1691     }
1692
1693     if ((ret = krb5_cc_default(context, &ccache)) != 0) {
1694         error = vstralloc("error initializing ccache: ", error_message(ret),
1695             NULL);
1696         goto cleanup;
1697     }
1698     if ((ret = krb5_cc_initialize(context, ccache, client)) != 0) {
1699         error = vstralloc("error initializing ccache: ", error_message(ret),
1700             NULL);
1701         goto cleanup;
1702     }
1703     if ((ret = krb5_cc_store_cred(context, ccache, &creds)) != 0) {
1704         error = vstralloc("error storing creds in ccache: ",
1705             error_message(ret), NULL);
1706         /* FALLTHROUGH */
1707     }
1708     krb5_cc_close(context, ccache);
1709 cleanup:
1710     krb5_free_cred_contents(context, &creds);
1711 cleanup2:
1712 #if 0
1713     krb5_free_principal(context, client);
1714     krb5_free_principal(context, server);
1715 #endif
1716     krb5_free_context(context);
1717     return (error);
1718 }
1719
1720 /*
1721  * get rid of tickets
1722  */
1723 kdestroy()
1724 {
1725     krb5_context context;
1726     krb5_ccache ccache;
1727
1728     if ((krb5_init_context(&context)) != 0) {
1729         return;
1730     }
1731     if ((krb5_cc_default(context, &ccache)) != 0) {
1732         goto cleanup;
1733     }
1734
1735     krb5_cc_destroy(context, ccache);
1736     krb5_cc_close(context, ccache);
1737
1738 cleanup:
1739      krb5_free_context(context);
1740      return;
1741 }
1742
1743 static void
1744 parse_pkt(pkt, buf, bufsize)
1745     pkt_t *pkt;
1746     const void *buf;
1747     size_t bufsize;
1748 {
1749     const unsigned char *bufp = buf;
1750
1751     k5printf(("krb5: parse_pkt: parsing buffer of %d bytes\n", bufsize));
1752
1753     pkt->type = (pktype_t)*bufp++;
1754     bufsize--;
1755
1756     if (bufsize == 0) {
1757         pkt->body[0] = '\0';
1758     } else {
1759         if (bufsize > sizeof(pkt->body) - 1)
1760             bufsize = sizeof(pkt->body) - 1;
1761         memcpy(pkt->body, bufp, bufsize);
1762         pkt->body[sizeof(pkt->body) - 1] = '\0';
1763     }
1764
1765     k5printf(("krb5: parse_pkt: %s (%d): \"%s\"\n", pkt_type2str(pkt->type),
1766         pkt->type, pkt->body));
1767 }
1768
1769
1770 /*
1771  * Formats an error from the gss api
1772  */
1773 static const char *
1774 gss_error(major, minor)
1775     OM_uint32 major, minor;
1776 {
1777     static gss_buffer_desc msg;
1778     OM_uint32 min_stat, msg_ctx;
1779
1780     if (msg.length > 0)
1781         gss_release_buffer(&min_stat, &msg);
1782
1783     msg_ctx = 0;
1784     if (major == GSS_S_FAILURE)
1785         gss_display_status(&min_stat, minor, GSS_C_MECH_CODE, GSS_C_NULL_OID,
1786             &msg_ctx, &msg);
1787     else
1788         gss_display_status(&min_stat, major, GSS_C_GSS_CODE, GSS_C_NULL_OID,
1789             &msg_ctx, &msg);
1790     return ((const char *)msg.value);
1791 }
1792
1793 /*
1794  * Transmits a gss_buffer_desc over a krb5_handle, adding
1795  * the necessary headers to allow the remote end to decode it.
1796  * Encryption must be done by the caller.
1797  */
1798 static int
1799 send_token(kc, handle, tok)
1800     struct krb5_conn *kc;
1801     int handle;
1802     const gss_buffer_desc *tok;
1803 {
1804     OM_uint32 netlength, nethandle;
1805     struct iovec iov[3];
1806
1807     k5printf(("krb5: send_token: writing %d bytes to %s\n", tok->length,
1808         kc->hostname));
1809
1810     if (tok->length > AMANDA_MAX_TOK_SIZE) {
1811         kc->errmsg = newvstralloc(kc->errmsg, "krb5 write error to ",
1812             kc->hostname, ": token too large", NULL);
1813         return (-1);
1814     }
1815
1816     /*
1817      * Format is:
1818      *   32 bit length (network byte order)
1819      *   32 bit handle (network byte order)
1820      *   data
1821      */
1822     netlength = htonl(tok->length);
1823     iov[0].iov_base = (void *)&netlength;
1824     iov[0].iov_len = sizeof(netlength);
1825
1826     nethandle = htonl(handle);
1827     iov[1].iov_base = (void *)&nethandle;
1828     iov[1].iov_len = sizeof(nethandle);
1829
1830     iov[2].iov_base = (void *)tok->value;
1831     iov[2].iov_len = tok->length;
1832
1833     if (net_writev(kc->fd, iov, 3) < 0) {
1834         kc->errmsg = newvstralloc(kc->errmsg, "krb5 write error to ",
1835             kc->hostname, ": ", strerror(errno), NULL);
1836         return (-1);
1837     }
1838     return (0);
1839 }
1840
1841 static int
1842 recv_token(kc, handle, gtok, timeout)
1843     struct krb5_conn *kc;
1844     int *handle;
1845     gss_buffer_desc *gtok;
1846     int timeout;
1847 {
1848     OM_uint32 netint;
1849
1850     assert(kc->fd >= 0);
1851     assert(gtok != NULL);
1852
1853     k5printf(("krb5: recv_token: reading from %s\n", kc->hostname));
1854
1855     switch (net_read(kc, &netint, sizeof(netint), timeout)) {
1856     case -1:
1857         kc->errmsg = newvstralloc(kc->errmsg, "recv error: ", strerror(errno),
1858             NULL);
1859         k5printf(("krb5 recv_token error return: %s\n", kc->errmsg));
1860         return (-1);
1861     case 0:
1862         gtok->length = 0;
1863         return (0);
1864     default:
1865         break;
1866     }
1867     gtok->length = ntohl(netint);
1868
1869     if (gtok->length > AMANDA_MAX_TOK_SIZE) {
1870         kc->errmsg = newstralloc(kc->errmsg, "recv error: buffer too large");
1871         k5printf(("krb5 recv_token error return: %s\n", kc->errmsg));
1872         return (-1);
1873     }
1874
1875     switch (net_read(kc, &netint, sizeof(netint), timeout)) {
1876     case -1:
1877         kc->errmsg = newvstralloc(kc->errmsg, "recv error: ", strerror(errno),
1878             NULL);
1879         k5printf(("krb5 recv_token error return: %s\n", kc->errmsg));
1880         return (-1);
1881     case 0:
1882         gtok->length = 0;
1883         return (0);
1884     default:
1885         break;
1886     }
1887     if (handle != NULL)
1888         *handle = ntohl(netint);
1889
1890     gtok->value = alloc(gtok->length);
1891     switch (net_read(kc, gtok->value, gtok->length, timeout)) {
1892     case -1:
1893         kc->errmsg = newvstralloc(kc->errmsg, "recv error: ", strerror(errno),
1894             NULL);
1895         k5printf(("krb5 recv_token error return: %s\n", kc->errmsg));
1896         amfree(gtok->value);
1897         return (-1);
1898     case 0:
1899         amfree(gtok->value);
1900         gtok->length = 0;
1901         break;
1902     default:
1903         break;
1904     }
1905
1906     k5printf(("krb5: recv_token: read %d bytes from %s\n", gtok->length,
1907         kc->hostname));
1908     return (gtok->length);
1909 }
1910
1911 #ifdef AMANDA_KRB5_ENCRYPT
1912 static int
1913 kencrypt(ks, tok, enctok)
1914     struct krb5_stream *ks;
1915     gss_buffer_desc *tok, *enctok;
1916 {
1917     int conf_state;
1918     OM_uint32 maj_stat, min_stat;
1919
1920     assert(ks->kc->gss_context != GSS_C_NO_CONTEXT);
1921     maj_stat = gss_seal(&min_stat, ks->kc->gss_context, 1,
1922         GSS_C_QOP_DEFAULT, tok, &conf_state, enctok);
1923     if (maj_stat != GSS_S_COMPLETE || conf_state == 0) {
1924         security_stream_seterror(&ks->secstr,
1925             "krb5 encryption failed to %s: %s",
1926             ks->kc->hostname, gss_error(maj_stat, min_stat));
1927         return (-1);
1928     }
1929     return (0);
1930 }
1931
1932 static int
1933 kdecrypt(ks, enctok, tok)
1934     struct krb5_stream *ks;
1935     gss_buffer_desc *enctok, *tok;
1936 {
1937     OM_uint32 maj_stat, min_stat;
1938     int conf_state, qop_state;
1939
1940     k5printf(("krb5: kdecrypt: decrypting %d bytes\n", enctok->length));
1941
1942     assert(ks->kc->gss_context != GSS_C_NO_CONTEXT);
1943     maj_stat = gss_unseal(&min_stat, ks->kc->gss_context, enctok, tok,
1944         &conf_state, &qop_state);
1945     if (maj_stat != GSS_S_COMPLETE) {
1946         security_stream_seterror(&ks->secstr, "krb5 decrypt error from %s: %s",
1947             ks->kc->hostname, gss_error(maj_stat, min_stat));
1948         return (-1);
1949     }
1950     return (0);
1951 }
1952 #endif
1953
1954 /*
1955  * Writes out the entire iovec
1956  */
1957 static int
1958 net_writev(fd, iov, iovcnt)
1959     int fd, iovcnt;
1960     struct iovec *iov;
1961 {
1962     int delta, n, total;
1963
1964     assert(iov != NULL);
1965
1966     total = 0;
1967     while (iovcnt > 0) {
1968         /*
1969          * Write the iovec
1970          */
1971         total += n = writev(fd, iov, iovcnt);
1972         if (n < 0)
1973             return (-1);
1974         if (n == 0) {
1975             errno = EIO;
1976             return (-1);
1977         }
1978         /*
1979          * Iterate through each iov.  Figure out what we still need
1980          * to write out.
1981          */
1982         for (; n > 0; iovcnt--, iov++) {
1983             /* 'delta' is the bytes written from this iovec */
1984             delta = n < iov->iov_len ? n : iov->iov_len;
1985             /* subtract from the total num bytes written */
1986             n -= delta;
1987             assert(n >= 0);
1988             /* subtract from this iovec */
1989             iov->iov_len -= delta;
1990             (char *)iov->iov_base += delta;
1991             /* if this iovec isn't empty, run the writev again */
1992             if (iov->iov_len > 0)
1993                 break;
1994         }
1995     }
1996     return (total);
1997 }
1998
1999 /*
2000  * Like read(), but waits until the entire buffer has been filled.
2001  */
2002 static ssize_t
2003 net_read(kc, vbuf, origsize, timeout)
2004     struct krb5_conn *kc;
2005     void *vbuf;
2006     size_t origsize;
2007     int timeout;
2008 {
2009     char *buf = vbuf, *off;     /* ptr arith */
2010     int nread;
2011     size_t size = origsize;
2012
2013     while (size > 0) {
2014         if (kc->readbuf.left == 0) {
2015             if (net_read_fillbuf(kc, timeout) < 0)
2016                 return (-1);
2017             if (kc->readbuf.size == 0)
2018                 return (0);
2019         }
2020         nread = min(kc->readbuf.left, size);
2021         off = kc->readbuf.buf + kc->readbuf.size - kc->readbuf.left;
2022         memcpy(buf, off, nread);
2023
2024         buf += nread;
2025         size -= nread;
2026         kc->readbuf.left -= nread;
2027     }
2028     return ((ssize_t)origsize);
2029 }
2030
2031 /*
2032  * net_read likes to do a lot of little reads.  Buffer it.
2033  */
2034 static int
2035 net_read_fillbuf(kc, timeout)
2036     struct krb5_conn *kc;
2037     int timeout;
2038 {
2039     fd_set readfds;
2040     struct timeval tv;
2041
2042     FD_ZERO(&readfds);
2043     FD_SET(kc->fd, &readfds);
2044     tv.tv_sec = timeout;
2045     tv.tv_usec = 0;
2046     switch (select(kc->fd + 1, &readfds, NULL, NULL, &tv)) {
2047     case 0:
2048         errno = ETIMEDOUT;
2049         /* FALLTHROUGH */
2050     case -1:
2051         return (-1);
2052     case 1:
2053         assert(FD_ISSET(kc->fd, &readfds));
2054         break;
2055     default:
2056         assert(0);
2057         break;
2058     }
2059     kc->readbuf.left = 0;
2060     kc->readbuf.size = read(kc->fd, kc->readbuf.buf,
2061         sizeof(kc->readbuf.buf));
2062 k5printf(("net_read_fillbuf: read %d characters w/ errno %d\n", kc->readbuf.size, errno));
2063     if (kc->readbuf.size < 0)
2064         return (-1);
2065     kc->readbuf.left = kc->readbuf.size;
2066     return (0);
2067 }
2068
2069 /*
2070  * hackish, but you can #undef AMANDA_PRINCIPAL here, and you can both
2071  * hardcode a principal in your build and use the .k5amandahosts.  This is
2072  * available because sites that run pre-releases of amanda 2.5.0 before 
2073  * this feature was there do not behave this way...
2074  */
2075
2076 /*#undef AMANDA_PRINCIPAL*/
2077
2078 /*
2079  * check ~/.k5amandahosts to see if this principal is allowed in.  If it's
2080  * hardcoded, then we don't check the realm
2081  */
2082 static char *
2083 krb5_checkuser(host, name, realm)
2084         char *host, *name, *realm;
2085 {
2086 #ifdef AMANDA_PRINCIPAL
2087     if(strcmp(name, AMANDA_PRINCIPAL) == 0) {
2088         return(NULL);
2089     } else {
2090         return(vstralloc("does not match compiled in default"));
2091     }
2092 #else
2093     struct passwd *pwd;
2094     char *ptmp;
2095     char *result = "generic error";     /* default is to not permit */
2096     FILE *fp = NULL;
2097     struct stat sbuf;
2098     uid_t localuid;
2099     char *line = NULL;
2100     char *filehost = NULL, *fileuser = NULL, *filerealm = NULL;
2101     char n1[NUM_STR_SIZE];
2102     char n2[NUM_STR_SIZE];
2103
2104     assert( host != NULL);
2105     assert( name != NULL);
2106
2107     if((pwd = getpwnam(CLIENT_LOGIN)) == NULL) {
2108         result = vstralloc("can not find user ", CLIENT_LOGIN, NULL);
2109     }
2110     localuid = pwd->pw_uid;
2111
2112 #ifdef USE_AMANDAHOSTS
2113     ptmp = stralloc2(pwd->pw_dir, "/.k5amandahosts");
2114 #else
2115     ptmp = stralloc2(pwd->pw_dir, "/.k5login");
2116 #endif
2117
2118     if(!ptmp) {
2119         result = vstralloc("could not find home directory for ", CLIENT_LOGIN, NULL);
2120         goto common_exit;
2121    }
2122
2123    /*
2124     * check to see if the ptmp file does nto exist.
2125     */
2126    if(access(ptmp, R_OK) == -1 && errno == ENOENT) {
2127         /*
2128          * in this case we check to see if the principal matches
2129          * the destination user mimicing the .k5login functionality.
2130          */
2131          if(strcmp(name, CLIENT_LOGIN) != 0) {
2132                 result = vstralloc(name, " does not match ",
2133                         CLIENT_LOGIN, NULL);
2134                 goto common_exit;
2135         }
2136         result = NULL;
2137         goto common_exit;
2138     }
2139
2140     k5printf(("opening ptmp: %s\n", (ptmp)?ptmp: "NULL!"));
2141     if((fp = fopen(ptmp, "r")) == NULL) {
2142         result = vstralloc("can not open ", ptmp, NULL);
2143         goto common_exit;
2144     }
2145     k5printf(("opened ptmp\n"));
2146
2147     if (fstat(fileno(fp), &sbuf) != 0) {
2148         result = vstralloc("cannot fstat ", ptmp, ": ", strerror(errno), NULL);
2149         goto common_exit;
2150
2151     }
2152
2153     if (sbuf.st_uid != localuid) {
2154         snprintf(n1, sizeof(n1), "%ld", (long) sbuf.st_uid);
2155         snprintf(n2, sizeof(n2), "%ld", (long) localuid);
2156         result = vstralloc(ptmp, ": ",
2157             "owned by id ", n1,
2158             ", should be ", n2,
2159             NULL);
2160         goto common_exit;
2161     }
2162     if ((sbuf.st_mode & 077) != 0) {
2163         result = stralloc2(ptmp,
2164             ": incorrect permissions; file must be accessible only by its owner");
2165         goto common_exit;
2166     }       
2167
2168     while((line = agets(fp)) != NULL) {
2169 #if defined(SHOW_SECURITY_DETAIL)                               /* { */
2170         k5printf(("%s: processing line: <%s>\n", debug_prefix(NULL), line));
2171 #endif                                                          /* } */
2172         /* if there's more than one column, then it's the host */
2173         if( (filehost = strtok(line, " \t")) == NULL) {
2174             amfree(line);
2175             continue;
2176         }
2177
2178         /*
2179          * if there's only one entry, then it's a username and we have
2180          * no hostname.  (so the principal is allowed from anywhere.
2181          */
2182         if((fileuser = strtok(NULL, " \t")) == NULL) {
2183             fileuser = filehost;
2184             filehost = NULL;
2185         }
2186
2187         if(filehost && strcmp(filehost, host) != 0) {
2188             amfree(line);
2189             continue;
2190         } else {
2191                 k5printf(("found a host match\n"));
2192         }
2193
2194         if( (filerealm = strchr(fileuser, '@')) != NULL) {
2195             *filerealm++ = '\0';
2196         }
2197
2198         /*
2199          * we have a match.  We're going to be a little bit insecure
2200          * and indicate that the principal is correct but the realm is
2201          * not if that's the case.  Technically we should say nothing
2202          * and let the user figure it out, but it's helpful for debugging.
2203          * You likely only get this far if you've turned on cross-realm auth
2204          * anyway...
2205          */
2206         k5printf(("comparing %s %s\n", fileuser, name));
2207         if(strcmp(fileuser, name) == 0) {
2208                 k5printf(("found a match!\n"));
2209                 if(realm && filerealm && (strcmp(realm, filerealm)!=0)) {
2210                         amfree(line);
2211                         continue;
2212                 }
2213                 result = NULL;
2214                 goto common_exit;
2215         }
2216
2217         amfree(line);
2218     }
2219
2220     result = vstralloc("no match in ", ptmp, NULL);
2221
2222 common_exit:
2223     if(fp)
2224         afclose(fp);
2225     if(line)
2226         amfree(line);
2227     return(result);
2228 #endif /* AMANDA_PRINCIPAL */
2229 }
2230
2231 #else
2232 void krb5_security_dummy (void) {}
2233 #endif  /* KRB5_SECURITY */