- principal_name = conf_fn("krb5principal", arg);
- }
-#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->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 (connq.qlength > AMANDA_KRB5_MAXCONN) {
- k5printf(("krb5_connect: too many conections (%d), delaying %s\n",
- 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(fd, EV_WRITEFD, connect_callback, kh);
- kh->ev_timeout = event_register(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(cookie)
- 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);
- amfree(kh->hostname);
- amfree(kh);
-}
-
-/*
- * Called when a tcp connection is finished connecting and is ready
- * to be authenticated.
- */
-static void
-connect_callback(cookie)
- 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(cookie)
- 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(in, out, fn)
- int in, out;
- void (*fn) P((security_handle_t *, pkt_t *));
-{
- struct sockaddr_in sin;
- size_t len;
- struct krb5_conn *kc;
- struct hostent *he;
-
- /*
- * Make sure we're initted
- */
- init();
-
- 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(hostname)
- const char *hostname;
-{
- struct krb5_conn *kc;
-
- k5printf(("krb5: conn_get: %s\n", hostname));
-
- for (kc = connq_first(); kc != NULL; kc = 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);
- connq_append(kc);
- return (kc);
-}
-
-/*
- * Delete a reference to a connection, and close it if it is the last
- * reference.
- */
-static void
-conn_put(kc)
- 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);
- }
- 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(kc)
- 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(kc->fd, EV_READFD, conn_read_callback, kc);
- kc->ev_read_refcnt = 1;
-}
-
-static void
-conn_read_cancel(kc)
- 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(inst)
- 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 int
-krb5_sendpkt(cookie, pkt)
- void *cookie;
- pkt_t *pkt;
-{
- struct krb5_handle *kh = cookie;
- gss_buffer_desc tok;
- int rval;
- unsigned char c, *buf;
-
- assert(kh != NULL);
- assert(pkt != NULL);
-
- k5printf(("krb5: sendpkt: enter\n"));
-
- if (pkt->body[0] == '\0') {
- c = (unsigned char)pkt->type;
- tok.length = 1;
- tok.value = &c;
- } else {
- tok.length = strlen(pkt->body) + 2;
- tok.value = alloc(tok.length);
- buf = tok.value;
- *buf++ = (unsigned char)pkt->type;
- strncpy(buf, pkt->body, tok.length - 2);
- buf[tok.length - 2] = '\0';