- */
-#define KRB5_STREAM_BUFSIZE (MAX_TAPE_BLOCK_BYTES * 2)
-
-/*
- * This is the max number of outgoing connections we can have at once.
- * planner/amcheck/etc will open a bunch of connections as it tries
- * to contact everything. We need to limit this to avoid blowing
- * the max number of open file descriptors a process can have.
- */
-#define AMANDA_KRB5_MAXCONN 40
-
-/*
- * This is a frame read off of the connection. Each frame has an
- * associated handle and a gss_buffer which contains a len,value pair.
- */
-struct krb5_frame {
- int handle; /* proto handle */
- gss_buffer_desc tok; /* token */
- TAILQ_ENTRY(krb5_frame) tq; /* queue handle */
-};
-
-/*
- * This is a krb5 connection to a host. We should only have
- * one connection per host.
- */
-struct krb5_conn {
- int fd; /* tcp connection */
- struct { /* buffer read() calls */
- char buf[KRB5_STREAM_BUFSIZE]; /* buffer */
- size_t left; /* unread data */
- ssize_t size; /* size of last read */
- } readbuf;
- enum { unauthed, authed } state;
- event_handle_t *ev_read; /* read (EV_READFD) handle */
- int ev_read_refcnt; /* number of readers */
- char hostname[MAX_HOSTNAME_LENGTH+1]; /* human form of above */
- char *errmsg; /* error passed up */
- gss_ctx_id_t gss_context; /* GSSAPI context */
- int refcnt; /* number of handles using */
- TAILQ_HEAD(, krb5_frame) frameq; /* queue of read frames */
- TAILQ_ENTRY(krb5_conn) tq; /* queue handle */
-};
-
-
-struct krb5_stream;
-
-/*
- * This is the private handle data.
- */
-struct krb5_handle {
- security_handle_t sech; /* MUST be first */
- char *hostname; /* ptr to kc->hostname */
- struct krb5_stream *ks; /* virtual stream we xmit over */
-
- union {
- void (*recvpkt)(void *, pkt_t *, security_status_t);
- /* func to call when packet recvd */
- void (*connect)(void *, security_handle_t *, security_status_t);
- /* func to call when connected */
- } fn;
- void *arg; /* argument to pass function */
- void *datap; /* argument to pass function */
- event_handle_t *ev_wait; /* wait handle for connects */
- char *(*conf_fn)(char *, void *); /* used to get config info */
- event_handle_t *ev_timeout; /* timeout handle for recv */
-};
-
-/*
- * This is the internal security_stream data for krb5.
- */
-struct krb5_stream {
- security_stream_t secstr; /* MUST be first */
- struct krb5_conn *kc; /* physical connection */
- int handle; /* protocol handle */
- event_handle_t *ev_read; /* read (EV_WAIT) event handle */
- void (*fn)(void *, void *, ssize_t);/* read event fn */
- void *arg; /* arg for previous */
- char buf[KRB5_STREAM_BUFSIZE];
- ssize_t len;
-};
-
-/*
- * Interface functions
- */
-static ssize_t krb5_sendpkt(void *, pkt_t *);
-static int krb5_stream_accept(void *);
-static int krb5_stream_auth(void *);
-static int krb5_stream_id(void *);
-static int krb5_stream_write(void *, const void *, size_t);
-static void * krb5_stream_client(void *, int);
-static void * krb5_stream_server(void *);
-static void krb5_accept(const struct security_driver *, int, int,
- void (*)(security_handle_t *, pkt_t *));
-static void krb5_close(void *);
-static void krb5_connect(const char *, char *(*)(char *, void *),
- void (*)(void *, security_handle_t *, security_status_t),
- void *, void *);
-static void krb5_recvpkt(void *, void (*)(void *, pkt_t *, security_status_t),
- void *, int);
-static void krb5_recvpkt_cancel(void *);
-static void krb5_stream_close(void *);
-static void krb5_stream_read(void *, void (*)(void *, void *, ssize_t), void *);
-static ssize_t krb5_stream_read_sync(void *, void **);
-static void krb5_stream_read_cancel(void *);
-
-/*
- * This is our interface to the outside world.
- */
-const security_driver_t krb5_security_driver = {
- "krb5",
- krb5_connect,
- krb5_accept,
- krb5_close,
- krb5_sendpkt,
- krb5_recvpkt,
- krb5_recvpkt_cancel,
- krb5_stream_server,
- krb5_stream_accept,
- krb5_stream_client,
- krb5_stream_close,
- krb5_stream_auth,
- krb5_stream_id,
- krb5_stream_write,
- krb5_stream_read,
- krb5_stream_read_sync,
- krb5_stream_read_cancel,
- sec_close_connection_none,
-};
-
-/*
- * Cache the local hostname
- */
-static char hostname[MAX_HOSTNAME_LENGTH+1];
-
-/*
- * This is a queue of open connections
- */
-static struct {
- TAILQ_HEAD(, krb5_conn) tailq;
- int qlength;
-} krb5_connq = {
- TAILQ_HEAD_INITIALIZER(krb5_connq.tailq), 0
-};
-#define krb5_connq_first() TAILQ_FIRST(&krb5_connq.tailq)
-#define krb5_connq_next(kc) TAILQ_NEXT(kc, tq)
-#define krb5_connq_append(kc) do { \
- TAILQ_INSERT_TAIL(&krb5_connq.tailq, kc, tq); \
- krb5_connq.qlength++; \
-} while (0)
-#define krb5_connq_remove(kc) do { \
- assert(krb5_connq.qlength > 0); \
- TAILQ_REMOVE(&krb5_connq.tailq, kc, tq); \
- krb5_connq.qlength--; \
-} while (0)
-
-static int newhandle = 1;
-
-/*
- * This is a function that should be called if a new security_handle_t is
- * created. If NULL, no new handles are created.
- * It is passed the new handle and the received pkt
- */
-static void (*accept_fn)(security_handle_t *, pkt_t *);
-
-/*
- * Local functions
- */
-static void init(void);
-#ifdef BROKEN_MEMORY_CCACHE
-static void cleanup(void);
-#endif
-static const char *get_tgt(char *, char *);
-static void open_callback(void *);
-static void connect_callback(void *);
-static void connect_timeout(void *);
-static int send_token(struct krb5_conn *, int, const gss_buffer_desc *);
-static ssize_t recv_token(struct krb5_conn *, int *, gss_buffer_desc *, int);
-static void recvpkt_callback(void *, void *, ssize_t);
-static void recvpkt_timeout(void *);
-static void stream_read_callback(void *);
-static void stream_read_sync_callback2(void *, void *, ssize_t);
-static int gss_server(struct krb5_conn *);
-static int gss_client(struct krb5_handle *);
-static const char *gss_error(OM_uint32, OM_uint32);
-
-#ifdef AMANDA_KRB5_ENCRYPT
-static int kdecrypt(struct krb5_stream *, gss_buffer_desc *, gss_buffer_desc *);
-static int kencrypt(struct krb5_stream *, gss_buffer_desc *, gss_buffer_desc *);
-#endif
-static struct krb5_conn *conn_get(const char *);
-static void conn_put(struct krb5_conn *);
-static void conn_read(struct krb5_conn *);
-static void conn_read_cancel(struct krb5_conn *);
-static void conn_read_callback(void *);
-static int conn_run_frameq(struct krb5_conn *, struct krb5_stream *);
-static char * krb5_checkuser(char *, char *, char *);
-
-
-/*
- * krb5 version of a security handle allocator. Logically sets
- * up a network "connection".
- */
-static void
-krb5_connect(
- const char *hostname,
- char * (*conf_fn)(char *, void *),
- void (*fn)(void *, security_handle_t *, security_status_t),
- void * arg,
- void * datap)
-{
- struct krb5_handle *kh;
- struct hostent *he;
- struct servent *se;
- int fd;
- int port;
- const char *err;
- char *keytab_name = NULL;
- char *principal_name = NULL;
-
- assert(hostname != NULL);
-
- k5printf(("krb5_connect: %s\n", hostname));
-
- /*
- * Make sure we're initted
- */
- init();
-
- kh = alloc(SIZEOF(*kh));
- security_handleinit(&kh->sech, &krb5_security_driver);
- kh->hostname = NULL;
- kh->ks = NULL;
- kh->ev_wait = NULL;
- kh->ev_timeout = NULL;
-
-#ifdef AMANDA_KEYTAB
- keytab_name = AMANDA_KEYTAB;
-#else
- if(conf_fn) {
- keytab_name = conf_fn("krb5keytab", datap);
- }
-#endif
-#ifdef AMANDA_PRINCIPAL
- principal_name = AMANDA_PRINCIPAL;
-#else
- if(conf_fn) {
- principal_name = conf_fn("krb5principal", datap);
- }
-#endif
-
- if ((err = get_tgt(keytab_name, principal_name)) != NULL) {
- security_seterror(&kh->sech, "%s: could not get TGT: %s",
- hostname, err);
- (*fn)(arg, &kh->sech, S_ERROR);
- return;
- }
-
- if ((he = gethostbyname(hostname)) == NULL) {
- security_seterror(&kh->sech,
- "%s: could not resolve hostname", hostname);
- (*fn)(arg, &kh->sech, S_ERROR);
- return;
- }
- kh->fn.connect = fn;
- kh->conf_fn = conf_fn;
- kh->arg = arg;
- kh->datap = datap;
- kh->hostname = stralloc(he->h_name);
- kh->ks = krb5_stream_client(kh, newhandle++);
-
- if (kh->ks == NULL)
- goto error;
-
- fd = kh->ks->kc->fd;
-
- if (fd < 0) {
- /*
- * We need to open a new connection. See if we have too
- * many connections open.
- */
- if (krb5_connq.qlength > AMANDA_KRB5_MAXCONN) {
- k5printf(("krb5_connect: too many conections (%d), delaying %s\n",
- krb5_connq.qlength, kh->hostname));
- krb5_stream_close(kh->ks);
- kh->ev_wait = event_register((event_id_t)open_callback,
- EV_WAIT, open_callback, kh);
- return;
- }
-
- if ((se = getservbyname(AMANDA_KRB5_SERVICE_NAME, "tcp")) == NULL)
- port = htons(AMANDA_KRB5_DEFAULT_PORT);
- else
- port = se->s_port;
-
- /*
- * Get a non-blocking socket.
- */
- fd = stream_client(kh->hostname, ntohs(port), KRB5_STREAM_BUFSIZE,
- KRB5_STREAM_BUFSIZE, NULL, 1);
- if (fd < 0) {
- security_seterror(&kh->sech,
- "can't connect to %s:%d: %s", hostname, ntohs(port),
- strerror(errno));
- goto error;
- }
- kh->ks->kc->fd = fd;
- }
- /*
- * The socket will be opened async so hosts that are down won't
- * block everything. We need to register a write event
- * so we will know when the socket comes alive.
- * We also register a timeout.
- */
- kh->ev_wait = event_register((event_id_t)fd, EV_WRITEFD,
- connect_callback, kh);
- kh->ev_timeout = event_register((event_id_t)GSS_TIMEOUT, EV_TIME,
- connect_timeout, kh);
-
- return;
-
-error:
- (*fn)(arg, &kh->sech, S_ERROR);
-}
-
-/*
- * Called when there are not too many connections open such that
- * we can open more.
- */
-static void
-open_callback(
- void * cookie)
-{
- struct krb5_handle *kh = cookie;
-
- event_release(kh->ev_wait);
-
- k5printf(("krb5: open_callback: possible connections available, retry %s\n",
- kh->hostname));
- krb5_connect(kh->hostname, kh->conf_fn, kh->fn.connect, kh->arg,kh->datap);
- amfree(kh->hostname);
- amfree(kh);
-}
-
-/*
- * Called when a tcp connection is finished connecting and is ready
- * to be authenticated.
- */
-static void
-connect_callback(
- void * cookie)
-{
- struct krb5_handle *kh = cookie;
-
- event_release(kh->ev_wait);
- kh->ev_wait = NULL;
- event_release(kh->ev_timeout);
- kh->ev_timeout = NULL;
-
- if (kh->ks->kc->state == unauthed) {
- if (gss_client(kh) < 0) {
- (*kh->fn.connect)(kh->arg, &kh->sech, S_ERROR);
- return;
- }
- kh->ks->kc->state = authed;
- }
- assert(kh->ks->kc->gss_context != GSS_C_NO_CONTEXT);
-
- (*kh->fn.connect)(kh->arg, &kh->sech, S_OK);
-}
-
-/*
- * Called if a connection times out before completion.
- */
-static void
-connect_timeout(
- void * cookie)
-{
- struct krb5_handle *kh = cookie;
-
- event_release(kh->ev_wait);
- kh->ev_wait = NULL;
- event_release(kh->ev_timeout);
- kh->ev_timeout = NULL;
-
- (*kh->fn.connect)(kh->arg, &kh->sech, S_TIMEOUT);
-}
-
-/*
- * Setup to handle new incoming connections
- */
-static void
-krb5_accept(
- const struct security_driver *driver,
- int in,
- int out,
- void (*fn)(security_handle_t *, pkt_t *))
-{
- struct sockaddr_in sin;
- socklen_t len;
- struct krb5_conn *kc;
- struct hostent *he;
-
- /*
- * Make sure we're initted
- */
- init();
-
- /* shut up compiler */
- driver=driver;
- out=out;
-
- len = SIZEOF(sin);
- if (getpeername(in, (struct sockaddr *)&sin, &len) < 0)
- return;
- he = gethostbyaddr((void *)&sin.sin_addr, SIZEOF(sin.sin_addr), AF_INET);
- if (he == NULL)
- return;
-
- kc = conn_get(he->h_name);
- kc->fd = in;
- if (gss_server(kc) < 0)
- error("gss_server failed: %s\n", kc->errmsg);
- kc->state = authed;
- accept_fn = fn;
- conn_read(kc);
-}
-
-/*
- * Locate an existing connection to the given host, or create a new,
- * unconnected entry if none exists. The caller is expected to check
- * for the lack of a connection (kc->fd == -1) and set one up.
- */
-static struct krb5_conn *
-conn_get(
- const char * hostname)
-{
- struct krb5_conn *kc;
-
- k5printf(("krb5: conn_get: %s\n", hostname));
-
- for (kc = krb5_connq_first(); kc != NULL; kc = krb5_connq_next(kc)) {
- if (strcasecmp(hostname, kc->hostname) == 0)
- break;
- }
-
- if (kc != NULL) {
- kc->refcnt++;
- k5printf(("krb5: conn_get: exists, refcnt to %s is now %d\n",
- kc->hostname, kc->refcnt));
- return (kc);
- }
-
- k5printf(("krb5: conn_get: creating new handle\n"));
- /*
- * We can't be creating a new handle if we are the client
- */
- assert(accept_fn == NULL);
- kc = alloc(SIZEOF(*kc));
- kc->fd = -1;
- kc->readbuf.left = 0;
- kc->readbuf.size = 0;
- kc->state = unauthed;
- kc->ev_read = NULL;
- strncpy(kc->hostname, hostname, SIZEOF(kc->hostname) - 1);
- kc->hostname[SIZEOF(kc->hostname) - 1] = '\0';
- kc->errmsg = NULL;
- kc->gss_context = GSS_C_NO_CONTEXT;
- /*
- * [XXX] this is set to 2 in order to force the connection to stay
- * open and process more protocol requests. (basically consistant
- * with bsd-security.c, and theoretically krb4-security.c. This
- * needs to be addressed in a cleaner way.
- */
- kc->refcnt = 2;
- TAILQ_INIT(&kc->frameq);
- krb5_connq_append(kc);
- return (kc);
-}
-
-/*
- * Delete a reference to a connection, and close it if it is the last
- * reference.
- */
-static void
-conn_put(
- struct krb5_conn * kc)
-{
- OM_uint32 min_stat;
- struct krb5_frame *kf;
-
- assert(kc->refcnt > 0);
- if (--kc->refcnt > 0) {
- k5printf(("krb5: conn_put: decrementing refcnt for %s to %d\n",
- kc->hostname, kc->refcnt));
- return;
- }
- k5printf(("krb5: conn_put: closing connection to %s\n", kc->hostname));
- if (kc->fd != -1)
- aclose(kc->fd);
- if (kc->ev_read != NULL)
- event_release(kc->ev_read);
- if (kc->errmsg != NULL)
- amfree(kc->errmsg);
- gss_delete_sec_context(&min_stat, &kc->gss_context, GSS_C_NO_BUFFER);
- while ((kf = TAILQ_FIRST(&kc->frameq)) != NULL) {
- TAILQ_REMOVE(&kc->frameq, kf, tq);
- if (kf->tok.value != NULL)
- amfree(kf->tok.value);
- amfree(kf);
- }
- krb5_connq_remove(kc);
- amfree(kc);
- /* signal that a connection is available */
- event_wakeup((event_id_t)open_callback);
-}
-
-/*
- * Turn on read events for a conn. Or, increase a refcnt if we are
- * already receiving read events.
- */
-static void
-conn_read(
- struct krb5_conn *kc)
-{
-
- if (kc->ev_read != NULL) {
- kc->ev_read_refcnt++;
- k5printf(("krb5: conn_read: incremented refcnt to %d for %s\n",
- kc->ev_read_refcnt, kc->hostname));
- return;
- }
- k5printf(("krb5: conn_read registering event handler for %s\n",
- kc->hostname));
- kc->ev_read = event_register((event_id_t)kc->fd, EV_READFD, conn_read_callback, kc);
- kc->ev_read_refcnt = 1;
-}
-
-static void
-conn_read_cancel(
- struct krb5_conn * kc)
-{
-
- if (--kc->ev_read_refcnt > 0) {
- k5printf(("krb5: conn_read_cancel: decremented refcnt to %d for %s\n",
- kc->ev_read_refcnt, kc->hostname));
- return;
- }
- k5printf(("krb5: conn_read_cancel: releasing event handler for %s\n",
- kc->hostname));
- event_release(kc->ev_read);
- kc->ev_read = NULL;
-}
-
-/*
- * frees a handle allocated by the above
- */
-static void
-krb5_close(
- void * inst)
-{
- struct krb5_handle *kh = inst;
-
- assert(kh != NULL);
-
- k5printf(("krb5: closing handle to %s\n", kh->hostname));
-
- if (kh->ks != NULL) {
- /* This may be null if we get here on an error */
- krb5_recvpkt_cancel(kh);
- security_stream_close(&kh->ks->secstr);
- }
- amfree(kh->hostname);
- amfree(kh);
-}
-
-/*
- * Transmit a packet. Encrypt first.
- */
-static ssize_t
-krb5_sendpkt(
- void * cookie,
- pkt_t * pkt)
-{
- struct krb5_handle *kh = cookie;
- gss_buffer_desc tok;
- int rval;
- unsigned char *buf;
-
- assert(kh != NULL);
- assert(pkt != NULL);
-
- k5printf(("krb5: sendpkt: enter\n"));
-
- if (pkt->body[0] == '\0') {
- tok.length = 1;
- tok.value = alloc(SIZEOF(pkt->type));
- memcpy(tok.value, &pkt->type, sizeof(unsigned char));
- } else {
- tok.length = strlen(pkt->body) + 2;
- tok.value = alloc(tok.length);
- buf = tok.value;
- *buf++ = (unsigned char)pkt->type;
- strncpy((char *)buf, pkt->body, tok.length - 2);
- buf[tok.length - 2] = '\0';
- }
-
- k5printf(("krb5: sendpkt: %s (%d) pkt_t (len %d) contains:\n\n\"%s\"\n\n",
- pkt_type2str(pkt->type), pkt->type, strlen(pkt->body), pkt->body));
-
- rval = krb5_stream_write(kh->ks, tok.value, tok.length);
- if (rval < 0)
- security_seterror(&kh->sech, security_stream_geterror(&kh->ks->secstr));
- /*@ignore@*/
- amfree(tok.value);
- /*@end@*/
- return (rval);
-}
-
-/*
- * Set up to receive a packet asyncronously, and call back when
- * it has been read.
- */
-static void
-krb5_recvpkt(
- void * cookie,
- void (*fn)(void *, pkt_t *, security_status_t),
- void * arg,
- int timeout)
-{
- struct krb5_handle *kh = cookie;
-
- assert(kh != NULL);
-
- k5printf(("krb5: recvpkt registered for %s\n", kh->hostname));
-
- /*
- * Reset any pending timeout on this handle
- */
- if (kh->ev_timeout != NULL)
- event_release(kh->ev_timeout);
-
- /*
- * Negative timeouts mean no timeout
- */
- if (timeout < 0)
- kh->ev_timeout = NULL;
- else
- kh->ev_timeout = event_register((event_id_t)timeout, EV_TIME,
- recvpkt_timeout, kh);
-
- kh->fn.recvpkt = fn;
- kh->arg = arg;
- krb5_stream_read(kh->ks, recvpkt_callback, kh);
-}
-
-/*
- * Remove a async receive request from the queue
- */
-static void
-krb5_recvpkt_cancel(
- void * cookie)
-{
- struct krb5_handle *kh = cookie;
-
- k5printf(("krb5: cancelling recvpkt for %s\n", kh->hostname));
-
- assert(kh != NULL);
-
- krb5_stream_read_cancel(kh->ks);
- if (kh->ev_timeout != NULL) {
- event_release(kh->ev_timeout);
- kh->ev_timeout = NULL;
- }
-}
-
-/*
- * This is called when a handle is woken up because data read off of the
- * net is for it.
- */
-static void
-recvpkt_callback(
- void *cookie,
- void *buf,
- ssize_t bufsize)
-{
- pkt_t pkt;
- struct krb5_handle *kh = cookie;
-
- assert(kh != NULL);
-
- /*
- * We need to cancel the recvpkt request before calling
- * the callback because the callback may reschedule us.
- */
- krb5_recvpkt_cancel(kh);
-
- switch (bufsize) {
- case 0:
- security_seterror(&kh->sech,
- "EOF on read from %s", kh->hostname);
- (*kh->fn.recvpkt)(kh->arg, NULL, S_ERROR);
- return;
- case -1:
- security_seterror(&kh->sech, security_stream_geterror(&kh->ks->secstr));
- (*kh->fn.recvpkt)(kh->arg, NULL, S_ERROR);
- return;
- default:
- parse_pkt(&pkt, buf, (size_t)bufsize);
- k5printf(("krb5: received %s pkt (%d) from %s, contains:\n\n\"%s\"\n\n",
- pkt_type2str(pkt.type), pkt.type, kh->hostname, pkt.body));
- (*kh->fn.recvpkt)(kh->arg, &pkt, S_OK);
- return;
- }
-}
-
-/*
- * This is called when a handle times out before receiving a packet.
- */
-static void
-recvpkt_timeout(
- void * cookie)
-{
- struct krb5_handle *kh = cookie;
-
- assert(kh != NULL);
-
- k5printf(("krb5: recvpkt timeout for %s\n", kh->hostname));
-
- krb5_recvpkt_cancel(kh);
- (*kh->fn.recvpkt)(kh->arg, NULL, S_TIMEOUT);
-}
-
-/*
- * Create the server end of a stream. For krb5, this means setup a stream
- * object and allocate a new handle for it.
- */
-static void *
-krb5_stream_server(
- void * h)
-{
- struct krb5_handle *kh = h;
- struct krb5_stream *ks;
-
- assert(kh != NULL);
-
- ks = alloc(SIZEOF(*ks));
- security_streaminit(&ks->secstr, &krb5_security_driver);
- ks->kc = conn_get(kh->hostname);
- /*
- * Stream should already be setup!
- */
- if (ks->kc->fd < 0) {
- conn_put(ks->kc);
- amfree(ks);
- security_seterror(&kh->sech, "lost connection");
- return (NULL);
- }
- /*
- * so as not to conflict with the amanda server's handle numbers,
- * we start at 5000 and work down
- */
- ks->handle = (int)(5000 - newhandle++);
- ks->ev_read = NULL;
- k5printf(("krb5: stream_server: created stream %d\n", ks->handle));
- return (ks);
-}
-
-/*
- * Accept an incoming connection on a stream_server socket
- * Nothing needed for krb5.
- */
-static int
-krb5_stream_accept(
- void * s)
-{
-
- /* shut up compiler */
- s = s;
-
- return (0);
-}
-
-/*
- * Return a connected stream. For krb5, this means setup a stream
- * with the supplied handle.
- */
-static void *
-krb5_stream_client(
- void * h,
- int id)
-{
- struct krb5_handle *kh = h;
- struct krb5_stream *ks;
-
- assert(kh != NULL);
-
- if (id <= 0) {
- security_seterror(&kh->sech,
- "%d: invalid security stream id", id);
- return (NULL);
- }
-
- ks = alloc(SIZEOF(*ks));
- security_streaminit(&ks->secstr, &krb5_security_driver);
- ks->handle = (int)id;
- ks->ev_read = NULL;
- ks->kc = conn_get(kh->hostname);
-
- k5printf(("krb5: stream_client: connected to stream %d\n", id));
-
- return (ks);
-}
-
-/*
- * Close and unallocate resources for a stream.
- */
-static void
-krb5_stream_close(
- void * s)
-{
- struct krb5_stream *ks = s;
-
- assert(ks != NULL);
-
- k5printf(("krb5: stream_close: closing stream %d\n", ks->handle));
-
- krb5_stream_read_cancel(ks);
- conn_put(ks->kc);
- amfree(ks);
-}
-
-/*
- * Authenticate a stream
- * Nothing needed for krb5. The tcp connection is authenticated
- * on startup.
- */
-static int
-krb5_stream_auth(
- void * s)
-{
- /* shut up compiler */
- s = s;
-
- return (0);
-}
-
-/*
- * Returns the stream id for this stream. This is just the local
- * port.
- */
-static int
-krb5_stream_id(
- void * s)
-{
- struct krb5_stream *ks = s;
-
- assert(ks != NULL);
-
- return (ks->handle);
-}
-
-/*
- * Write a chunk of data to a stream. Blocks until completion.
- */
-static int
-krb5_stream_write(
- void * s,
- const void *buf,
- size_t size)
-{
- struct krb5_stream *ks = s;
- gss_buffer_desc tok;
-#ifdef AMANDA_KRB5_ENCRYPT
- gss_buffer_desc enctok;
- OM_uint32 min_stat;
-#endif
- int rc;
-
- assert(ks != NULL);
-
- k5printf(("krb5: stream_write: writing %d bytes to %s:%d\n", size,
- ks->kc->hostname, ks->handle));
-
- tok.length = size;
- tok.value = (void *)buf; /* safe to discard const */
-#ifdef AMANDA_KRB5_ENCRYPT
- if (kencrypt(ks, &tok, &enctok) < 0)
- return (-1);
- rc = send_token(ks->kc, ks->handle, &enctok);
-#else
- rc = send_token(ks->kc, ks->handle, &tok);
-#endif
- if (rc < 0)
- security_stream_seterror(&ks->secstr, ks->kc->errmsg);
-#ifdef AMANDA_KRB5_ENCRYPT
- gss_release_buffer(&min_stat, &enctok);
-#endif
- return (rc);
-}
-
-/*
- * Submit a request to read some data. Calls back with the given
- * function and arg when completed.
- */
-static void
-krb5_stream_read(
- void * s,
- void (*fn)(void *, void *, ssize_t),
- void * arg)
-{
- struct krb5_stream *ks = s;
-
- assert(ks != NULL);
-
- /*
- * Only one read request can be active per stream.
- */
- ks->fn = fn;
- ks->arg = arg;
-
- /*
- * First see if there's any queued frames for this stream.
- * If so, we're done.
- */
- if (conn_run_frameq(ks->kc, ks) > 0)
- return;
-
- if (ks->ev_read == NULL) {
- ks->ev_read = event_register((event_id_t)ks->kc, EV_WAIT,
- stream_read_callback, ks);
- conn_read(ks->kc);
- }
-}