diff options
Diffstat (limited to 'crypto/kerberosIV/server')
-rw-r--r-- | crypto/kerberosIV/server/Makefile.in | 79 | ||||
-rw-r--r-- | crypto/kerberosIV/server/kerberos.c | 846 |
2 files changed, 925 insertions, 0 deletions
diff --git a/crypto/kerberosIV/server/Makefile.in b/crypto/kerberosIV/server/Makefile.in new file mode 100644 index 0000000..64e7eb1 --- /dev/null +++ b/crypto/kerberosIV/server/Makefile.in @@ -0,0 +1,79 @@ +# $Id: Makefile.in,v 1.24 1997/05/02 17:52:00 assar Exp $ + +SHELL = /bin/sh + +srcdir = @srcdir@ +VPATH = @srcdir@ + +CC = @CC@ +AR = ar +RANLIB = @RANLIB@ +DEFS = @DEFS@ +CFLAGS = @CFLAGS@ +LD_FLAGS = @LD_FLAGS@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +MKINSTALLDIRS = @top_srcdir@/mkinstalldirs + +LIBS = @LIBS@ +LIB_DBM = @LIB_DBM@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +libdir = @libdir@ +libexecdir = @libexecdir@ +transform=@program_transform_name@ +EXECSUFFIX=@EXECSUFFIX@ + +PROGS = kerberos$(EXECSUFFIX) + +SOURCES = kerberos.c + +OBJECTS = kerberos.o + +all: $(PROGS) + +Wall: + make CFLAGS="-g -Wall -Wno-comment -Wmissing-prototypes -Wmissing-declarations -D__USE_FIXED_PROTOTYPES__" + +.c.o: + $(CC) -c $(CPPFLAGS) $(DEFS) -I../include -I$(srcdir) $(CFLAGS) $< + +install: all + $(MKINSTALLDIRS) $(libexecdir) + for x in $(PROGS); do \ + $(INSTALL_PROGRAM) $$x $(libexecdir)/`echo $$x | sed '$(transform)'`; \ + done + +uninstall: + for x in $(PROGS); do \ + rm -f $(libexecdir)/`echo $$x | sed '$(transform)'`; \ + done + +TAGS: $(SOURCES) + etags $(SOURCES) + +check: + +clean: + rm -f *.a *.o $(PROGS) + +mostlyclean: clean + +distclean: clean + rm -f Makefile *.tab.c *~ + +realclean: distclean + rm -f TAGS + +dist: $(DISTFILES) + for file in $(DISTFILES); do \ + ln $$file ../`cat ../.fname`/lib \ + || cp -p $$file ../`cat ../.fname`/lib; \ + done + +kerberos$(EXECSUFFIX): kerberos.o + $(CC) $(LD_FLAGS) $(LDFLAGS) -o $@ kerberos.o -L../lib/kdb -lkdb -L../lib/krb -lkrb -L../lib/des -ldes -L../lib/roken -lroken $(LIB_DBM) $(LIBS) -lroken + +$(OBJECTS): ../include/config.h 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; + } + } + } + } + } +} |