summaryrefslogtreecommitdiffstats
path: root/crypto/kerberosIV/server/kerberos.c
diff options
context:
space:
mode:
authormarkm <markm@FreeBSD.org>1997-09-04 06:04:33 +0000
committermarkm <markm@FreeBSD.org>1997-09-04 06:04:33 +0000
commita8a89cfaf983bc64f4b42f7c35209a5a36dd0fe8 (patch)
tree0b84977f19022a965f8c6145f067f951173f6290 /crypto/kerberosIV/server/kerberos.c
downloadFreeBSD-src-a8a89cfaf983bc64f4b42f7c35209a5a36dd0fe8.zip
FreeBSD-src-a8a89cfaf983bc64f4b42f7c35209a5a36dd0fe8.tar.gz
Initial import of KTH eBones. This has been cleaned up to only include
the "core" Kerberos functionality. The rest of the userland will get their own changes later.
Diffstat (limited to 'crypto/kerberosIV/server/kerberos.c')
-rw-r--r--crypto/kerberosIV/server/kerberos.c846
1 files changed, 846 insertions, 0 deletions
diff --git a/crypto/kerberosIV/server/kerberos.c b/crypto/kerberosIV/server/kerberos.c
new file mode 100644
index 0000000..405e48d
--- /dev/null
+++ b/crypto/kerberosIV/server/kerberos.c
@@ -0,0 +1,846 @@
+/*
+ * Copyright 1985, 1986, 1987, 1988 by the Massachusetts Institute
+ * of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ */
+
+#include "config.h"
+#include "protos.h"
+
+RCSID("$Id: kerberos.c,v 1.64 1997/05/20 18:40:46 bg Exp $");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#elif defined(HAVE_SYS_TIME_H)
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if defined(HAVE_SYS_IOCTL_H) && SunOS != 4
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif /* HAVE_SYS_FILIO_H */
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <err.h>
+
+#ifdef SOCKS
+#include <socks.h>
+#endif
+
+#include <roken.h>
+
+#include <des.h>
+#include <krb.h>
+#include <krb_db.h>
+#include <prot.h>
+#include <klog.h>
+
+#include <kdc.h>
+
+static des_key_schedule master_key_schedule;
+static des_cblock master_key;
+
+static struct timeval kerb_time;
+static u_char master_key_version;
+static char k_instance[INST_SZ];
+static char *lt;
+static int more;
+
+static int mflag; /* Are we invoked manually? */
+static char *log_file; /* name of alt. log file */
+static int nflag; /* don't check max age */
+static int rflag; /* alternate realm specified */
+
+/* fields within the received request packet */
+static char *req_name_ptr;
+static char *req_inst_ptr;
+static char *req_realm_ptr;
+static u_int32_t req_time_ws;
+
+static char local_realm[REALM_SZ];
+
+/* options */
+static int max_age = -1;
+static int pause_int = -1;
+
+/*
+ * Print usage message and exit.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s [-s] [-m] [-n] [-p pause_seconds]"
+ " [-a max_age] [-l log_file] [-r realm] [database_pathname]\n",
+ __progname);
+ exit(1);
+}
+
+/*
+ * kerb_err_reply creates an error reply packet and sends it to the
+ * client.
+ */
+
+static void
+kerb_err_reply(int f, struct sockaddr_in *client, int err, char *string)
+{
+ static KTEXT_ST e_pkt_st;
+ KTEXT e_pkt = &e_pkt_st;
+ static char e_msg[128];
+
+ strcpy(e_msg, "\nKerberos error -- ");
+ strcat(e_msg, string);
+ cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr,
+ req_time_ws, err, e_msg);
+ sendto(f, (char*)e_pkt->dat, e_pkt->length, 0, (struct sockaddr *)client,
+ sizeof(*client));
+}
+
+static void
+hang(void)
+{
+ if (pause_int == -1) {
+ klog(L_KRB_PERR, "Kerberos will pause so as not to loop init");
+ for (;;)
+ pause();
+ } else {
+ char buf[256];
+ snprintf(buf, sizeof(buf),
+ "Kerberos will wait %d seconds before dying so as not to loop init",
+ pause_int);
+ klog(L_KRB_PERR, buf);
+ sleep(pause_int);
+ klog(L_KRB_PERR, "Do svedania....\n");
+ exit(1);
+ }
+}
+
+static int
+check_princ(char *p_name, char *instance, unsigned int lifetime, Principal *p)
+{
+ static int n;
+ static int more;
+
+ n = kerb_get_principal(p_name, instance, p, 1, &more);
+
+ if (n < 0) {
+ lt = klog(L_KRB_PERR, "Database unavailable!");
+ hang();
+ }
+
+ /*
+ * if more than one p_name, pick one, randomly create a session key,
+ * compute maximum lifetime, lookup authorizations if applicable,
+ * and stuff into cipher.
+ */
+ if (n == 0) {
+ /* service unknown, log error, skip to next request */
+ lt = klog(L_ERR_UNK, "UNKNOWN %s.%s", p_name, instance);
+ return KERB_ERR_PRINCIPAL_UNKNOWN;
+ }
+ if (more) {
+ /* not unique, log error */
+ lt = klog(L_ERR_NUN, "Principal not unique %s.%s", p_name, instance);
+ return KERB_ERR_PRINCIPAL_NOT_UNIQUE;
+ }
+ /* If the user's key is null, we want to return an error */
+ if ((p->key_low == 0) && (p->key_high == 0)) {
+ /* User has a null key */
+ lt = klog(L_ERR_NKY, "Null key %s.%s", p_name, instance);
+ return KERB_ERR_NULL_KEY;
+ }
+ if (master_key_version != p->kdc_key_ver) {
+ /* log error reply */
+ lt = klog(L_ERR_MKV,
+ "Incorrect master key version for %s.%s: %d (should be %d)",
+ p->name, p->instance, p->kdc_key_ver, master_key_version);
+ return KERB_ERR_NAME_MAST_KEY_VER;
+ }
+ /* make sure the service hasn't expired */
+ if ((u_int32_t) p->exp_date < (u_int32_t) kerb_time.tv_sec) {
+ /* service did expire, log it */
+ time_t t = p->exp_date;
+ lt = klog(L_ERR_SEXP,
+ "Principal %s.%s expired at %s", p->name, p->instance,
+ krb_stime(&t));
+ return KERB_ERR_NAME_EXP;
+ }
+ /* ok is zero */
+ return 0;
+}
+
+static void
+unseal(des_cblock *key)
+{
+ kdb_encrypt_key(key, key, &master_key, master_key_schedule, DES_DECRYPT);
+}
+
+
+/* Set the key for krb_rd_req so we can check tgt */
+static int
+set_tgtkey(char *r)
+ /* Realm for desired key */
+{
+ int n;
+ static char lastrealm[REALM_SZ];
+ Principal p_st;
+ Principal *p = &p_st;
+ des_cblock key;
+
+ if (!strcmp(lastrealm, r))
+ return (KSUCCESS);
+
+ klog(L_ALL_REQ, "Getting key for %s", r);
+
+ n = kerb_get_principal(KRB_TICKET_GRANTING_TICKET, r, p, 1, &more);
+ if (n == 0)
+ return (KFAILURE);
+
+ /* unseal tgt key from master key */
+ copy_to_key(&p->key_low, &p->key_high, key);
+ unseal(&key);
+ krb_set_key(key, 0);
+ strcpy(lastrealm, r);
+ return (KSUCCESS);
+}
+
+
+static int
+kerberos(unsigned char *buf, int len, struct in_addr client, KTEXT rpkt)
+{
+ int pvno;
+ int msg_type;
+ int lsb;
+ int life;
+ int flags = 0;
+ char name[ANAME_SZ], inst[INST_SZ], realm[REALM_SZ];
+ char service[SNAME_SZ], sinst[INST_SZ];
+ u_int32_t req_time;
+ static KTEXT_ST ticket, cipher, adat;
+ KTEXT tk = &ticket, ciph = &cipher, auth = &adat;
+ AUTH_DAT ad;
+ des_cblock session, key;
+ int err;
+ Principal a_name, s_name;
+
+ char *msg;
+
+
+ unsigned char *p = buf;
+ if(len < 2){
+ strcpy((char*)rpkt->dat, "Packet too short");
+ return KFAILURE;
+ }
+
+ gettimeofday(&kerb_time, NULL);
+
+ pvno = *p++;
+ if(pvno != KRB_PROT_VERSION){
+ msg = klog(L_KRB_PERR, "KRB protocol version mismatch (%d)", pvno);
+ strcpy((char*)rpkt->dat, msg);
+ return KERB_ERR_PKT_VER;
+ }
+ msg_type = *p++;
+ lsb = msg_type & 1;
+ msg_type &= ~1;
+ switch(msg_type){
+ case AUTH_MSG_KDC_REQUEST:
+ /* XXX range check */
+ p += krb_get_nir(p, name, inst, realm);
+ p += krb_get_int(p, &req_time, 4, lsb);
+ life = *p++;
+ p += krb_get_nir(p, service, sinst, NULL);
+ klog(L_INI_REQ, "AS REQ %s.%s@%s for %s.%s from %s",
+ name, inst, realm, service, sinst, inet_ntoa(client));
+ if((err = check_princ(name, inst, 0, &a_name))){
+ strcpy((char*)rpkt->dat, krb_get_err_text(err));
+ return err;
+ }
+ tk->length = 0;
+ if((err = check_princ(service, sinst, 0, &s_name))){
+ strcpy((char*)rpkt->dat, krb_get_err_text(err));
+ return err;
+ }
+ life = min(life, s_name.max_life);
+ life = min(life, a_name.max_life);
+
+ des_new_random_key(&session);
+ copy_to_key(&s_name.key_low, &s_name.key_high, key);
+ unseal(&key);
+ krb_create_ticket(tk, flags, a_name.name, a_name.instance,
+ local_realm, client.s_addr, session,
+ life, kerb_time.tv_sec,
+ s_name.name, s_name.instance, &key);
+ copy_to_key(&a_name.key_low, &a_name.key_high, key);
+ unseal(&key);
+ create_ciph(ciph, session, s_name.name, s_name.instance,
+ local_realm, life, s_name.key_version, tk,
+ kerb_time.tv_sec, &key);
+ memset(&session, 0, sizeof(session));
+ memset(&key, 0, sizeof(key));
+ {
+ KTEXT r;
+ r = create_auth_reply(name, inst, realm, req_time, 0,
+ a_name.exp_date, a_name.key_version, ciph);
+ memcpy(rpkt, r, sizeof(*rpkt));
+ }
+ return 0;
+ case AUTH_MSG_APPL_REQUEST:
+ strcpy(realm, (char*)buf + 3);
+ if((err = set_tgtkey(realm))){
+ msg = klog(L_ERR_UNK, "Unknown realm %s from %s",
+ realm, inet_ntoa(client));
+ strcpy((char*)rpkt->dat, msg);
+ return err;
+ }
+ p = buf + strlen(realm) + 4;
+ p = p + p[0] + p[1] + 2;
+ auth->length = p - buf;
+ memcpy(auth->dat, buf, auth->length);
+ err = krb_rd_req(auth, KRB_TICKET_GRANTING_TICKET,
+ realm, client.s_addr, &ad, 0);
+ if(err){
+ msg = klog(L_ERR_UNK, "krb_rd_req from %s: %s",
+ inet_ntoa(client), krb_get_err_text(err));
+ strcpy((char*)rpkt->dat, msg);
+ return err;
+ }
+ p += krb_get_int(p, &req_time, 4, lsb);
+ life = *p++;
+ p += krb_get_nir(p, service, sinst, NULL);
+ klog(L_APPL_REQ, "APPL REQ %s.%s@%s for %s.%s from %s",
+ ad.pname, ad.pinst, ad.prealm,
+ service, sinst,
+ inet_ntoa(client));
+ if(strcmp(ad.prealm, realm)){
+ msg = klog(L_ERR_UNK, "Can't hop realms: %s -> %s",
+ realm, ad.prealm);
+ strcpy((char*)rpkt->dat, msg);
+ return KERB_ERR_PRINCIPAL_UNKNOWN;
+ }
+
+ if(!strcmp(service, "changepw")){
+ strcpy((char*)rpkt->dat,
+ "Can't authorize password changed based on TGT");
+ return KERB_ERR_PRINCIPAL_UNKNOWN;
+ }
+
+ err = check_princ(service, sinst, life, &s_name);
+ if(err){
+ strcpy((char*)rpkt->dat, krb_get_err_text(err));
+ return err;
+ }
+ life = min(life,
+ krb_time_to_life(kerb_time.tv_sec,
+ krb_life_to_time(ad.time_sec,
+ ad.life)));
+ life = min(life, s_name.max_life);
+ copy_to_key(&s_name.key_low, &s_name.key_high, key);
+ unseal(&key);
+ des_new_random_key(&session);
+ krb_create_ticket(tk, flags, ad.pname, ad.pinst, ad.prealm,
+ client.s_addr, &session, life, kerb_time.tv_sec,
+ s_name.name, s_name.instance,
+ &key);
+
+ memset(&key, 0, sizeof(key));
+
+ create_ciph(ciph, session, service, sinst, local_realm,
+ life, s_name.key_version, tk,
+ kerb_time.tv_sec, &ad.session);
+
+ memset(&session, 0, sizeof(session));
+ memset(ad.session, 0, sizeof(ad.session));
+ {
+ KTEXT r;
+ r =create_auth_reply(ad.pname, ad.pinst, ad.prealm,
+ req_time, 0, 0, 0, ciph);
+ memcpy(rpkt, r, sizeof(*rpkt));
+ }
+ memset(&s_name, 0, sizeof(s_name));
+ return 0;
+
+ case AUTH_MSG_ERR_REPLY:
+ return -1;
+ default:
+ msg = klog(L_KRB_PERR, "Unknown message type: %d from %s",
+ msg_type, inet_ntoa(client));
+ strcpy((char*)rpkt->dat, msg);
+ return KFAILURE;
+ }
+}
+
+
+static void
+kerberos_wrap(int s, KTEXT data, struct sockaddr_in *client)
+{
+ KTEXT_ST pkt;
+ int err = kerberos(data->dat, data->length, client->sin_addr, &pkt);
+ if(err == -1)
+ return;
+ if(err){
+ kerb_err_reply(s, client, err, (char*)pkt.dat);
+ return;
+ }
+ sendto(s, pkt.dat, pkt.length, 0, (struct sockaddr *)client,
+ sizeof(*client));
+}
+
+
+/*
+ * setup_disc
+ *
+ * disconnect all descriptors, remove ourself from the process
+ * group that spawned us.
+ */
+
+static void
+setup_disc(void)
+{
+ int s;
+
+ for (s = 0; s < 3; s++) {
+ close(s);
+ }
+
+ open("/dev/null", 0);
+ dup2(0, 1);
+ dup2(0, 2);
+
+ setsid();
+
+ chdir("/tmp");
+ return;
+}
+
+/*
+ * Make sure that database isn't stale.
+ *
+ * Exit if it is; we don't want to tell lies.
+ */
+
+static void
+check_db_age(void)
+{
+ long age;
+
+ if (max_age != -1) {
+ /* Requires existance of kerb_get_db_age() */
+ gettimeofday(&kerb_time, 0);
+ age = kerb_get_db_age();
+ if (age == 0) {
+ klog(L_KRB_PERR, "Database currently being updated!");
+ hang();
+ }
+ if ((age + max_age) < kerb_time.tv_sec) {
+ klog(L_KRB_PERR, "Database out of date!");
+ hang();
+ /* NOTREACHED */
+ }
+ }
+}
+
+struct descr{
+ int s;
+ KTEXT_ST buf;
+ int type;
+ int timeout;
+};
+
+static void
+mksocket(struct descr *d, struct in_addr addr, int type,
+ const char *service, int port)
+{
+ struct sockaddr_in sina;
+ int on = 1;
+ int sock;
+
+ memset(d, 0, sizeof(struct descr));
+ if ((sock = socket(AF_INET, type, 0)) < 0)
+ err (1, "socket");
+#if defined(SO_REUSEADDR) && defined(HAVE_SETSOCKOPT)
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
+ sizeof(on)) < 0)
+ warn ("setsockopt (SO_REUSEADDR)");
+#endif
+ memset(&sina, 0, sizeof(sina));
+ sina.sin_family = AF_INET;
+ sina.sin_port = port;
+ sina.sin_addr = addr;
+ if (bind(sock, (struct sockaddr *)&sina, sizeof(sina)) < 0)
+ err (1, "bind '%s/%s' (%d)",
+ service, (type == SOCK_DGRAM) ? "udp" : "tcp",
+ ntohs(sina.sin_port));
+
+ if(type == SOCK_STREAM)
+ listen(sock, SOMAXCONN);
+ d->s = sock;
+ d->type = type;
+}
+
+
+static void loop(struct descr *fds, int maxfd);
+
+int
+main(int argc, char **argv)
+{
+ int child;
+ int c;
+ struct descr *fds;
+ int nfds;
+ int i;
+ int n;
+ int kerror;
+
+ umask(077); /* Create protected files */
+
+ set_progname (argv[0]);
+
+ while ((c = getopt(argc, argv, "snmp:a:l:r:")) != EOF) {
+ switch(c) {
+ case 's':
+ /*
+ * Set parameters to slave server defaults.
+ */
+ if (max_age == -1 && !nflag)
+ max_age = ONE_DAY; /* 24 hours */
+ if (pause_int == -1)
+ pause_int = FIVE_MINUTES; /* 5 minutes */
+#if 0
+ if (log_file == NULL) {
+ /* this is only silly */
+ log_file = KRBSLAVELOG;
+ }
+#endif
+ break;
+ case 'n':
+ max_age = -1; /* don't check max age. */
+ nflag++;
+ break;
+ case 'm':
+ mflag++; /* running manually; prompt for master key */
+ break;
+ case 'p':
+ /* Set pause interval. */
+ if (!isdigit(optarg[0]))
+ usage();
+ pause_int = atoi(optarg);
+ if ((pause_int < 5) || (pause_int > ONE_HOUR)) {
+ fprintf(stderr, "pause_int must be between 5 and 3600 seconds.\n");
+ usage();
+ }
+ break;
+ case 'a':
+ /* Set max age. */
+ if (!isdigit(optarg[0]))
+ usage();
+ max_age = atoi(optarg);
+ if ((max_age < ONE_HOUR) || (max_age > THREE_DAYS)) {
+ fprintf(stderr, "max_age must be between one hour and three days, in seconds\n");
+ usage();
+ }
+ break;
+ case 'l':
+ /* Set alternate log file */
+ log_file = optarg;
+ break;
+ case 'r':
+ /* Set realm name */
+ rflag++;
+ strcpy(local_realm, optarg);
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(log_file == NULL)
+ log_file = KRBLOG;
+
+ if (optind == (argc-1)) {
+ if (kerb_db_set_name(argv[optind]) != 0) {
+ fprintf(stderr, "Could not set alternate database name\n");
+ exit(1);
+ }
+ optind++;
+ }
+
+ if (optind != argc)
+ usage();
+
+ printf("Kerberos server starting\n");
+
+ if ((!nflag) && (max_age != -1))
+ printf("\tMaximum database age: %d seconds\n", max_age);
+ if (pause_int != -1)
+ printf("\tSleep for %d seconds on error\n", pause_int);
+ else
+ printf("\tSleep forever on error\n");
+ if (mflag)
+ printf("\tMaster key will be entered manually\n");
+
+ printf("\tLog file is %s\n", log_file);
+
+ kset_logfile(log_file);
+
+ /* find our hostname, and use it as the instance */
+ if (k_gethostname(k_instance, INST_SZ))
+ err (1, "gethostname");
+
+ /*
+ * Yes this looks backwards but it has to be this way to enable a
+ * smooth migration to the new port 88.
+ */
+ {
+ int p1, p2;
+ struct in_addr *a;
+
+ p1 = k_getportbyname ("kerberos-iv", "udp", htons(750));
+ p2 = k_getportbyname ("kerberos-sec", "udp", htons(88));
+
+ if (p1 == p2)
+ {
+ fprintf(stderr, "Either define kerberos-iv/udp as 750\n");
+ fprintf(stderr, " and kerberos-sec/udp as 88\n");
+ fprintf(stderr, "or the other way around!");
+ exit(1);
+ }
+
+ nfds = k_get_all_addrs (&a);
+ if (nfds < 0) {
+ struct in_addr any;
+
+ any.s_addr = INADDR_ANY;
+
+ fprintf (stderr, "Could not get local addresses, "
+ "binding to INADDR_ANY\n");
+ nfds = 1;
+ a = malloc(sizeof(*a) * nfds);
+ memcpy(a, &any, sizeof(struct in_addr));
+ }
+ nfds *= 4;
+ fds = (struct descr*)malloc(nfds * sizeof(struct descr));
+ for (i = 0; i < nfds/4; i++) {
+ mksocket(fds + 4 * i + 0, a[i], SOCK_DGRAM, "kerberos-iv", p1);
+ mksocket(fds + 4 * i + 1, a[i], SOCK_DGRAM, "kerberos-sec", p2);
+ mksocket(fds + 4 * i + 2, a[i], SOCK_STREAM, "kerberos-iv", p1);
+ mksocket(fds + 4 * i + 3, a[i], SOCK_STREAM, "kerberos-sec", p2);
+ }
+ free (a);
+ }
+ /* do all the database and cache inits */
+ if ((n = kerb_init())) {
+ if (mflag) {
+ printf("Kerberos db and cache init ");
+ printf("failed = %d ...exiting\n", n);
+ exit (1);
+ } else {
+ klog(L_KRB_PERR,
+ "Kerberos db and cache init failed = %d ...exiting", n);
+ hang();
+ }
+ }
+
+ /* Make sure database isn't stale */
+ check_db_age();
+
+ /* setup master key */
+ if (kdb_get_master_key (mflag, &master_key, master_key_schedule) != 0) {
+ klog (L_KRB_PERR, "kerberos: couldn't get master key.\n");
+ exit (1);
+ }
+ kerror = kdb_verify_master_key (&master_key, master_key_schedule, stdout);
+ if (kerror < 0) {
+ klog (L_KRB_PERR, "Can't verify master key.");
+ memset(master_key, 0, sizeof (master_key));
+ memset (master_key_schedule, 0, sizeof (master_key_schedule));
+ exit (1);
+ }
+
+ master_key_version = (u_char) kerror;
+
+ fprintf(stdout, "\nCurrent Kerberos master key version is %d\n",
+ master_key_version);
+ des_init_random_number_generator(&master_key);
+
+ if (!rflag) {
+ /* Look up our local realm */
+ krb_get_lrealm(local_realm, 1);
+ }
+ fprintf(stdout, "Local realm: %s\n", local_realm);
+ fflush(stdout);
+
+ if (set_tgtkey(local_realm)) {
+ /* Ticket granting service unknown */
+ klog(L_KRB_PERR, "Ticket granting ticket service unknown");
+ fprintf(stderr, "Ticket granting ticket service unknown\n");
+ exit(1);
+ }
+ if (mflag) {
+ if ((child = fork()) != 0) {
+ printf("Kerberos started, PID=%d\n", child);
+ exit(0);
+ }
+ setup_disc();
+ }
+
+ klog(L_ALL_REQ, "Starting Kerberos for %s (kvno %d)",
+ local_realm, master_key_version);
+
+ /* receive loop */
+ loop(fds, nfds);
+ exit(1);
+}
+
+
+static void
+loop(struct descr *fds, int nfds)
+{
+ for (;;) {
+ int ret;
+ fd_set readfds;
+ struct timeval tv;
+ int maxfd = 0;
+ struct descr *n, *minfree;
+
+ FD_ZERO(&readfds);
+ gettimeofday(&tv, NULL);
+ maxfd = 0;
+ minfree = NULL;
+ /* Remove expired TCP sockets, and add all other
+ to the set we are selecting on */
+ for(n = fds; n < fds + nfds; n++){
+ if(n->s >= 0 && n->timeout && tv.tv_sec > n->timeout){
+ kerb_err_reply(n->s, NULL, KERB_ERR_TIMEOUT, "Timeout");
+ close(n->s);
+ n->s = -1;
+ }
+ if(n->s < 0){
+ if(minfree == NULL) minfree = n;
+ continue;
+ }
+ FD_SET(n->s, &readfds);
+ maxfd = max(maxfd, n->s);
+ }
+ /* add more space for sockets */
+ if(minfree == NULL){
+ int i = nfds;
+ struct descr *new;
+ nfds *=2;
+ new = realloc(fds, sizeof(struct descr) * nfds);
+ if(new){
+ fds = new;
+ minfree = fds + i;
+ for(; i < nfds; i++) fds[i].s = -1;
+ }
+ }
+ ret = select(maxfd + 1, &readfds, 0, 0, 0);
+ for (n = fds; n < fds + nfds; n++){
+ if(n->s < 0) continue;
+ if (FD_ISSET(n->s, &readfds)){
+ if(n->type == SOCK_STREAM && n->timeout == 0){
+ /* add accepted socket to list of sockets we are
+ selecting on */
+ int s = accept(n->s, NULL, 0);
+ if(minfree == NULL){
+ kerb_err_reply(s, NULL, KFAILURE, "Out of memory");
+ close(s);
+ }else{
+ minfree->s = s;
+ minfree->type = SOCK_STREAM;
+ gettimeofday(&tv, NULL);
+ minfree->timeout = tv.tv_sec + 4; /* XXX */
+ }
+ }else{
+ int b;
+ struct sockaddr_in from;
+ int fromlen = sizeof(from);
+ b = recvfrom(n->s, n->buf.dat + n->buf.length,
+ MAX_PKT_LEN - n->buf.length, 0,
+ (struct sockaddr *)&from, &fromlen);
+ if(b < 0){
+ if(n->type == SOCK_STREAM){
+ close(n->s);
+ n->s = -1;
+ }
+ n->buf.length = 0;
+ continue;
+ }
+ n->buf.length += b;
+ if(n->type == SOCK_STREAM){
+ if(n->buf.length >= 4 && n->buf.dat[0] == 0){
+ /* if this is a new type of packet (with
+ the length attached to the head of the
+ packet), and there is no more data to
+ be read, fake an old packet, so the
+ code below will work */
+ u_int32_t len;
+ krb_get_int(n->buf.dat, &len, 4, 0);
+ if(n->buf.length == len + 4){
+ memmove(n->buf.dat, n->buf.dat + 4, len);
+ b = 0;
+ }
+ }
+ if(b == 0){
+ /* handle request if there are
+ no more bytes to read */
+ fromlen = sizeof(from);
+ getpeername(n->s,(struct sockaddr*)&from, &fromlen);
+ kerberos_wrap(n->s, &n->buf, &from);
+ n->buf.length = 0;
+ close(n->s);
+ n->s = -1;
+ }
+ }else{
+ /* udp packets are atomic */
+ kerberos_wrap(n->s, &n->buf, &from);
+ n->buf.length = 0;
+ }
+ }
+ }
+ }
+ }
+}
OpenPOWER on IntegriCloud