2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1999 University of Maryland
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
28 * $Id: krb5-security.c,v 1.12 2006/02/21 04:13:55 ktill Exp $
30 * krb5-security.c - kerberos V5 security module
44 #define BROKEN_MEMORY_CCACHE
46 #ifdef BROKEN_MEMORY_CCACHE
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
54 #define atexit(func) on_exit(func, 0)
56 #define atexit(func) (you must to resolve lack of atexit)
57 #endif /* HAVE_ON_EXIT */
58 #endif /* ! HAVE_ATEXIT */
61 #ifndef KRB5_HEIMDAL_INCLUDES
62 #include <gssapi/gssapi_generic.h>
64 #include <gssapi/gssapi.h>
68 #ifndef KRB5_ENV_CCNAME
69 #define KRB5_ENV_CCNAME "KRB5CCNAME"
72 /*#define KRB5_DEBUG*/
75 #define k5printf(x) dbprintf(x)
81 * consider undefining when kdestroy() is fixed. The current version does
82 * not work under krb5-1.2.4 in rh7.3, perhaps others.
84 #define KDESTROY_VIA_UNLINK 1
87 * Define this if you want all network traffic encrypted. This will
88 * extract a serious performance hit.
90 * It would be nice if we could do this on a filesystem-by-filesystem basis.
92 /*#define AMANDA_KRB5_ENCRYPT*/
95 * Where the keytab lives, if defined. Otherwise it expects something in the
98 /* #define AMANDA_KEYTAB "/.amanda-v5-keytab" */
101 * The name of the principal we authenticate with, if defined. Otherwise
102 * it expects something in the config file.
104 /* #define AMANDA_PRINCIPAL "service/amanda" */
107 * The lifetime of our tickets in seconds. This may or may not need to be
110 #define AMANDA_TKT_LIFETIME (12*60*60)
113 * The name of the service in /etc/services. This probably shouldn't be
116 #define AMANDA_KRB5_SERVICE_NAME "k5amanda"
119 * The default port to use if above entry in /etc/services doesn't exist
121 #define AMANDA_KRB5_DEFAULT_PORT 10082
124 * The timeout in seconds for each step of the GSS negotiation phase
126 #define GSS_TIMEOUT 30
129 * The largest buffer we can send/receive.
131 #define AMANDA_MAX_TOK_SIZE (MAX_TAPE_BLOCK_BYTES * 4)
134 * Magic values for krb5_conn->handle
136 #define H_EOF -1 /* this connection has been shut down */
139 * This is the tcp stream buffer size
141 #define KRB5_STREAM_BUFSIZE (MAX_TAPE_BLOCK_BYTES * 2)
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.
149 #define AMANDA_KRB5_MAXCONN 40
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.
156 int handle; /* proto handle */
157 gss_buffer_desc tok; /* token */
158 TAILQ_ENTRY(krb5_frame) tq; /* queue handle */
162 * This is a krb5 connection to a host. We should only have
163 * one connection per host.
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 */
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 */
187 * This is the private handle data.
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 */
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 */
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 */
207 * This is the internal security_stream data for krb5.
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 */
219 * Interface functions
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),
240 static void krb5_stream_read_cancel P((void *));
243 * This is our interface to the outside world.
245 const security_driver_t krb5_security_driver = {
261 krb5_stream_read_cancel,
265 * Cache the local hostname
267 static char hostname[MAX_HOSTNAME_LENGTH+1];
270 * This is a queue of open connections
273 TAILQ_HEAD(, krb5_conn) tailq;
276 TAILQ_HEAD_INITIALIZER(connq.tailq), 0
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); \
284 #define connq_remove(kc) do { \
285 assert(connq.qlength > 0); \
286 TAILQ_REMOVE(&connq.tailq, kc, tq); \
290 static int newhandle = 1;
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
297 static void (*accept_fn) P((security_handle_t *, pkt_t *));
302 static void init P((void));
303 #ifdef BROKEN_MEMORY_CCACHE
304 static void cleanup P((void));
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));
319 #ifdef AMANDA_KRB5_ENCRYPT
320 static int kdecrypt P((struct krb5_stream *, gss_buffer_desc *,
322 static int kencrypt P((struct krb5_stream *, gss_buffer_desc *,
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));
339 * krb5 version of a security handle allocator. Logically sets
340 * up a network "connection".
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));
349 struct krb5_handle *kh;
354 char *keytab_name = NULL;
355 char *principal_name = NULL;
357 assert(hostname != NULL);
359 k5printf(("krb5_connect: %s\n", hostname));
362 * Make sure we're initted
366 kh = alloc(sizeof(*kh));
367 security_handleinit(&kh->sech, &krb5_security_driver);
371 kh->ev_timeout = NULL;
374 keytab_name = AMANDA_KEYTAB;
377 keytab_name = conf_fn("krb5keytab", arg);
380 #ifdef AMANDA_PRINCIPAL
381 principal_name = AMANDA_PRINCIPAL;
384 principal_name = conf_fn("krb5principal", arg);
388 if ((err = get_tgt(keytab_name, principal_name)) != NULL) {
389 security_seterror(&kh->sech, "%s: could not get TGT: %s",
391 (*fn)(arg, &kh->sech, S_ERROR);
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);
402 kh->conf_fn = conf_fn;
404 kh->hostname = stralloc(he->h_name);
405 kh->ks = krb5_stream_client(kh, newhandle++);
414 * We need to open a new connection. See if we have too
415 * many connections open.
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);
426 if ((se = getservbyname(AMANDA_KRB5_SERVICE_NAME, "tcp")) == NULL)
427 port = htons(AMANDA_KRB5_DEFAULT_PORT);
432 * Get a non-blocking socket.
434 fd = stream_client(kh->hostname, ntohs(port), KRB5_STREAM_BUFSIZE,
435 KRB5_STREAM_BUFSIZE, NULL, 1);
437 security_seterror(&kh->sech,
438 "can't connect to %s:%d: %s", hostname, ntohs(port),
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.
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);
456 (*fn)(arg, &kh->sech, S_ERROR);
460 * Called when there are not too many connections open such that
464 open_callback(cookie)
467 struct krb5_handle *kh = cookie;
469 event_release(kh->ev_wait);
471 k5printf(("krb5: open_callback: possible connections available, retry %s\n",
473 krb5_connect(kh->hostname, kh->conf_fn, kh->fn.connect, kh->arg);
474 amfree(kh->hostname);
479 * Called when a tcp connection is finished connecting and is ready
480 * to be authenticated.
483 connect_callback(cookie)
486 struct krb5_handle *kh = cookie;
488 event_release(kh->ev_wait);
490 event_release(kh->ev_timeout);
491 kh->ev_timeout = NULL;
493 if (kh->ks->kc->state == unauthed) {
494 if (gss_client(kh) < 0) {
495 (*kh->fn.connect)(kh->arg, &kh->sech, S_ERROR);
498 kh->ks->kc->state = authed;
500 assert(kh->ks->kc->gss_context != GSS_C_NO_CONTEXT);
502 (*kh->fn.connect)(kh->arg, &kh->sech, S_OK);
506 * Called if a connection times out before completion.
509 connect_timeout(cookie)
512 struct krb5_handle *kh = cookie;
514 event_release(kh->ev_wait);
516 event_release(kh->ev_timeout);
517 kh->ev_timeout = NULL;
519 (*kh->fn.connect)(kh->arg, &kh->sech, S_TIMEOUT);
523 * Setup to handle new incoming connections
526 krb5_accept(in, out, fn)
528 void (*fn) P((security_handle_t *, pkt_t *));
530 struct sockaddr_in sin;
532 struct krb5_conn *kc;
536 * Make sure we're initted
541 if (getpeername(in, (struct sockaddr *)&sin, &len) < 0)
543 he = gethostbyaddr((void *)&sin.sin_addr, sizeof(sin.sin_addr), AF_INET);
547 kc = conn_get(he->h_name);
549 if (gss_server(kc) < 0)
550 error("gss_server failed: %s\n", kc->errmsg);
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.
561 static struct krb5_conn *
563 const char *hostname;
565 struct krb5_conn *kc;
567 k5printf(("krb5: conn_get: %s\n", hostname));
569 for (kc = connq_first(); kc != NULL; kc = connq_next(kc)) {
570 if (strcasecmp(hostname, kc->hostname) == 0)
576 k5printf(("krb5: conn_get: exists, refcnt to %s is now %d\n",
577 kc->hostname, kc->refcnt));
581 k5printf(("krb5: conn_get: creating new handle\n"));
583 * We can't be creating a new handle if we are the client
585 assert(accept_fn == NULL);
586 kc = alloc(sizeof(*kc));
588 kc->readbuf.left = 0;
589 kc->readbuf.size = 0;
590 kc->state = unauthed;
592 strncpy(kc->hostname, hostname, sizeof(kc->hostname) - 1);
593 kc->hostname[sizeof(kc->hostname) - 1] = '\0';
595 kc->gss_context = GSS_C_NO_CONTEXT;
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.
603 TAILQ_INIT(&kc->frameq);
609 * Delete a reference to a connection, and close it if it is the last
614 struct krb5_conn *kc;
617 struct krb5_frame *kf;
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));
625 k5printf(("krb5: conn_put: closing connection to %s\n", kc->hostname));
628 if (kc->ev_read != NULL)
629 event_release(kc->ev_read);
630 if (kc->errmsg != NULL)
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);
641 /* signal that a connection is available */
642 event_wakeup((event_id_t)open_callback);
646 * Turn on read events for a conn. Or, increase a refcnt if we are
647 * already receiving read events.
651 struct krb5_conn *kc;
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));
660 k5printf(("krb5: conn_read registering event handler for %s\n",
662 kc->ev_read = event_register(kc->fd, EV_READFD, conn_read_callback, kc);
663 kc->ev_read_refcnt = 1;
668 struct krb5_conn *kc;
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));
676 k5printf(("krb5: conn_read_cancel: releasing event handler for %s\n",
678 event_release(kc->ev_read);
683 * frees a handle allocated by the above
689 struct krb5_handle *kh = inst;
693 k5printf(("krb5: closing handle to %s\n", kh->hostname));
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);
700 amfree(kh->hostname);
705 * Transmit a packet. Encrypt first.
708 krb5_sendpkt(cookie, pkt)
712 struct krb5_handle *kh = cookie;
715 unsigned char c, *buf;
720 k5printf(("krb5: sendpkt: enter\n"));
722 if (pkt->body[0] == '\0') {
723 c = (unsigned char)pkt->type;
727 tok.length = strlen(pkt->body) + 2;
728 tok.value = alloc(tok.length);
730 *buf++ = (unsigned char)pkt->type;
731 strncpy(buf, pkt->body, tok.length - 2);
732 buf[tok.length - 2] = '\0';
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));
738 rval = krb5_stream_write(kh->ks, tok.value, tok.length);
740 security_seterror(&kh->sech, security_stream_geterror(&kh->ks->secstr));
747 * Set up to receive a packet asyncronously, and call back when
751 krb5_recvpkt(cookie, fn, arg, timeout)
753 void (*fn) P((void *, pkt_t *, security_status_t));
756 struct krb5_handle *kh = cookie;
760 k5printf(("krb5: recvpkt registered for %s\n", kh->hostname));
763 * Reset any pending timeout on this handle
765 if (kh->ev_timeout != NULL)
766 event_release(kh->ev_timeout);
769 * Negative timeouts mean no timeout
772 kh->ev_timeout = NULL;
774 kh->ev_timeout = event_register(timeout, EV_TIME, recvpkt_timeout, kh);
778 krb5_stream_read(kh->ks, recvpkt_callback, kh);
782 * Remove a async receive request from the queue
785 krb5_recvpkt_cancel(cookie)
788 struct krb5_handle *kh = cookie;
790 k5printf(("krb5: cancelling recvpkt for %s\n", kh->hostname));
794 krb5_stream_read_cancel(kh->ks);
795 if (kh->ev_timeout != NULL) {
796 event_release(kh->ev_timeout);
797 kh->ev_timeout = NULL;
802 * This is called when a handle is woken up because data read off of the
806 recvpkt_callback(cookie, buf, bufsize)
811 struct krb5_handle *kh = cookie;
816 * We need to cancel the recvpkt request before calling
817 * the callback because the callback may reschedule us.
819 krb5_recvpkt_cancel(kh);
823 security_seterror(&kh->sech,
824 "EOF on read from %s", kh->hostname);
825 (*kh->fn.recvpkt)(kh->arg, NULL, S_ERROR);
828 security_seterror(&kh->sech, security_stream_geterror(&kh->ks->secstr));
829 (*kh->fn.recvpkt)(kh->arg, NULL, S_ERROR);
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);
841 * This is called when a handle times out before receiving a packet.
844 recvpkt_timeout(cookie)
847 struct krb5_handle *kh = cookie;
851 k5printf(("krb5: recvpkt timeout for %s\n", kh->hostname));
853 krb5_recvpkt_cancel(kh);
854 (*kh->fn.recvpkt)(kh->arg, NULL, S_TIMEOUT);
858 * Create the server end of a stream. For krb5, this means setup a stream
859 * object and allocate a new handle for it.
862 krb5_stream_server(h)
865 struct krb5_handle *kh = h;
866 struct krb5_stream *ks;
870 ks = alloc(sizeof(*ks));
871 security_streaminit(&ks->secstr, &krb5_security_driver);
872 ks->kc = conn_get(kh->hostname);
874 * Stream should already be setup!
876 if (ks->kc->fd < 0) {
879 security_seterror(&kh->sech, "lost connection");
883 * so as not to conflict with the amanda server's handle numbers,
884 * we start at 5000 and work down
886 ks->handle = 5000 - newhandle++;
888 k5printf(("krb5: stream_server: created stream %d\n", ks->handle));
893 * Accept an incoming connection on a stream_server socket
894 * Nothing needed for krb5.
897 krb5_stream_accept(s)
905 * Return a connected stream. For krb5, this means setup a stream
906 * with the supplied handle.
909 krb5_stream_client(h, id)
913 struct krb5_handle *kh = h;
914 struct krb5_stream *ks;
919 security_seterror(&kh->sech,
920 "%d: invalid security stream id", id);
924 ks = alloc(sizeof(*ks));
925 security_streaminit(&ks->secstr, &krb5_security_driver);
928 ks->kc = conn_get(kh->hostname);
930 k5printf(("krb5: stream_client: connected to stream %d\n", id));
936 * Close and unallocate resources for a stream.
942 struct krb5_stream *ks = s;
946 k5printf(("krb5: stream_close: closing stream %d\n", ks->handle));
948 krb5_stream_read_cancel(ks);
954 * Authenticate a stream
955 * Nothing needed for krb5. The tcp connection is authenticated
967 * Returns the stream id for this stream. This is just the local
974 struct krb5_stream *ks = s;
982 * Write a chunk of data to a stream. Blocks until completion.
985 krb5_stream_write(s, buf, size)
990 struct krb5_stream *ks = s;
992 #ifdef AMANDA_KRB5_ENCRYPT
993 gss_buffer_desc enctok;
1000 k5printf(("krb5: stream_write: writing %d bytes to %s:%d\n", size,
1001 ks->kc->hostname, ks->handle));
1004 tok.value = (void *)buf; /* safe to discard const */
1005 #ifdef AMANDA_KRB5_ENCRYPT
1006 if (kencrypt(ks, &tok, &enctok) < 0)
1008 rc = send_token(ks->kc, ks->handle, &enctok);
1010 rc = send_token(ks->kc, ks->handle, &tok);
1013 security_stream_seterror(&ks->secstr, ks->kc->errmsg);
1014 #ifdef AMANDA_KRB5_ENCRYPT
1015 gss_release_buffer(&min_stat, &enctok);
1021 * Submit a request to read some data. Calls back with the given
1022 * function and arg when completed.
1025 krb5_stream_read(s, fn, arg)
1027 void (*fn) P((void *, void *, int));
1029 struct krb5_stream *ks = s;
1034 * Only one read request can be active per stream.
1040 * First see if there's any queued frames for this stream.
1041 * If so, we're done.
1043 if (conn_run_frameq(ks->kc, ks) > 0)
1046 if (ks->ev_read == NULL) {
1047 ks->ev_read = event_register((event_id_t)ks->kc, EV_WAIT,
1048 stream_read_callback, ks);
1054 * Cancel a previous stream read request. It's ok if we didn't have a read
1058 krb5_stream_read_cancel(s)
1061 struct krb5_stream *ks = s;
1065 if (ks->ev_read != NULL) {
1066 event_release(ks->ev_read);
1068 conn_read_cancel(ks->kc);
1073 * Callback for krb5_stream_read
1076 stream_read_callback(arg)
1079 struct krb5_stream *ks = arg;
1083 k5printf(("krb5: stream_read_callback: handle %d\n", ks->handle));
1085 conn_run_frameq(ks->kc, ks);
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.
1093 * Returns 1 if a frame was found and processed.
1096 conn_run_frameq(kc, ks)
1097 struct krb5_conn *kc;
1098 struct krb5_stream *ks;
1100 struct krb5_frame *kf, *nextkf;
1101 gss_buffer_desc *enctok, *dectok;
1102 #ifdef AMANDA_KRB5_ENCRYPT
1104 gss_buffer_desc tok;
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.
1113 for (kf = TAILQ_FIRST(&kc->frameq); kf != NULL; kf = nextkf) {
1114 nextkf = TAILQ_NEXT(kf, tq);
1116 if (kf->handle != ks->handle && kf->handle != H_EOF) {
1117 k5printf(("krb5: conn_frameq_run: not for us (handle %d)\n",
1122 * We want all listeners to see the EOF, so never remove it.
1123 * It will get cleaned up when the connection is closed
1126 if (kf->handle != H_EOF)
1127 TAILQ_REMOVE(&kc->frameq, kf, tq);
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.
1134 krb5_stream_read_cancel(ks);
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 */
1145 #ifdef AMANDA_KRB5_ENCRYPT
1147 if (kdecrypt(ks, enctok, &tok) < 0) {
1148 k5printf(("krb5: stream_read_callback: kdecrypt error\n"));
1149 (*ks->fn)(ks->arg, NULL, -1);
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);
1162 amfree(enctok->value);
1164 return (1); /* stop after one frame */
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.
1175 conn_read_callback(cookie)
1178 struct krb5_conn *kc = cookie;
1179 struct krb5_handle *kh;
1180 struct krb5_frame *kf;
1182 gss_buffer_desc *dectok;
1184 #ifdef AMANDA_KRB5_ENCRYPT
1185 gss_buffer_desc tok;
1189 assert(cookie != NULL);
1191 k5printf(("krb5: conn_read_callback\n"));
1193 kf = alloc(sizeof(*kf));
1194 TAILQ_INSERT_TAIL(&kc->frameq, kf, tq);
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",
1201 kf->tok.value = NULL;
1204 rc = event_wakeup((event_id_t)kc);
1205 k5printf(("krb5: conn_read_callback: event_wakeup return %d\n", rc));
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));
1216 * If there is no accept fn registered, then just leave the
1217 * packet queued. The caller may register a function later.
1219 if (accept_fn == NULL) {
1220 k5printf(("krb5: no accept_fn so leaving packet queued.\n"));
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);
1229 kh->ev_timeout = NULL;
1231 TAILQ_REMOVE(&kc->frameq, kf, tq);
1232 k5printf(("krb5: new connection\n"));
1233 #ifdef AMANDA_KRB5_ENCRYPT
1235 rc = kdecrypt(kh->ks, &kf->tok, dectok);
1240 #ifdef AMANDA_KRB5_ENCRYPT
1242 security_seterror(&kh->sech, security_geterror(&kh->ks->secstr));
1243 (*accept_fn)(&kh->sech, NULL);
1247 parse_pkt(&pkt, dectok->value, dectok->length);
1248 #ifdef AMANDA_KRB5_ENCRYPT
1249 gss_release_buffer(&min_stat, dectok);
1251 (*accept_fn)(&kh->sech, &pkt);
1253 amfree(kf->tok.value);
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.
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.
1268 if (accept_fn != NULL)
1270 /* accept_fn = NULL; */
1274 * Negotiate a krb5 gss context from the client end.
1278 struct krb5_handle *kh;
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;
1286 gss_name_t gss_name;
1288 k5printf(("gss_client\n"));
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,
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);
1300 amfree(send_tok.value);
1301 kc->gss_context = GSS_C_NO_CONTEXT;
1304 * Perform the context-establishement loop.
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.
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.
1317 for (recv_tok.length = 0;;) {
1319 maj_stat = gss_init_sec_context(&min_stat,
1320 GSS_C_NO_CREDENTIAL,
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 */
1330 NULL); /* ignore time_rec */
1332 if (recv_tok.length != 0) {
1333 amfree(recv_tok.value);
1334 recv_tok.length = 0;
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));
1345 * Send back the response
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);
1352 gss_release_buffer(&min_stat, &send_tok);
1355 * If we need to continue, then register for more packets
1357 if (maj_stat != GSS_S_CONTINUE_NEEDED)
1360 if ((rc = recv_token(kc, NULL, &recv_tok, GSS_TIMEOUT)) <= 0) {
1362 security_seterror(&kh->sech,
1363 "recv error in gss loop: %s", kc->errmsg);
1365 security_seterror(&kh->sech, "EOF in gss loop");
1372 gss_release_name(&min_stat, &gss_name);
1377 * Negotiate a krb5 gss context from the server end.
1381 struct krb5_conn *kc;
1383 OM_uint32 maj_stat, min_stat, ret_flags;
1384 gss_buffer_desc send_tok, recv_tok;
1386 gss_name_t gss_name;
1387 gss_cred_id_t gss_creds;
1388 char *p, *realm, *msg;
1393 k5printf(("gss_server\n"));
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.
1403 if (getuid() != 0) {
1404 snprintf(errbuf, sizeof(errbuf),
1405 "real uid is %ld, needs to be 0 to read krb5 host key",
1409 if (seteuid(0) < 0) {
1410 snprintf(errbuf, sizeof(errbuf),
1411 "can't seteuid to uid 0: %s", strerror(errno));
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))
1421 maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NULL_OID,
1423 if (maj_stat != GSS_S_COMPLETE) {
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);
1431 amfree(send_tok.value);
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);
1443 gss_release_name(&min_stat, &gss_name);
1445 for (recv_tok.length = 0;;) {
1446 if ((rc = recv_token(kc, NULL, &recv_tok, GSS_TIMEOUT)) <= 0) {
1448 snprintf(errbuf, sizeof(errbuf),
1449 "recv error in gss loop: %s", kc->errmsg);
1452 snprintf(errbuf, sizeof(errbuf), "EOF in gss loop");
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);
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);
1467 amfree(recv_tok.value);
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';
1473 gss_release_buffer(&min_stat, &send_tok);
1476 gss_release_buffer(&min_stat, &send_tok);
1480 * If we need to get more from the client, then register for
1483 if (maj_stat != GSS_S_CONTINUE_NEEDED)
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);
1494 gss_release_name(&min_stat, &gss_name);
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);
1507 * If the principal doesn't match, complain
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);
1515 amfree(send_tok.value);
1521 kc->errmsg = stralloc(errbuf);
1522 k5printf(("gss_server returning %d\n", rval));
1527 * Setup some things about krb5. This should only be called once.
1532 static int beenhere = 0;
1535 int krb5_setenv P((const char *, const char *, int));
1541 #ifndef BROKEN_MEMORY_CCACHE
1542 krb5_setenv(KRB5_ENV_CCNAME, "MEMORY:amanda_ccache", 1);
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.
1550 * We just use a lame ccache scheme with a uid suffix.
1555 snprintf(ccache, sizeof(ccache), "FILE:/tmp/amanda_ccache.%ld.%ld",
1556 (long)geteuid(), (long)getpid());
1557 krb5_setenv(KRB5_ENV_CCNAME, ccache, 1);
1561 gethostname(hostname, sizeof(hostname) - 1);
1562 hostname[sizeof(hostname) - 1] = '\0';
1564 * In case it isn't fully qualified, do a DNS lookup.
1566 if ((he = gethostbyname(hostname)) != NULL)
1567 strncpy(hostname, he->h_name, sizeof(hostname) - 1);
1570 * Lowercase the results. We assume all host/ principals will be
1573 for (p = hostname; *p != '\0'; p++) {
1574 if (isupper((int)*p))
1579 #ifdef BROKEN_MEMORY_CCACHE
1583 #ifdef KDESTROY_VIA_UNLINK
1585 snprintf(ccache, sizeof(ccache), "/tmp/amanda_ccache.%ld.%ld",
1586 (long)geteuid(), (long)getpid());
1595 * Get a ticket granting ticket and stuff it in the cache
1598 get_tgt(keytab_name, principal_name)
1599 char *keytab_name, *principal_name;
1601 krb5_context context;
1602 krb5_error_code ret;
1603 krb5_principal client = NULL, server = NULL;
1605 krb5_keytab keytab = NULL;
1608 krb5_data tgtname = { 0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME };
1609 static char *error = NULL;
1611 if (error != NULL) {
1616 if ((ret = krb5_init_context(&context)) != 0) {
1617 error = vstralloc("error initializing krb5 context: ",
1618 error_message(ret), NULL);
1622 krb5_init_ets(context);
1625 error = vstralloc("error -- no krb5 keytab defined", NULL);
1629 if(!principal_name) {
1630 error = vstralloc("error -- no krb5 principal defined", NULL);
1635 * Resolve keytab file into a keytab object
1637 if ((ret = krb5_kt_resolve(context, keytab_name, &keytab)) != 0) {
1638 error = vstralloc("error resolving keytab ", keytab, ": ",
1639 error_message(ret), NULL);
1644 * Resolve the amanda service held in the keytab into a principal
1647 ret = krb5_parse_name(context, principal_name, &client);
1649 error = vstralloc("error parsing ", principal_name, ": ",
1650 error_message(ret), NULL);
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,
1662 error = vstralloc("error while building server name: ",
1663 error_message(ret), NULL);
1667 ret = krb5_timeofday(context, &now);
1669 error = vstralloc("error getting time of day: ", error_message(ret),
1674 memset(&creds, 0, sizeof(creds));
1675 creds.times.starttime = 0;
1676 creds.times.endtime = now + AMANDA_TKT_LIFETIME;
1678 creds.client = client;
1679 creds.server = server;
1682 * Get a ticket for the service, using the keytab
1684 ret = krb5_get_in_tkt_with_keytab(context, 0, NULL, NULL, NULL,
1685 keytab, 0, &creds, 0);
1688 error = vstralloc("error getting ticket for ", principal_name,
1689 ": ", error_message(ret), NULL);
1693 if ((ret = krb5_cc_default(context, &ccache)) != 0) {
1694 error = vstralloc("error initializing ccache: ", error_message(ret),
1698 if ((ret = krb5_cc_initialize(context, ccache, client)) != 0) {
1699 error = vstralloc("error initializing ccache: ", error_message(ret),
1703 if ((ret = krb5_cc_store_cred(context, ccache, &creds)) != 0) {
1704 error = vstralloc("error storing creds in ccache: ",
1705 error_message(ret), NULL);
1708 krb5_cc_close(context, ccache);
1710 krb5_free_cred_contents(context, &creds);
1713 krb5_free_principal(context, client);
1714 krb5_free_principal(context, server);
1716 krb5_free_context(context);
1721 * get rid of tickets
1725 krb5_context context;
1728 if ((krb5_init_context(&context)) != 0) {
1731 if ((krb5_cc_default(context, &ccache)) != 0) {
1735 krb5_cc_destroy(context, ccache);
1736 krb5_cc_close(context, ccache);
1739 krb5_free_context(context);
1744 parse_pkt(pkt, buf, bufsize)
1749 const unsigned char *bufp = buf;
1751 k5printf(("krb5: parse_pkt: parsing buffer of %d bytes\n", bufsize));
1753 pkt->type = (pktype_t)*bufp++;
1757 pkt->body[0] = '\0';
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';
1765 k5printf(("krb5: parse_pkt: %s (%d): \"%s\"\n", pkt_type2str(pkt->type),
1766 pkt->type, pkt->body));
1771 * Formats an error from the gss api
1774 gss_error(major, minor)
1775 OM_uint32 major, minor;
1777 static gss_buffer_desc msg;
1778 OM_uint32 min_stat, msg_ctx;
1781 gss_release_buffer(&min_stat, &msg);
1784 if (major == GSS_S_FAILURE)
1785 gss_display_status(&min_stat, minor, GSS_C_MECH_CODE, GSS_C_NULL_OID,
1788 gss_display_status(&min_stat, major, GSS_C_GSS_CODE, GSS_C_NULL_OID,
1790 return ((const char *)msg.value);
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.
1799 send_token(kc, handle, tok)
1800 struct krb5_conn *kc;
1802 const gss_buffer_desc *tok;
1804 OM_uint32 netlength, nethandle;
1805 struct iovec iov[3];
1807 k5printf(("krb5: send_token: writing %d bytes to %s\n", tok->length,
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);
1818 * 32 bit length (network byte order)
1819 * 32 bit handle (network byte order)
1822 netlength = htonl(tok->length);
1823 iov[0].iov_base = (void *)&netlength;
1824 iov[0].iov_len = sizeof(netlength);
1826 nethandle = htonl(handle);
1827 iov[1].iov_base = (void *)&nethandle;
1828 iov[1].iov_len = sizeof(nethandle);
1830 iov[2].iov_base = (void *)tok->value;
1831 iov[2].iov_len = tok->length;
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);
1842 recv_token(kc, handle, gtok, timeout)
1843 struct krb5_conn *kc;
1845 gss_buffer_desc *gtok;
1850 assert(kc->fd >= 0);
1851 assert(gtok != NULL);
1853 k5printf(("krb5: recv_token: reading from %s\n", kc->hostname));
1855 switch (net_read(kc, &netint, sizeof(netint), timeout)) {
1857 kc->errmsg = newvstralloc(kc->errmsg, "recv error: ", strerror(errno),
1859 k5printf(("krb5 recv_token error return: %s\n", kc->errmsg));
1867 gtok->length = ntohl(netint);
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));
1875 switch (net_read(kc, &netint, sizeof(netint), timeout)) {
1877 kc->errmsg = newvstralloc(kc->errmsg, "recv error: ", strerror(errno),
1879 k5printf(("krb5 recv_token error return: %s\n", kc->errmsg));
1888 *handle = ntohl(netint);
1890 gtok->value = alloc(gtok->length);
1891 switch (net_read(kc, gtok->value, gtok->length, timeout)) {
1893 kc->errmsg = newvstralloc(kc->errmsg, "recv error: ", strerror(errno),
1895 k5printf(("krb5 recv_token error return: %s\n", kc->errmsg));
1896 amfree(gtok->value);
1899 amfree(gtok->value);
1906 k5printf(("krb5: recv_token: read %d bytes from %s\n", gtok->length,
1908 return (gtok->length);
1911 #ifdef AMANDA_KRB5_ENCRYPT
1913 kencrypt(ks, tok, enctok)
1914 struct krb5_stream *ks;
1915 gss_buffer_desc *tok, *enctok;
1918 OM_uint32 maj_stat, min_stat;
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));
1933 kdecrypt(ks, enctok, tok)
1934 struct krb5_stream *ks;
1935 gss_buffer_desc *enctok, *tok;
1937 OM_uint32 maj_stat, min_stat;
1938 int conf_state, qop_state;
1940 k5printf(("krb5: kdecrypt: decrypting %d bytes\n", enctok->length));
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));
1955 * Writes out the entire iovec
1958 net_writev(fd, iov, iovcnt)
1962 int delta, n, total;
1964 assert(iov != NULL);
1967 while (iovcnt > 0) {
1971 total += n = writev(fd, iov, iovcnt);
1979 * Iterate through each iov. Figure out what we still need
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 */
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)
2000 * Like read(), but waits until the entire buffer has been filled.
2003 net_read(kc, vbuf, origsize, timeout)
2004 struct krb5_conn *kc;
2009 char *buf = vbuf, *off; /* ptr arith */
2011 size_t size = origsize;
2014 if (kc->readbuf.left == 0) {
2015 if (net_read_fillbuf(kc, timeout) < 0)
2017 if (kc->readbuf.size == 0)
2020 nread = min(kc->readbuf.left, size);
2021 off = kc->readbuf.buf + kc->readbuf.size - kc->readbuf.left;
2022 memcpy(buf, off, nread);
2026 kc->readbuf.left -= nread;
2028 return ((ssize_t)origsize);
2032 * net_read likes to do a lot of little reads. Buffer it.
2035 net_read_fillbuf(kc, timeout)
2036 struct krb5_conn *kc;
2043 FD_SET(kc->fd, &readfds);
2044 tv.tv_sec = timeout;
2046 switch (select(kc->fd + 1, &readfds, NULL, NULL, &tv)) {
2053 assert(FD_ISSET(kc->fd, &readfds));
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)
2065 kc->readbuf.left = kc->readbuf.size;
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...
2076 /*#undef AMANDA_PRINCIPAL*/
2079 * check ~/.k5amandahosts to see if this principal is allowed in. If it's
2080 * hardcoded, then we don't check the realm
2083 krb5_checkuser(host, name, realm)
2084 char *host, *name, *realm;
2086 #ifdef AMANDA_PRINCIPAL
2087 if(strcmp(name, AMANDA_PRINCIPAL) == 0) {
2090 return(vstralloc("does not match compiled in default"));
2095 char *result = "generic error"; /* default is to not permit */
2100 char *filehost = NULL, *fileuser = NULL, *filerealm = NULL;
2101 char n1[NUM_STR_SIZE];
2102 char n2[NUM_STR_SIZE];
2104 assert( host != NULL);
2105 assert( name != NULL);
2107 if((pwd = getpwnam(CLIENT_LOGIN)) == NULL) {
2108 result = vstralloc("can not find user ", CLIENT_LOGIN, NULL);
2110 localuid = pwd->pw_uid;
2112 #ifdef USE_AMANDAHOSTS
2113 ptmp = stralloc2(pwd->pw_dir, "/.k5amandahosts");
2115 ptmp = stralloc2(pwd->pw_dir, "/.k5login");
2119 result = vstralloc("could not find home directory for ", CLIENT_LOGIN, NULL);
2124 * check to see if the ptmp file does nto exist.
2126 if(access(ptmp, R_OK) == -1 && errno == ENOENT) {
2128 * in this case we check to see if the principal matches
2129 * the destination user mimicing the .k5login functionality.
2131 if(strcmp(name, CLIENT_LOGIN) != 0) {
2132 result = vstralloc(name, " does not match ",
2133 CLIENT_LOGIN, NULL);
2140 k5printf(("opening ptmp: %s\n", (ptmp)?ptmp: "NULL!"));
2141 if((fp = fopen(ptmp, "r")) == NULL) {
2142 result = vstralloc("can not open ", ptmp, NULL);
2145 k5printf(("opened ptmp\n"));
2147 if (fstat(fileno(fp), &sbuf) != 0) {
2148 result = vstralloc("cannot fstat ", ptmp, ": ", strerror(errno), NULL);
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, ": ",
2162 if ((sbuf.st_mode & 077) != 0) {
2163 result = stralloc2(ptmp,
2164 ": incorrect permissions; file must be accessible only by its owner");
2168 while((line = agets(fp)) != NULL) {
2169 #if defined(SHOW_SECURITY_DETAIL) /* { */
2170 k5printf(("%s: processing line: <%s>\n", debug_prefix(NULL), line));
2172 /* if there's more than one column, then it's the host */
2173 if( (filehost = strtok(line, " \t")) == NULL) {
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.
2182 if((fileuser = strtok(NULL, " \t")) == NULL) {
2183 fileuser = filehost;
2187 if(filehost && strcmp(filehost, host) != 0) {
2191 k5printf(("found a host match\n"));
2194 if( (filerealm = strchr(fileuser, '@')) != NULL) {
2195 *filerealm++ = '\0';
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
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)) {
2220 result = vstralloc("no match in ", ptmp, NULL);
2228 #endif /* AMANDA_PRINCIPAL */
2232 void krb5_security_dummy (void) {}
2233 #endif /* KRB5_SECURITY */