diff options
author | Artur Grabowski <art@cvs.openbsd.org> | 1997-11-28 12:49:35 +0000 |
---|---|---|
committer | Artur Grabowski <art@cvs.openbsd.org> | 1997-11-28 12:49:35 +0000 |
commit | c7b7a71f79cef9dbb230f353d9bbf3d6ef3a5aed (patch) | |
tree | 5817f345511882de1c9e1a57f3095352ce671421 /kerberosIV/kerberos | |
parent | 0857c8c45edb4fe59f82903f40d99a3aa19a04f7 (diff) |
The first big step towards a complete upgrade to kth-krb4-0.9.7
Diffstat (limited to 'kerberosIV/kerberos')
-rw-r--r-- | kerberosIV/kerberos/Makefile | 4 | ||||
-rw-r--r-- | kerberosIV/kerberos/kerberos.c | 1046 |
2 files changed, 593 insertions, 457 deletions
diff --git a/kerberosIV/kerberos/Makefile b/kerberosIV/kerberos/Makefile index 455fe5ad922..8b16fd72bcc 100644 --- a/kerberosIV/kerberos/Makefile +++ b/kerberosIV/kerberos/Makefile @@ -1,9 +1,11 @@ # from @(#)Makefile 8.1 (Berkeley) 6/1/93 -# $Id: Makefile,v 1.1 1995/12/14 06:52:52 tholo Exp $ +# $Id: Makefile,v 1.2 1997/11/28 12:48:46 art Exp $ PROG= kerberos DPADD= ${LIBKDB} ${LIBKRB} ${LIBDES} LDADD= -lkdb -lkrb -ldes MAN= kerberos.8 +CFLAGS+=-I${.CURDIR} + .include <bsd.prog.mk> diff --git a/kerberosIV/kerberos/kerberos.c b/kerberosIV/kerberos/kerberos.c index 9d0dbe42550..7f66aa55e65 100644 --- a/kerberosIV/kerberos/kerberos.c +++ b/kerberosIV/kerberos/kerberos.c @@ -1,103 +1,73 @@ -/* $Id: kerberos.c,v 1.5 1997/06/29 10:32:14 provos Exp $ */ +/* $KTH: kerberos.c,v 1.70 1997/09/26 18:06:38 joda Exp $ */ -/*- - * Copyright 1987, 1988 by the Student Information Processing Board - * of the Massachusetts Institute of Technology + +/* + * Copyright 1985, 1986, 1987, 1988 by the Massachusetts Institute + * of Technology. * - * 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 names of M.I.T. and the M.I.T. S.I.P.B. not be - * used in advertising or publicity pertaining to distribution - * of the software without specific, written prior permission. - * M.I.T. and the M.I.T. S.I.P.B. make no representations about - * the suitability of this software for any purpose. It is - * provided "as is" without express or implied warranty. + * For copying and distribution information, please see the file + * <mit-copyright.h>. */ -#include "kerberosIV/site.h" - #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <sys/types.h> - #include <sys/time.h> #include <time.h> - -#include <sys/stat.h> -#include <fcntl.h> -#include <sys/ioctl.h> - +#include <sys/select.h> #include <errno.h> #include <unistd.h> - +#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> -#include <sys/socket.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/filio.h> #include <netdb.h> +#include <stdarg.h> +#include <err.h> #include <des.h> #include <kerberosIV/krb.h> #include <kerberosIV/krb_db.h> - #include <prot.h> -#include <klog.h> -#include <kdc.h> +#include "klog.h" -static struct sockaddr_in sina = {AF_INET}; -int f; - -/* XXX several files in libkdb know about this */ -char *progname; +#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 Principal a_name_data; /* for requesting user */ -static Principal s_name_data; /* for services requested */ -static des_cblock session_key; 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 int lflag; /* Have we set an alterate log file? */ -static char *log_file; /* name of alt. log file */ +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 u_char req_msg_type; -static u_char req_version; static char *req_name_ptr; static char *req_inst_ptr; static char *req_realm_ptr; -static u_long req_time_ws; - -int req_act_vno = KRB_PROT_VERSION; /* Temporary for version skew */ +static u_int32_t req_time_ws; static char local_realm[REALM_SZ]; -/* statistics */ -static int q_bytes; /* current bytes remaining in queue */ -static int q_n; /* how many consecutive non-zero - * q_bytes */ -static int max_q_bytes; -static int max_q_n; -static int n_auth_req; -static int n_appl_req; -static int n_packets; - +/* options */ static int max_age = -1; static int pause_int = -1; +static char progname[]="kerberos"; /* * Print usage message and exit. @@ -105,31 +75,31 @@ static int pause_int = -1; static void usage(void) { - fprintf(stderr, "Usage: %s [-s] [-m] [-n] [-p pause_seconds]%s%s\n", progname, - " [-a max_age] [-l log_file] [-r realm]" - ," [database_pathname]" - ); + 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_er_reply creates an error reply packet and sends it to the + * kerb_err_reply creates an error reply packet and sends it to the * client. */ static void -kerb_err_reply(struct sockaddr_in *client, KTEXT pkt, long int err, char *string) +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]; - bzero(e_msg, sizeof e_msg); 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, e_pkt->dat, e_pkt->length, 0, (struct sockaddr*)client, S_AD_SZ); + sendto(f, (char*)e_pkt->dat, e_pkt->length, 0, (struct sockaddr *)client, + sizeof(*client)); } static void @@ -141,9 +111,9 @@ hang(void) pause(); } else { char buf[256]; - (void) snprintf(buf, sizeof(buf), - "Kerberos will wait %d seconds before dying so as not to loop init", - pause_int); + 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"); @@ -151,29 +121,6 @@ hang(void) } } -/* - * Given a pointer to a long containing the number of seconds - * since the beginning of time (midnight 1 Jan 1970 GMT), return - * a string containing the local time in the form: - * - * "25-Jan-88 10:17:56" - */ - -static char * -strtime(time_t *t) -{ - static char st_data[40]; - static char *st = st_data; - struct tm *tm; - char *month_sname(int n); - - tm = localtime(t); - (void) snprintf(st, sizeof(st_data), "%2d-%s-%02d %02d:%02d:%02d", - tm->tm_mday, month_sname(tm->tm_mon + 1), tm->tm_year, - tm->tm_hour, tm->tm_min, tm->tm_sec); - return st; -} - static int check_princ(char *p_name, char *instance, unsigned int lifetime, Principal *p) { @@ -181,9 +128,6 @@ check_princ(char *p_name, char *instance, unsigned int lifetime, Principal *p) static int more; n = kerb_get_principal(p_name, instance, p, 1, &more); - klog(L_ALL_REQ, - "Principal: \"%s\", Instance: \"%s\" Lifetime = %d n = %d", - p_name, instance, lifetime, n, 0); if (n < 0) { lt = klog(L_KRB_PERR, "Database unavailable!"); @@ -197,43 +141,47 @@ check_princ(char *p_name, char *instance, unsigned int lifetime, Principal *p) */ if (n == 0) { /* service unknown, log error, skip to next request */ - lt = klog(L_ERR_UNK, "UNKNOWN \"%s\" \"%s\"", p_name, - instance, 0); + 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, 0); + 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, 0); + 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, - "Key vers incorrect, KRB = %d, \"%s\" \"%s\" = %d", - master_key_version, p->name, p->instance, p->kdc_key_ver, - 0); + "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_long) p->exp_date < (u_long) kerb_time.tv_sec) { + 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, - "EXPIRED \"%s\" \"%s\" %s", p->name, p->instance, - strtime((time_t*)&(p->exp_date)), 0); + "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) @@ -248,315 +196,236 @@ set_tgtkey(char *r) if (!strcmp(lastrealm, r)) return (KSUCCESS); - log("Getting key for %s", r); + klog(L_ALL_REQ, "Getting key for %s", r); - n = kerb_get_principal("krbtgt", r, p, 1, &more); + n = kerb_get_principal(KRB_TICKET_GRANTING_TICKET, r, p, 1, &more); if (n == 0) return (KFAILURE); /* unseal tgt key from master key */ - bcopy(&p->key_low, key, 4); - bcopy(&p->key_high, ((long *) key) + 1, 4); - kdb_encrypt_key(&key, &key, &master_key, - master_key_schedule, DES_DECRYPT); + copy_to_key(&p->key_low, &p->key_high, key); + unseal(&key); krb_set_key(key, 0); strcpy(lastrealm, r); return (KSUCCESS); } -static void -kerberos(struct sockaddr_in *client, KTEXT pkt) -{ - static KTEXT_ST rpkt_st; - KTEXT rpkt = &rpkt_st; - static KTEXT_ST ciph_st; - KTEXT ciph = &ciph_st; - static KTEXT_ST tk_st; - KTEXT tk = &tk_st; - static KTEXT_ST auth_st; - KTEXT auth = &auth_st; - AUTH_DAT ad_st; - AUTH_DAT *ad = &ad_st; - - - static struct in_addr client_host; - static int msg_byte_order; - static int swap_bytes; - static u_char k_flags; - u_long lifetime; - int i; - des_cblock key; - des_key_schedule key_s; - char *ptr; - - - - ciph->length = 0; - client_host = client->sin_addr; - - /* eval macros and correct the byte order and alignment as needed */ - req_version = pkt_version(pkt); /* 1 byte, version */ - req_msg_type = pkt_msg_type(pkt); /* 1 byte, Kerberos msg type */ - - req_act_vno = req_version; - - /* check packet version */ - if (req_version != KRB_PROT_VERSION) { - lt = klog(L_KRB_PERR, - "KRB prot version mismatch: KRB =%d request = %d", - KRB_PROT_VERSION, req_version, 0); - /* send an error reply */ - kerb_err_reply(client, pkt, KERB_ERR_PKT_VER, lt); - return; - } - msg_byte_order = req_msg_type & 1; - - swap_bytes = 0; - if (msg_byte_order != HOST_BYTE_ORDER) { - swap_bytes++; +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){ + strcpy((char*)rpkt->dat, "Packet too short"); + return KFAILURE; } - klog(L_KRB_PINFO, - "Prot version: %d, Byte order: %d, Message type: %d", - req_version, msg_byte_order, req_msg_type); - switch (req_msg_type & ~1) { + 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 (%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))){ + 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->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)); { - u_long req_life; /* Requested liftime */ - char *service; /* Service name */ - char *instance; /* Service instance */ - - n_auth_req++; - tk->length = 0; - k_flags = 0; /* various kerberos flags */ - - - /* set up and correct for byte order and alignment */ - req_name_ptr = (char *) pkt_a_name(pkt); - req_inst_ptr = (char *) pkt_a_inst(pkt); - req_realm_ptr = (char *) pkt_a_realm(pkt); - bcopy(pkt_time_ws(pkt), &req_time_ws, sizeof(req_time_ws)); - /* time has to be diddled */ - if (swap_bytes) { - swap_u_long(req_time_ws); - } - ptr = (char *) pkt_time_ws(pkt) + 4; - - req_life = (unsigned char) (*ptr++); - - service = ptr; - instance = ptr + strlen(service) + 1; - - rpkt = &rpkt_st; - klog(L_INI_REQ, - "Initial ticket request Host: %s User: \"%s\" \"%s\"", - inet_ntoa(client_host), req_name_ptr, req_inst_ptr, 0); - - if ((i = check_princ(req_name_ptr, req_inst_ptr, 0, - &a_name_data))) { - kerb_err_reply(client, pkt, i, lt); - return; - } - tk->length = 0; /* init */ - if (strcmp(service, "krbtgt")) - klog(L_NTGT_INTK, - "INITIAL request from %s.%s for %s.%s", - req_name_ptr, req_inst_ptr, service, instance, 0); - /* this does all the checking */ - if ((i = check_princ(service, instance, 0, - &s_name_data))) { - kerb_err_reply(client, pkt, i, lt); - return; - } - /* Bound requested lifetime with service and user */ - lifetime = min(req_life, ((u_long) s_name_data.max_life)); - lifetime = min(lifetime, ((u_long) a_name_data.max_life)); - -#ifdef NOENCRYPTION - bzero(session_key, sizeof(des_cblock)); -#else - des_new_random_key(&session_key); -#endif - /* unseal server's key from master key */ - bcopy(&s_name_data.key_low, key, 4); - bcopy(&s_name_data.key_high, ((long *) key) + 1, 4); - kdb_encrypt_key(&key, &key, &master_key, - master_key_schedule, DES_DECRYPT); - /* construct and seal the ticket */ - krb_create_ticket(tk, k_flags, a_name_data.name, - a_name_data.instance, local_realm, - client_host.s_addr, session_key, lifetime, kerb_time.tv_sec, - s_name_data.name, s_name_data.instance, &key); - bzero(key, sizeof(key)); - bzero(key_s, sizeof(key_s)); - - /* - * get the user's key, unseal it from the server's key, and - * use it to seal the cipher - */ - - /* a_name_data.key_low a_name_data.key_high */ - bcopy(&a_name_data.key_low, key, 4); - bcopy(&a_name_data.key_high, ((long *) key) + 1, 4); - - /* unseal the a_name key from the master key */ - kdb_encrypt_key(&key, &key, &master_key, - master_key_schedule, DES_DECRYPT); - - create_ciph(ciph, session_key, s_name_data.name, - s_name_data.instance, local_realm, lifetime, - s_name_data.key_version, tk, kerb_time.tv_sec, &key); - - /* clear session key */ - bzero(session_key, sizeof(session_key)); - - bzero(key, sizeof(key)); - - - - /* always send a reply packet */ - rpkt = create_auth_reply(req_name_ptr, req_inst_ptr, - req_realm_ptr, req_time_ws, 0, a_name_data.exp_date, - a_name_data.key_version, ciph); - sendto(f, rpkt->dat, rpkt->length, 0, (struct sockaddr*)client, S_AD_SZ); - bzero(&a_name_data, sizeof(a_name_data)); - bzero(&s_name_data, sizeof(s_name_data)); - break; + 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: - { - u_long time_ws; /* Workstation time */ - u_long req_life; /* Requested liftime */ - char *service; /* Service name */ - char *instance; /* Service instance */ - int kerno; /* Kerberos error number */ - char tktrlm[REALM_SZ]; - - n_appl_req++; - tk->length = 0; - k_flags = 0; /* various kerberos flags */ - - auth->length = 4 + strlen((char*)pkt->dat + 3); - auth->length += (int) *(pkt->dat + auth->length) + - (int) *(pkt->dat + auth->length + 1) + 2; - - bcopy(pkt->dat, auth->dat, auth->length); - - strncpy(tktrlm, (char*)(auth->dat + 3), REALM_SZ); - if (set_tgtkey(tktrlm)) { - lt = klog(L_ERR_UNK, - "FAILED realm %s unknown. Host: %s ", - tktrlm, inet_ntoa(client_host)); - kerb_err_reply(client, pkt, kerno, lt); - return; - } - kerno = krb_rd_req(auth, "ktbtgt", tktrlm, client_host.s_addr, - ad, 0); - - if (kerno) { - klog(L_ERR_UNK, "FAILED krb_rd_req from %s: %s", - inet_ntoa(client_host), krb_err_txt[kerno]); - kerb_err_reply(client, pkt, kerno, "krb_rd_req failed"); - return; - } - ptr = (char *) pkt->dat + auth->length; - - bcopy(ptr, &time_ws, 4); - ptr += 4; - - req_life = (unsigned char) (*ptr++); - - service = ptr; - instance = ptr + strlen(service) + 1; + strcpy(realm, (char*)buf + 3); + 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)); + 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->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)); + 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 (%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); + strcpy((char*)rpkt->dat, msg); + return KERB_ERR_PRINCIPAL_UNKNOWN; + } - klog(L_APPL_REQ, "APPL Request %s.%s@%s on %s for %s.%s", - ad->pname, ad->pinst, ad->prealm, inet_ntoa(client_host), - service, instance, 0); + if(!strcmp(service, "changepw")){ + strcpy((char*)rpkt->dat, + "Can't authorize password changed based on TGT"); + return KERB_ERR_PRINCIPAL_UNKNOWN; + } - if (strcmp(ad->prealm, tktrlm)) { - kerb_err_reply(client, pkt, KERB_ERR_PRINCIPAL_UNKNOWN, - "Can't hop realms"); - return; - } - if (!strcmp(service, "changepw")) { - kerb_err_reply(client, pkt, KERB_ERR_PRINCIPAL_UNKNOWN, - "Can't authorize password changed based on TGT"); - return; - } - kerno = check_princ(service, instance, req_life, - &s_name_data); - if (kerno) { - kerb_err_reply(client, pkt, kerno, lt); - return; - } - /* Bound requested lifetime with service and user */ - lifetime = min(req_life, - krb_time_to_life(kerb_time.tv_sec,krb_life_to_time(ad->time_sec,ad->life))); - lifetime = min(lifetime, ((u_long) s_name_data.max_life)); - - /* unseal server's key from master key */ - bcopy(&s_name_data.key_low, key, 4); - bcopy(&s_name_data.key_high, ((long *) key) + 1, 4); - kdb_encrypt_key(&key, &key, &master_key, - master_key_schedule, DES_DECRYPT); - /* construct and seal the ticket */ - -#ifdef NOENCRYPTION - bzero(session_key, sizeof(des_cblock)); -#else - des_new_random_key(&session_key); -#endif - - krb_create_ticket(tk, k_flags, ad->pname, ad->pinst, - ad->prealm, client_host.s_addr, - session_key, lifetime, kerb_time.tv_sec, - s_name_data.name, s_name_data.instance, - &key); - bzero(key, sizeof(key)); - bzero(key_s, sizeof(key_s)); - - create_ciph(ciph, session_key, service, instance, - local_realm, - lifetime, s_name_data.key_version, tk, - kerb_time.tv_sec, &ad->session); - - /* clear session key */ - bzero(session_key, sizeof(session_key)); - - bzero(ad->session, sizeof(ad->session)); - - rpkt = create_auth_reply(ad->pname, ad->pinst, - ad->prealm, time_ws, - 0, 0, 0, ciph); - sendto(f, rpkt->dat, rpkt->length, 0, (struct sockaddr*)client, S_AD_SZ); - bzero(&s_name_data, sizeof(s_name_data)); - break; + 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->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); -#ifdef notdef_DIE - case AUTH_MSG_DIE: + memset(&session, 0, sizeof(session)); + memset(ad.session, 0, sizeof(ad.session)); { - lt = klog(L_DEATH_REQ, - "Host: %s User: \"%s\" \"%s\" Kerberos killed", - inet_ntoa(client_host), req_name_ptr, req_inst_ptr, 0); - exit(0); + KTEXT r; + r =create_auth_reply(ad.pname, ad.pinst, ad.prealm, + req_time, 0, 0, 0, ciph); + memcpy(rpkt, r, sizeof(*rpkt)); } -#endif /* notdef_DIE */ - + memset(&s_name, 0, sizeof(s_name)); + return 0; + + case AUTH_MSG_ERR_REPLY: + return -1; default: - { - lt = klog(L_KRB_PERR, - "Unknown message type: %d from %s port %u", - req_msg_type, inet_ntoa(client_host), - ntohs(client->sin_port)); - break; - } + 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)); + strcpy((char*)rpkt->dat, msg); + 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 * @@ -570,16 +439,16 @@ setup_disc(void) int s; for (s = 0; s < 3; s++) { - (void) close(s); + close(s); } - (void) open("/dev/null", 0); - (void) dup2(0, 1); - (void) dup2(0, 2); + open("/dev/null", 0); + dup2(0, 1); + dup2(0, 2); setsid(); - (void) chdir("/tmp"); + chdir("/tmp"); return; } @@ -589,7 +458,8 @@ setup_disc(void) * Exit if it is; we don't want to tell lies. */ -static void check_db_age(void) +static void +check_db_age(void) { long age; @@ -609,25 +479,159 @@ static void check_db_age(void) } } +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) + 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); + 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); + memcpy(a, &any, sizeof(struct in_addr)); + } + *fds = malloc(*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) { - struct sockaddr_in from; - register int n; - int on = 1; int child; - struct servent *sp; - int fromlen; - static KTEXT_ST pkt_st; - KTEXT pkt = &pkt_st; - int kerror; int c; - extern char *optarg; - extern int optind; + struct descr *fds; + int nfds; + int n; + int kerror; + int i_flag = 0; + struct in_addr i_addr; + char *port_spec = "+"; - progname = argv[0]; + umask(077); /* Create protected files */ - while ((c = getopt(argc, argv, "snmp:a:l:r:")) != -1) { + while ((c = getopt(argc, argv, "snmp:P:a:l:r:i:")) != EOF) { switch(c) { case 's': /* @@ -637,10 +641,6 @@ main(int argc, char **argv) max_age = ONE_DAY; /* 24 hours */ if (pause_int == -1) pause_int = FIVE_MINUTES; /* 5 minutes */ - if (lflag == 0) { - log_file = KRBSLAVELOG; - lflag++; - } break; case 'n': max_age = -1; /* don't check max age. */ @@ -659,6 +659,9 @@ main(int argc, char **argv) usage(); } break; + case 'P': + port_spec = optarg; + break; case 'a': /* Set max age. */ if (!isdigit(optarg[0])) @@ -671,7 +674,6 @@ main(int argc, char **argv) break; case 'l': /* Set alternate log file */ - lflag++; log_file = optarg; break; case 'r': @@ -679,12 +681,20 @@ main(int argc, char **argv) rflag++; strcpy(local_realm, optarg); 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"); @@ -707,40 +717,22 @@ main(int argc, char **argv) if (mflag) printf("\tMaster key will be entered manually\n"); - printf("\tLog file is %s\n", lflag ? log_file : KRBLOG); + printf("\tLog file is %s\n", log_file); - if (lflag) - kset_logfile(log_file); + kset_logfile(log_file); /* find our hostname, and use it as the instance */ - if (gethostname(k_instance, INST_SZ)) { - fprintf(stderr, "%s: gethostname error\n", progname); - exit(1); - } - - if ((sp = getservbyname("kerberos", "udp")) == 0) { - fprintf(stderr, "%s: udp/kerberos unknown service\n", progname); - exit(1); - } - sina.sin_port = sp->s_port; + if (k_gethostname(k_instance, INST_SZ)) + err (1, "gethostname"); - if ((f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - fprintf(stderr, "%s: Can't open socket\n", progname); - exit(1); - } - if (setsockopt(f, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) - fprintf(stderr, "%s: setsockopt (SO_REUSEADDR)\n", progname); + make_sockets(port_spec, i_flag ? &i_addr : NULL, &fds, &nfds); - if (bind(f, (struct sockaddr*)&sina, sizeof(sina)) < 0) { - fprintf(stderr, "%s: Can't bind socket\n", progname); - exit(1); - } /* 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); + exit (1); } else { klog(L_KRB_PERR, "Kerberos db and cache init failed = %d ...exiting", n); @@ -753,15 +745,15 @@ main(int argc, char **argv) /* 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); + 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."); - bzero (master_key, sizeof (master_key)); - bzero (master_key_schedule, sizeof (master_key_schedule)); - exit (-1); + memset(master_key, 0, sizeof (master_key)); + memset (master_key_schedule, 0, sizeof (master_key_schedule)); + exit (1); } master_key_version = (u_char) kerror; @@ -790,28 +782,170 @@ main(int argc, char **argv) } 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(n->buf.dat, "GET ", 4) == 0 && + strncmp(n->buf.dat + n->buf.length - 4, + "\r\n\r\n", 4) == 0){ + char *p; + n->buf.dat[n->buf.length - 1] = 0; + strtok(n->buf.dat, " \t\r\n"); + p = strtok(NULL, " \t\r\n"); + if(p == NULL) + p = ""; + if(*p == '/') p++; + p = strdup(p); + n->buf.length = base64_decode(p, n->buf.dat); + free(p); + 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" + "<TITLE>404 Not found</TITLE>\r\n" + "<H1>404 Not found</H1>\r\n" + "That page does not exist. Information about " + "<A HREF=\"http://www.pdc.kth.se/kth-krb\">KTH-KRB</A> " + "is available elsewhere.\r\n"; + 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 (;;) { - fromlen = S_AD_SZ; - n = recvfrom(f, pkt->dat, MAX_PKT_LEN, 0, (struct sockaddr*)&from, &fromlen); - if (n > 0) { - pkt->length = n; - pkt->mbz = 0; /* force zeros to catch runaway strings */ - /* see what is left in the input queue */ - ioctl(f, FIONREAD, &q_bytes); - gettimeofday(&kerb_time, NULL); - q_n++; - max_q_n = max(max_q_n, q_n); - n_packets++; - klog(L_NET_INFO, - "q_byt %d, q_n %d, rd_byt %d, mx_q_b %d, mx_q_n %d, n_pkt %d", - q_bytes, q_n, n, max_q_bytes, max_q_n, n_packets, 0); - max_q_bytes = max(max_q_bytes, q_bytes); - if (!q_bytes) - q_n = 0; /* reset consecutive packets */ - kerberos(&from, pkt); - } else - klog(L_NET_ERR, - "%s: bad recvfrom n = %d errno = %d", progname, n, errno, 0); + 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); + } + } } } |