/* $OpenBSD: kerberos.c,v 1.15 1998/11/28 23:41:01 art Exp $ */ /* $KTH: kerberos.c,v 1.70 1997/09/26 18:06:38 joda Exp $ */ /* * This source code is no longer held under any constraint of USA * `cryptographic laws' since it was exported legally. The cryptographic * functions were removed from the code and a "Bones" distribution was * made. A Commodity Jurisdiction Request #012-94 was filed with the * USA State Department, who handed it to the Commerce department. The * code was determined to fall under General License GTDA under ECCN 5D96G, * and hence exportable. The cryptographic interfaces were re-added by Eric * Young, and then KTH proceeded to maintain the code in the free world. */ /*- * Copyright (C) 1989 by the Massachusetts Institute of Technology * * Export of this software from the United States of America is assumed * to require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "klog.h" #include "version.h" #include "krb_log.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 = KRBLOG; /* 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; static char progname[]="kerberos"; #ifndef MAX #define MAX(a,b) (((a)>(b))?(a):(b)) #endif /* MAX */ #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif /* MIN */ /* * 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] [-i address_to_listen_on]" " [-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]; strncpy(e_msg, "\nKerberos error -- ", sizeof(e_msg)); if (strlen(e_msg) < sizeof(e_msg)) strncat(e_msg, string, sizeof(e_msg) - strlen(e_msg) - 1); e_msg[sizeof(e_msg) - 1] = '\0'; 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); strncpy(lastrealm, r, sizeof(lastrealm) - 1); lastrealm[sizeof(lastrealm) - 1] = '\0'; return (KSUCCESS); } static int kerberos(unsigned char *buf, int len, char *proto, struct sockaddr_in *client, struct sockaddr_in *server, 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){ strncpy((char*)rpkt->dat, "Packet too short", MAX_KTXT_LEN - 1); rpkt->dat[MAX_KTXT_LEN - 1] = '\0'; return KFAILURE; } gettimeofday(&kerb_time, NULL); pvno = *p++; if(pvno != KRB_PROT_VERSION){ msg = klog(L_KRB_PERR, "KRB protocol version mismatch (%d)", pvno); strncpy((char*)rpkt->dat, msg, MAX_KTXT_LEN - 1); rpkt->dat[MAX_KTXT_LEN - 1] = '\0'; 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 (%s/%u)", name, inst, realm, service, sinst, inet_ntoa(client->sin_addr), proto, ntohs(server->sin_port)); if((err = check_princ(name, inst, 0, &a_name))){ strncpy((char*)rpkt->dat, krb_get_err_text(err), MAX_KTXT_LEN - 1); rpkt->dat[MAX_KTXT_LEN - 1] = '\0'; return err; } tk->length = 0; if((err = check_princ(service, sinst, 0, &s_name))){ strncpy((char*)rpkt->dat, krb_get_err_text(err), MAX_KTXT_LEN - 1); rpkt->dat[MAX_KTXT_LEN -1] = '\0'; 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->sin_addr.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: strncpy(realm, (char*)buf + 3, REALM_SZ - 1); realm[REALM_SZ - 1] = '\0'; if((err = set_tgtkey(realm))){ msg = klog(L_ERR_UNK, "Unknown realm %s from %s (%s/%u)", realm, inet_ntoa(client->sin_addr), proto, ntohs(server->sin_port)); strncpy((char*)rpkt->dat, msg, MAX_KTXT_LEN - 1); rpkt->dat[MAX_KTXT_LEN -1] = '\0'; 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->sin_addr.s_addr, &ad, 0); if(err){ msg = klog(L_ERR_UNK, "krb_rd_req from %s (%s/%u): %s", inet_ntoa(client->sin_addr), proto, ntohs(server->sin_port), krb_get_err_text(err)); strncpy((char*)rpkt->dat, msg, MAX_KTXT_LEN - 1); rpkt->dat[MAX_KTXT_LEN - 1] = '\0'; 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 (%s/%u)", ad.pname, ad.pinst, ad.prealm, service, sinst, inet_ntoa(client->sin_addr), proto, ntohs(server->sin_port)); if(strcmp(ad.prealm, realm)){ msg = klog(L_ERR_UNK, "Can't hop realms: %s -> %s", realm, ad.prealm); strncpy((char*)rpkt->dat, msg, MAX_KTXT_LEN - 1); rpkt->dat[MAX_KTXT_LEN - 1] = '\0'; return KERB_ERR_PRINCIPAL_UNKNOWN; } if(!strcmp(service, "changepw")){ strncpy((char*)rpkt->dat, "Can't authorize password changed based on TGT", MAX_KTXT_LEN - 1); rpkt->dat[MAX_KTXT_LEN - 1] = '\0'; return KERB_ERR_PRINCIPAL_UNKNOWN; } err = check_princ(service, sinst, life, &s_name); if(err){ strncpy((char*)rpkt->dat, krb_get_err_text(err), MAX_KTXT_LEN - 1); rpkt->dat[MAX_KTXT_LEN - 1] = '\0'; 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->sin_addr.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 (%s/%u)", msg_type, inet_ntoa(client->sin_addr), proto, ntohs(server->sin_port)); strncpy((char*)rpkt->dat, msg, MAX_KTXT_LEN - 1); rpkt->dat[MAX_KTXT_LEN - 1] = '\0'; return KFAILURE; } } static void kerberos_wrap(int s, KTEXT data, char *proto, struct sockaddr_in *client, struct sockaddr_in *server) { KTEXT_ST pkt; int http_flag = strcmp(proto, "http") == 0; int err = kerberos(data->dat, data->length, proto, client, server, &pkt); if(err == -1) return; if(http_flag){ const char *msg = "HTTP/1.1 200 OK\r\n" "Server: KTH-KRB/" VERSION "\r\n" "Content-type: application/octet-stream\r\n" "Content-transfer-encoding: binary\r\n\r\n"; sendto(s, msg, strlen(msg), 0, (struct sockaddr *)client, sizeof(*client)); } 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; struct sockaddr_in addr; }; static void mksocket(struct descr *d, struct in_addr addr, int type, const char *service, int port) { int on = 1; int sock; memset(d, 0, sizeof(struct descr)); if ((sock = socket(AF_INET, type, 0)) < 0) err (1, "socket"); if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) < 0) warn ("setsockopt (SO_REUSEADDR)"); memset(&d->addr, 0, sizeof(d->addr)); d->addr.sin_family = AF_INET; d->addr.sin_port = port; d->addr.sin_addr = addr; if (bind(sock, (struct sockaddr *)&d->addr, sizeof(d->addr)) < 0) err (1, "bind '%s/%s' (%d)", service, (type == SOCK_DGRAM) ? "udp" : "tcp", ntohs(d->addr.sin_port)); if(type == SOCK_STREAM) listen(sock, SOMAXCONN); d->s = sock; d->type = type; } static void loop(struct descr *fds, int maxfd); struct port_spec { int port; int type; }; static int add_port(struct port_spec **ports, int *num_ports, int port, int type) { struct port_spec *tmp; tmp = realloc(*ports, (*num_ports + 1) * sizeof(*tmp)); if(tmp == NULL) { free(*ports); return ENOMEM; } *ports = tmp; tmp[*num_ports].port = port; tmp[*num_ports].type = type; (*num_ports)++; return 0; } void make_sockets(char *port_spec, struct in_addr *i_addr, struct descr **fds, int *nfds) { int tp; struct in_addr *a; char *p, *q, *pos = NULL; struct servent *sp; struct port_spec *ports = NULL; int num_ports = 0; int i, j; for(p = strtok_r(port_spec, ", \t", &pos); p; p = strtok_r(NULL, ", \t", &pos)){ if(strcmp(p, "+") == 0){ add_port(&ports, &num_ports, 88, SOCK_DGRAM); add_port(&ports, &num_ports, 88, SOCK_STREAM); add_port(&ports, &num_ports, 750, SOCK_DGRAM); add_port(&ports, &num_ports, 750, SOCK_STREAM); }else{ q = strchr(p, '/'); if(q){ *q = 0; q++; } sp = getservbyname(p, q); if(sp) tp = ntohs(sp->s_port); else if(sscanf(p, "%d", &tp) != 1) { warnx("Unknown port: %s%s%s", p, q ? "/" : "", q ? q : ""); continue; } if(q){ if(strcasecmp(q, "tcp") == 0) add_port(&ports, &num_ports, tp, SOCK_STREAM); else if(strcasecmp(q, "udp") == 0) add_port(&ports, &num_ports, tp, SOCK_DGRAM); else warnx("Unknown protocol type: %s", q); }else{ add_port(&ports, &num_ports, tp, SOCK_DGRAM); add_port(&ports, &num_ports, tp, SOCK_STREAM); } } } if(num_ports == 0) errx(1, "No valid ports specified!"); if (i_addr) { *nfds = 1; a = malloc(sizeof(*a) * *nfds); if (a == NULL) errx (1, "Failed to allocate %u bytes", sizeof(*a) * *nfds); memcpy(a, i_addr, sizeof(struct in_addr)); } else *nfds = k_get_all_addrs (&a); if (*nfds < 0) { struct in_addr any; any.s_addr = INADDR_ANY; warnx ("Could not get local addresses, binding to INADDR_ANY"); *nfds = 1; a = malloc(sizeof(*a) * *nfds); if (a == NULL) errx (1, "Failed to allocate %u bytes", sizeof(*a) * *nfds); memcpy(a, &any, sizeof(struct in_addr)); } *fds = malloc(*nfds * num_ports * sizeof(**fds)); if (*fds == NULL) errx (1, "Failed to allocate %u bytes", *nfds * num_ports * sizeof(**fds)); for (i = 0; i < *nfds; i++) { for(j = 0; j < num_ports; j++) { mksocket(*fds + num_ports * i + j, a[i], ports[j].type, "", htons(ports[j].port)); } } *nfds *= num_ports; free(ports); free (a); } int main(int argc, char **argv) { int child; int c; struct descr *fds; int nfds; int n; int kerror; int i_flag = 0; struct in_addr i_addr; char *port_spec = "+"; umask(077); /* Create protected files */ while ((c = getopt(argc, argv, "snmp:P:a:l:r:i:")) != 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 */ 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 'P': port_spec = optarg; 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++; strncpy(local_realm, optarg, REALM_SZ - 1); local_realm[REALM_SZ - 1] = '\0'; break; case 'i': /* Only listen on this address */ if(inet_aton (optarg, &i_addr) == 0) { fprintf (stderr, "Bad address: %s\n", optarg); exit (1); } ++i_flag; break; default: usage(); break; } } 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); { char hostname[MAXHOSTNAMELEN]; /* find our hostname, and use it as the instance */ if (gethostname(hostname, sizeof(hostname))) err (1, "gethostname"); strncpy(k_instance, hostname, sizeof(k_instance) - 1); k_instance[sizeof(k_instance) - 1] = '\0'; } make_sockets(port_spec, i_flag ? &i_addr : NULL, &fds, &nfds); /* 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."); 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); } void read_socket(struct descr *n) { 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; return; } n->buf.length += b; if(n->type == SOCK_STREAM){ char *proto = "tcp"; if(n->buf.length > 4 && strncmp((char *)n->buf.dat, "GET ", 4) == 0 && strncmp((char *)n->buf.dat + n->buf.length - 4, "\r\n\r\n", 4) == 0){ char *p; char *save = NULL; n->buf.dat[n->buf.length - 1] = 0; strtok_r(n->buf.dat, " \t\r\n", &save); p = strtok_r(NULL, " \t\r\n", &save); if(p == NULL) p = ""; if(*p == '/') p++; n->buf.length = base64_decode(p, n->buf.dat); if(n->buf.length <= 0){ const char *msg = "HTTP/1.1 404 Not found\r\n" "Server: KTH-KRB/" VERSION "\r\n" "Content-type: text/html\r\n" "Content-transfer-encoding: 8bit\r\n\r\n" "404 Not found\r\n" "

404 Not found

\r\n" "That page does not exist. Information about " "KTH-KRB " "is available elsewhere.\r\n"; fromlen = sizeof(from); if(getpeername(n->s,(struct sockaddr*)&from, &fromlen) == 0) klog(L_KRB_PERR, "Unknown HTTP request from %s", inet_ntoa(from.sin_addr)); else klog(L_KRB_PERR, "Unknown HTTP request from "); write(n->s, msg, strlen(msg)); close(n->s); n->s = -1; n->buf.length = 0; return; } proto = "http"; b = 0; } else 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, proto, &from, &n->addr); n->buf.length = 0; close(n->s); n->s = -1; } }else{ /* udp packets are atomic */ kerberos_wrap(n->s, &n->buf, "udp", &from, &n->addr); n->buf.length = 0; } } static void loop(struct descr *fds, int nfds) { for (;;) { int ret; fd_set readfds; struct timeval tv; int maxfd = 0; struct descr *n, *minfree; int accepted; /* accept at most one socket per `round' */ 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); accepted = 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; if(accepted) continue; accepted = 1; 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 */ minfree->buf.length = 0; memcpy(&minfree->addr, &n->addr, sizeof(minfree->addr)); } }else read_socket(n); } } } }