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