From b50f996d1f2445b693a09861210d78fa8aeb136b Mon Sep 17 00:00:00 2001 From: danw Date: Wed, 22 Jul 1998 14:55:23 +0000 Subject: [PATCH] New reg_svr --- reg_svr/Makefile.in | 32 +- reg_svr/errors | 95 +++++ reg_svr/genkey.c | 134 ++++++ reg_svr/kerberos.c | 208 ++++++++++ reg_svr/protocol.c | 329 +++++++++++++++ reg_svr/reg_svr.c | 968 -------------------------------------------- reg_svr/reg_svr.h | 134 +++--- reg_svr/reg_svr.pc | 847 ++++++++++++++++++++++++++++++++++++++ reg_svr/requests.c | 394 ------------------ reg_svr/words.c | 414 +++++++++++++++++++ 10 files changed, 2118 insertions(+), 1437 deletions(-) create mode 100644 reg_svr/errors create mode 100644 reg_svr/genkey.c create mode 100644 reg_svr/kerberos.c create mode 100644 reg_svr/protocol.c delete mode 100644 reg_svr/reg_svr.c create mode 100644 reg_svr/reg_svr.pc delete mode 100644 reg_svr/requests.c create mode 100644 reg_svr/words.c diff --git a/reg_svr/Makefile.in b/reg_svr/Makefile.in index 8f430fd7..ed222507 100644 --- a/reg_svr/Makefile.in +++ b/reg_svr/Makefile.in @@ -4,13 +4,18 @@ SHELL=/bin/sh @SET_MAKE@ CC=@CC@ -CPPFLAGS=@CPPFLAGS@ +CPPFLAGS=@CPPFLAGS@ @RSAREF_CPPFLAGS@ CFLAGS=@CFLAGS@ DEFS=@DEFS@ ALL_CFLAGS=$(CPPFLAGS) $(CFLAGS) $(DEFS) +PRO_C=@PRO_C@ +PRO_C_DEFS=@PRO_C_DEFS@ +PRO_C_INCLUDES=@PRO_C_INCLUDES@ +PRO_C_OPTS=@PRO_C_OPTS@ +PRO_C_FLAGS=$(PRO_C_OPTS) $(PRO_C_DEFS) $(PRO_C_INCLUDES) LDFLAGS=@LDFLAGS@ SQL_LIBS=@SQL_LIBS@ -LIBS=-lkadm @LIBS@ +LIBS=-lkadm @RSAREF_LIBS@ @LIBS@ INSTALL=@INSTALL@ INSTALL_PROGRAM=@INSTALL_PROGRAM@ @@ -20,10 +25,18 @@ SRCTOP=@top_srcdir@ BUILDTOP=.. mrbindir=@mrbindir@ -REG_OBJS=reg_svr.o requests.o -START_OBJS=startreg.o +REG_OBJS= kerberos.o protocol.o reg_svr.o words.o +START_OBJS= startreg.o +GEN_OBJS= genkey.o -TARGET=reg_svr startreg +CFILES= reg_svr.c + +TARGET= reg_svr startreg genkey + +.SUFFIXES: .pc + +.pc.c: + $(PRO_C) $(PRO_C_FLAGS) INAME=$< ONAME=$@ .c.o: $(CC) -c $(ALL_CFLAGS) $< @@ -31,16 +44,19 @@ TARGET=reg_svr startreg all: $(TARGET) clean: - rm -f $(REG_OBJS) $(START_OBJS) $(TARGET) + rm -f $(REG_OBJS) $(START_OBJS) $(CFILES) $(TARGET) -depend: +depend: $(CFILES) install: all $(INSTALL_PROGRAM) reg_svr $(mrbindir) $(INSTALL_PROGRAM) startreg $(mrbindir) reg_svr: $(REG_OBJS) - $(CC) -o $@ $(LDFLAGS) $(REG_OBJS) ../server/libmrglue.a $(SQL_LIBS) $(LIBS) + $(CC) -o $@ $(LDFLAGS) $(REG_OBJS) $(SQL_LIBS) $(LIBS) startreg: $(START_OBJS) $(CC) -o $@ $(LDFLAGS) $(START_OBJS) $(LIBS) + +genkey: $(GEN_OBJS) + $(CC) -o $@ $(LDFLAGS) $(GEN_OBJS) $(LIBS) diff --git a/reg_svr/errors b/reg_svr/errors new file mode 100644 index 00000000..ddb2a470 --- /dev/null +++ b/reg_svr/errors @@ -0,0 +1,95 @@ +# 1 INTERNAL_ERROR +An internal error has occurred, and the registration applet is unable +to continue. The error is: "%s". + +Please try again later. If this problem persists, contact the Athena +User Accounts Office in N42-105A, x3-1325. +# 2 PROTOCOL_ERROR +A protocol error has occurred, and the registration applet is unable +to continue. This may indicate that you are using an out-of-date +version of the registration applet. + +If you are unable to fix this problem, contact the Athena User +Accounts Office in N42-105A, x3-1325. +# 3 DATABASE_CLOSED +Sorry, it appears that the Moira database (which is needed for account +registration) is currently closed. Please try again later. +# 4 ENCRYPT_KEY +The registration server is unable to decrypt the data from the +registration applet. This probably means that you are using an +out-of-date version of the registration applet. + +If you are unable to fix this problem, contact the Athena User +Accounts Office in N42-105A, x3-1325. +# 5 NOT_FOUND_IN_DATABASE +The registration server was unable to locate you in the accounts +database. Please make sure you typed in your name and MIT ID number +correctly. + +If you are a new staff member, this may mean that you have not been +entered into the database yet. Try again tomorrow. If this problem +persists, contact the Athena User Accounts Office in N42-105A, +x3-1325. +# 6 ALREADY_REGISTERED +You have already registered for an account. The username you selected +is "%s". + +If you don't remember your password, or you do not think that this is +your account, contact the Athena User Accounts Office in N42-105A, +x3-1325. +# 7 ACCOUNT_DELETED +Your account, "%s", has been deactivated, and you are not listed as +eligible for another account. If you have any questions, contact +the Athena User Accounts Office in N42-105A, x3-1325. +# 8 NOT_ELIGIBLE +You were found in the database, but are listed as ineligible for an +account. If you have any questions, contact the Athena User Accounts +Office in N42-105A, x3-1325. +# 9 FOUND +OK. I've found you in the database: %s, account class %s. +# 10 FORCED_USERNAME +OK. I've found you in the database: %s, account class %s. + +The username "%s" has previously been reserved for this account. +# 11 BAD_SIX_WORDS +At least one of the six words you typed was incorrect. Please make +sure you typed in all six of the words correctly and in order. + +If you have lost your Athena Account Coupon, you will need to +get a new one, by going to either the Student Services Center in +Building 11, or the Athena User Accounts Office in N42-105A, x3-1325. +# 12 BAD_USERNAME +"%s" is not a valid username. Your username must be between %d and %d +characters long, must start with a lowercase letter, and must consist +entirely of lowercase letters, digits 0-9, and the underscore (_) +character. +# 13 USERNAME_UNAVAILABLE +Sorry, that username is already taken. Please select another. +# 14 RESERVED_USERNAME_UNAVAILABLE +Oops. Your account was reserved with the username "%s", but that +username is already taken. You will not be able to register your +account until this problem is resolved. Please contact the Athena User +Accounts Office in N42-105A, x3-1325. +# 15 USERNAME_OK +OK. I've reserved the username "%s" for you. +# 16 PASSWORD_SHORT +That password is too short! Please use a longer password. +# 17 PASSWORD_SIMPLE +That password is too simple. Please use a password with a mix of +different kinds of characters. +# 18 PASSWORD_SAMPLE +Those were just examples! You should invent your own secret password, +that no one else knows. +# 19 KADM_ERROR +An unexpected error occurred while trying to register your username +and password with the Kerberos server. The error was: "%s". + +Please try again later. If this problem persists, contact the Athena +User Accounts Office in N42-105A, x3-1325. +# 20 DONE +Your account has been registered and will become active later tonight. + +You should be able to log in by tomorrow morning using your Athena +username (%s) and the password you chose. + +Welcome to Athena! diff --git a/reg_svr/genkey.c b/reg_svr/genkey.c new file mode 100644 index 00000000..228272b4 --- /dev/null +++ b/reg_svr/genkey.c @@ -0,0 +1,134 @@ +/* $Id$ + * + * Utility program to generate a public/private key pair + * + * Copyright (C) 1998 by the Massachusetts Institute of Technology + * For copying and distribution information, please see the file + * . + * + */ + +#include +#include +#include "reg_svr.h" + +#include + +#include +#include +#include +#include + +#include + +/* RSARef includes */ +#include "global.h" +#include "rsaref.h" + +RCSID("$Header$"); + +void printhex(FILE *out, unsigned char *buf, int len); + +int main(int argc, char **argv) +{ + R_RSA_PRIVATE_KEY private; + R_RSA_PUBLIC_KEY public; + R_RSA_PROTO_KEY proto; + R_RANDOM_STRUCT random; + FILE *in, *out; + int needed; + unsigned char *buf; + char pubname[MAXPATHLEN], oldname[MAXPATHLEN]; + char *whoami = argv[0], *inname = argv[1], *outname = argv[2]; + + if (strchr(whoami, '/')) + whoami = strrchr(whoami, '/'); + + if (argc != 3) + { + com_err(NULL, 0, "Usage: %s infile outfile\n" + "where infile is a file of random data", whoami); + exit(1); + } + in = fopen(inname, "r"); + if (!in) + { + com_err(whoami, errno, "trying to open %s", inname); + exit(1); + } + + R_RandomInit(&random); + R_GetRandomBytesNeeded(&needed, &random); + buf = malloc(needed); + if (fread(buf, needed, 1, in) != 1) + { + com_err(whoami, 0, "Not enough random input data: need %d bytes\n", + needed); + exit(1); + } + R_RandomUpdate(&random, buf, needed); + + proto.bits = 1024; + proto.useFermat4 = 1; + + if (R_GeneratePEMKeys(&public, &private, &proto, &random)) + { + com_err(whoami, 0, "Couldn't generate key"); + exit(1); + } + + sprintf(oldname, "%s.old", outname); + rename(outname, oldname); + out = fopen(outname, "w"); + if (!out) + { + com_err(whoami, errno, "opening %s", outname); + exit(1); + } + if (fwrite(&private, sizeof(private), 1, out) != 1) + { + com_err(whoami, errno, "writing %s", outname); + exit(1); + } + fclose(out); + + sprintf(pubname, "%s.pub", outname); + sprintf(oldname, "%s.old", pubname); + rename(pubname, oldname); + out = fopen(pubname, "w"); + if (!out) + { + com_err(whoami, errno, "opening %s", pubname); + exit(1); + } + if (fwrite(&public, sizeof(public), 1, out) != 1) + { + com_err(whoami, errno, "writing %s", pubname); + exit(1); + } + fclose(out); + + sprintf(pubname, "%s.pub.txt", outname); + out = fopen(pubname, "w"); + if (!out) + { + com_err(whoami, errno, "opening %s", pubname); + exit(1); + } + printhex(out, public.modulus, MAX_RSA_MODULUS_LEN); + fclose(out); + + exit(0); +} + +char hexd[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + +void printhex(FILE *out, unsigned char *buf, int len) +{ + while (len--) + { + fprintf(out, "%c%c", hexd[*buf>>4], hexd[*buf%0x10]); + buf++; + } +} diff --git a/reg_svr/kerberos.c b/reg_svr/kerberos.c new file mode 100644 index 00000000..0f68a456 --- /dev/null +++ b/reg_svr/kerberos.c @@ -0,0 +1,208 @@ +/* $Id$ + * + * Kerberos routines for registration server + * + * Copyright (C) 1998 by the Massachusetts Institute of Technology + * For copying and distribution information, please see the file + * . + * + */ + +#include +#include +#include "reg_svr.h" + +#if !defined(KRB4) && !defined(KRB5) +#define KRB5 +#endif + +#include +#include + +#include + +#ifdef KRB4 +#include +#include +#include +#include +#endif + +#ifdef KRB5 +#include +#include +#include + +krb5_context context; +#endif + +RCSID("$Header$"); + +extern char *hostname, *shorthostname; + +#ifdef KRB5 +long init_kerberos(void) +{ + krb5_error_code code; + + /* Initialize Kerberos stuff. */ + code = krb5_init_context(&context); + if (code) + return code; + krb_set_tkt_string("/tmp/tkt_ureg"); + return 0; +} + +/* Check the kerberos database to see if a principal exists */ +long check_kerberos(char *username) +{ + krb5_error_code code; + krb5_creds creds; + krb5_data *realm; + krb5_timestamp now; +#ifdef KERBEROS_TEST_REALM + char ubuf[256]; + + sprintf(ubuf, "%s@%s", username, KERBEROS_TEST_REALM); + username = ubuf; +#endif + + memset(&creds, 0, sizeof(creds)); + code = krb5_parse_name(context, username, &creds.client); + if (code) + goto cleanup; + + realm = krb5_princ_realm(context, creds.client); + code = krb5_build_principal_ext(context, &creds.server, + realm->length, realm->data, + KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, + realm->length, realm->data, 0); + if (code) + goto cleanup; + + code = krb5_timeofday(context, &now); + if (code) + goto cleanup; + + creds.times.starttime = 0; + creds.times.endtime = now + 60; + + code = krb5_get_in_tkt_with_password(context, + 0 /* options */, + NULL /* addrs */, + NULL /* ktypes */, + NULL /* pre_auth_types */, + "x" /* password */, + NULL /* ccache */, + &creds, + NULL /* ret_as_reply */); + +cleanup: + krb5_free_principal(context, creds.client); + krb5_free_principal(context, creds.server); + + if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) + return MR_SUCCESS; + else + return MR_IN_USE; +} + +/* Create a new principal in Kerberos */ +long register_kerberos(char *username, char *password) +{ + void *kadm_server_handle = NULL; + kadm5_ret_t status; + kadm5_principal_ent_rec princ; + kadm5_config_params realm_params; + char admin_princ[256]; +#ifdef KERBEROS_TEST_REALM + char ubuf[256]; + + sprintf(admin_princ, "moira/%s@%s", hostname, KERBEROS_TEST_REALM); + sprintf(ubuf, "%s@%s", username, KERBEROS_TEST_REALM); + username = ubuf; + realm_params.realm = KERBEROS_TEST_REALM; + realm_params.mask = KADM5_CONFIG_REALM; +#else + sprintf(admin_princ, "moira/%s", hostname); + realm_params.mask = 0; +#endif + + status = krb5_parse_name(context, username, &(princ.principal)); + if (status) + return status; + + + status = kadm5_init_with_skey(admin_princ, NULL, KADM5_ADMIN_SERVICE, + &realm_params, KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, &kadm_server_handle); + if (status) + goto cleanup; + + status = kadm5_create_principal(kadm_server_handle, &princ, + KADM5_PRINCIPAL, password); + +cleanup: + krb5_free_principal(context, princ.principal); + if (kadm_server_handle) + kadm5_destroy(kadm_server_handle); + + if (status == KADM5_DUP) + return MR_IN_USE; + else return status; +} +#endif + +#ifdef KRB4 +char realm[REALM_SZ]; + +long init_kerberos(void) +{ + return krb_get_lrealm(realm, 1); +} + +long check_kerberos(char *username) +{ + long status; + + status = krb_get_pw_in_tkt(username, "", realm, "krbtgt", realm, 1, ""); + if (status == KDC_PR_UNKNOWN) + return MR_SUCCESS; + else + return MR_IN_USE; +} + +long register_kerberos(char *username, char *password) +{ + long status; + Kadm_vals new; + des_cblock key; + unsigned long *lkey = (unsigned long *)key; + + if ((status = krb_get_svc_in_tkt(MOIRA_SNAME, shorthostname, realm, + PWSERV_NAME, KADM_SINST, 1, KEYFILE))) + return status; + + if ((status = kadm_init_link(PWSERV_NAME, KADM_SINST, realm)) != + KADM_SUCCESS) + return status; + + memset(&new, 0, sizeof(new)); + SET_FIELD(KADM_DESKEY, new.fields); + SET_FIELD(KADM_NAME, new.fields); + + des_string_to_key(password, key); + new.key_low = htonl(lkey[0]); + new.key_high = htonl(lkey[1]); + strcpy(new.name, username); + + status = kadm_add(&new); + memset(&new, 0, sizeof(new)); + dest_tkt(); + + if (status == KADM_INUSE) + return MR_IN_USE; + else + return status; +} +#endif diff --git a/reg_svr/protocol.c b/reg_svr/protocol.c new file mode 100644 index 00000000..a5a7f01e --- /dev/null +++ b/reg_svr/protocol.c @@ -0,0 +1,329 @@ +/* $Id$ + * + * Reg_svr protocol and encryption/decryption routines + * + * Copyright (C) 1998 by the Massachusetts Institute of Technology + * For copying and distribution information, please see the file + * . + * + */ + +#include +#include +#include "reg_svr.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* RSARef includes */ +#include "global.h" +#include "rsaref.h" + +RCSID("$Header$"); + +R_RSA_PRIVATE_KEY *rsa_key; +char *emsg[NUM_REG_ERRORS], *ename[NUM_REG_ERRORS]; +extern char *whoami; + +struct _handler { + char *name; + void (*handler)(reg_client *rc, int argc, char **argv); +} handlers[] = { + { "RIFO", RIFO }, + { "SWRD", SWRD }, + { "LOGN", LOGN }, + { "PSWD", PSWD }, + { "QUIT", QUIT }, + { NULL, NULL } +}; + +void parse_pdu(reg_client *rc, long len, char *buf); +void printhex(unsigned char *buf, int len); + +int read_rsa_key(void) +{ + struct stat statbuf; + int fd; + + if (stat(REG_SVR_RSA_KEY, &statbuf)) + return 0; + + fd = open(REG_SVR_RSA_KEY, O_RDONLY); + if (!fd) + return 0; + + rsa_key = malloc(statbuf.st_size); + if (!rsa_key) + return 0; + + if (read(fd, rsa_key, statbuf.st_size) != statbuf.st_size) + return 0; + + close(fd); + return 1; +} + +int read_errors(void) +{ + int i; + char errbuf[100], *p; + FILE *errs; + + errs = fopen(REG_SVR_ERROR_MESSAGES, "r"); + if (!errs) + return 0; + for (i = 0; i < NUM_REG_ERRORS && !feof(errs); i++) + { + if (errbuf[0] != '#' || errbuf[1] != ' ') + sprintf(errbuf, "# %d", i); + ename[i] = strdup(errbuf + 2); + if (ename[i][strlen(ename[i]) - 1] == '\n') + ename[i][strlen(ename[i]) - 1] = '\0'; + emsg[i] = strdup(""); + if (!ename[i] || !emsg[i]) + return 0; + while (1) { + if (!fgets(errbuf, sizeof(errbuf) - 1, errs)) + break; + if (*errbuf == '#') + break; + + if ((p = strchr(errbuf, '\n')) > errbuf) + { + *p = ' '; + *(p + 1) = '\0'; + } + emsg[i] = realloc(emsg[i], strlen(emsg[i]) + strlen(errbuf) + 1); + if (!emsg[i]) + return 0; + strcat(emsg[i], errbuf); + } + } + fclose(errs); + + if (i < NUM_REG_ERRORS) + { + com_err(whoami, 0, "Not enough error messages in %s", + REG_SVR_ERROR_MESSAGES); + exit(1); + } + return 1; +} + +void parse_packet(reg_client *rc, int type, long len, char *buf, int sleeping) +{ + switch (type) + { + case REG_RSA_ENCRYPTED_KEY: + { + unsigned char key[MAX_ENCRYPTED_KEY_LEN]; + unsigned int keylen; + + if (RSAPrivateDecrypt(key, &keylen, buf, len, rsa_key) || keylen != 8) + { + reply(rc, ENCRYPT_KEY, "INIT", "c", NULL); + return; + } + des_key_sched(key, rc->sched); + rc->encrypted = 1; + + if (sleeping) + reply(rc, DATABASE_CLOSED, "INIT", "c", NULL); + else + reply(rc, NO_MESSAGE, "GETN", "c", NULL); + return; + } + + case REG_ENCRYPTED: + { + char *outbuf, iv[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + if (!rc->encrypted) + { + reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, + "Encrypted packet unexpected"); + return; + } + + outbuf = malloc(len + 7); + if (!outbuf) + { + reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory"); + return; + } + des_cbc_encrypt(buf, outbuf, len, rc->sched, iv, 0); + + /* Undo PKCS#5 padding */ + len -= outbuf[len - 1]; + + parse_pdu(rc, len - 8, outbuf + 8); + free(outbuf); + return; + } + +#ifdef ALLOW_UNENCRYPTED + case REG_UNENCRYPTED: + parse_pdu(rc, len, buf); + return; +#endif + + default: + com_err(whoami, 0, "Bad packet (type %d, len %d)", type, len); + rc->lastmod = 0; + } +} + +void parse_pdu(reg_client *rc, long len, char *buf) +{ + char **argv, *p; + int argc, i; + void (*handler)(reg_client *rc, int argc, char **argv) = NULL; + + if (len < 8 || strcmp(buf, "v1")) + { + com_err(whoami, 0, "Bad packet version number %s", buf); + reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL); + return; + } + buf += 3; + len -= 3; + + for (i = 0; handlers[i].name; i++) + { + if (!strcmp(buf, handlers[i].name)) + { + handler = handlers[i].handler; + break; + } + } + if (!handler) + { + com_err(whoami, 0, "Bad packet request %s", buf); + reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL); + return; + } + buf += 5; + len -= 5; + + for (argc = 0, p = buf; p < buf + len; p++) + { + if (!*p) + argc++; + } + + argv = malloc(argc * sizeof(char *)); + if (!argv) + { + com_err(whoami, 0, "in parse_pdu"); + reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory"); + return; + } + + fprintf(stderr, "%s[#%d]: %s", whoami, rc->clientid, handlers[i].name); + for (argc = 0, p = buf - 1; p < buf + len - 1; p++) + { + if (!*p) + { + argv[argc++] = p + 1; + if (strcmp(handlers[i].name, "PSWD") != 0) + fprintf(stderr, " '%s'", p + 1); + } + } + fprintf(stderr, "\n"); + fflush(stderr); + + for (i = 0; i < argc; i++) + strtrim(argv[i]); + handler(rc, argc, argv); + free(argv); +} + +void reply(reg_client *rc, int msg, char *state, char *clean, char *data, + ...) +{ + /* reply() can't malloc, since it might be returning an "out of memory" + error. We'll use a static buffer which is much larger than any + message we'd be returning, and callers have to make sure that any + user-generated data is length-limited. */ + static char buf[8192], outbuf[8192]; + char *p; + int len, pad, pcount; + va_list ap; + long junk; + unsigned short *nrand; + + com_err(whoami, 0, "Reply: %s, go to state %s %s", ename[msg], state, clean); + + seed48(rc->random); + junk = lrand48(); + memcpy(buf + 3, &junk, 4); + junk = lrand48(); + memcpy(buf + 7, &junk, 4); + nrand = seed48(rc->random); + memcpy(rc->random, nrand, 48); + + memcpy(buf + 11, "v1", 3); + memcpy(buf + 14, state, len = strlen(state) + 1); + p = buf + 14 + len; + va_start(ap, data); + p += vsprintf(p, emsg[msg], ap); + va_end(ap); + *p++ = '\0'; + memcpy(p, clean, len = strlen(clean) + 1); + p += len; + if (data) + { + memcpy(p, data, len = strlen(data) + 1); + p += len; + } + + len = p - (buf + 3); + pad = 8 - len % 8; + for (pcount = pad; pcount; pcount--) + buf[3 + len++] = pad; + + if (rc->encrypted) + { + char iv[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + des_cbc_encrypt(buf + 3, outbuf + 3, len, rc->sched, iv, 1); + p = outbuf; + *p = REG_ENCRYPTED; + } + else + { + p = buf; + *p = REG_UNENCRYPTED; + } + + p[1] = len / 256; + p[2] = len % 256; + write(rc->fd, p, len + 3); + + /* If we're going to INIT, set lastmod to 0 to cause the connection + to be closed once we return to the main loop */ + if (!strcmp(state, "INIT")) + rc->lastmod = 0; +} + +char hexd[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + +void printhex(unsigned char *buf, int len) +{ + while (len--) + { + printf("%c%c", hexd[*buf>>4], hexd[*buf%0x10]); + buf++; + } + printf("\n"); +} diff --git a/reg_svr/reg_svr.c b/reg_svr/reg_svr.c deleted file mode 100644 index daef5ab7..00000000 --- a/reg_svr/reg_svr.c +++ /dev/null @@ -1,968 +0,0 @@ -/* $Id$ - * - * Server for user registration with Moira and Kerberos. - * - * This program is a client of the Kerberos admin_server and a - * server for the userreg program. It is not a client of the - * Moira server as it is linked with libmoiraglue which bypasses - * the network protocol. - * - * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology - * For copying and distribution information, please see the file - * . - */ - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "reg_svr.h" - -RCSID("$Header$"); - -extern char admin_errmsg[]; - -FILE *journal; -char *whoami; - -int parse_encrypted(struct msg *message, struct db_data *data); -int parse_encrypted(struct msg *message, struct db_data *data); -int db_callproc(int argc, char **argv, void *queue); -int find_user(struct msg *message); -int verify_user(struct msg *message, char *retval); -int ureg_kadm_init(void); -int reserve_krb(char *login); -int setpass_krb(char *login, char *password); -int reserve_user(struct msg *message, char *retval); -int set_final_status(struct msg *message); -int set_password(struct msg *message, char *retval); -int getuserinfo(int argc, char **argv, void *qa); -int set_identity(struct msg *message, char *retval); -int get_secure(struct msg *message, char *retval); -int set_secure(struct msg *message, char *retval); - -int main(int argc, char *argv[]) -{ - struct msg message; /* Storage for parsed packet */ - int status = SUCCESS; /* Error status */ - char retval[BUFSIZ]; /* Buffer to hold return message for client */ - - /* Initialize */ - whoami = argv[0]; - - /* Error messages sent one line at a time */ - setvbuf(stderr, NULL, _IOLBF, BUFSIZ); - setvbuf(stdout, NULL, _IOLBF, BUFSIZ); - - /* Initialize com_err error tables */ - init_ureg_err_tbl(); - init_krb_err_tbl(); - init_kadm_err_tbl(); - initialize_gdss_error_table(); - - /* Set the name of our kerberos ticket file */ - krb_set_tkt_string("/tmp/tkt_ureg"); - - /* Connect to the Moira server */ - if ((status = mr_connect(MOIRA_SERVER)) != MR_SUCCESS) - { - com_err(whoami, status, " on mr_connect"); - exit(1); - } - - /* Authorize, telling the server who you are */ - if ((status = mr_auth(whoami)) != MR_SUCCESS) - { - com_err(whoami, status, " on mr_auth"); - exit(1); - } - - journal = fopen(REGJOURNAL, "a"); - if (!journal) - { - com_err(whoami, errno, " while opening journal file"); - exit(1); - } - - /* Allow request layer to initialize */ - req_initialize(); - - /* Sit around waiting for requests from the client. */ - for (;;) - { - get_request(&message); - - switch (message.request) - { - case UREG_VERIFY_USER: - status = verify_user(&message, retval); - break; - case UREG_RESERVE_LOGIN: - status = reserve_user(&message, retval); - break; - case UREG_SET_PASSWORD: - case UREG_GET_KRB: - status = set_password(&message, retval); - break; - case UREG_SET_IDENT: - status = set_identity(&message, retval); - break; - case UREG_GET_SECURE: - status = get_secure(&message, retval); - break; - case UREG_SET_SECURE: - status = set_secure(&message, retval); - break; - default: - status = UREG_UNKNOWN_REQUEST; - critical_alert(FAIL_INST, "Unknown request %d from userreg.", - message.request); - break; - } - - /* Report what happened to client */ - report(status, retval); - } -} - -/* This routine makes sure that the ID from the database matches - the ID sent accross in the packet. The information in the packet - was created in the following way: - - The database used to contain encrypted IDs. Now we don't encrypt - them in the database, although there are still some encrypted IDs - there. - - The plain text ID number was encrypted via EncryptID() resulting - in the form that would appear in the Moira database. This is - concatenated to the plain text ID so that the ID string contains plain - text ID followed by a null followed by the encrypted ID. Other - information such as the username or password is appended. The whole - thing is then DES encrypted using the encrypted ID as the source of - the key. - - This routine tries each ID in the database that belongs - to someone with this user's first and last name and tries to - decrypt the packet with this information. If it succeeds, it returns - zero and initializes all the fields of the formatted packet structure - that depend on the encrypted information. */ - -int parse_encrypted(struct msg *message, struct db_data *data) -{ - des_cblock key; /* The key for DES en/decryption */ - des_key_schedule sched; /* En/decryption schedule */ - static char decrypt[BUFSIZ]; /* Buffer to hold decrypted information */ - long decrypt_len; /* Length of decypted ID information */ - static char hashid[14]; /* Buffer to hold one-way encrypted ID */ - char idnumber[BUFSIZ]; /* Buffer to hold plain-text ID */ - char *temp; /* A temporary string pointer */ - int len; /* Keeps track of length left in packet */ - int status = SUCCESS; /* Error status */ - - /* Make the decrypted information length the same as the encrypted - information length. Both are integral multples of eight bytes - because of the DES encryption routines. */ - decrypt_len = message->encrypted_len; - - /* Get key from the possibly one-way encrypted ID in the Moira database */ - if (data->mit_id[0] >= '0' && data->mit_id[0] <= '9') - { - char buf[32]; - - EncryptID(buf, data->mit_id, message->first, message->last); - des_string_to_key(buf, key); - } - else - des_string_to_key(data->mit_id, key); - - /* Get schedule from key */ - des_key_sched(key, sched); - /* Decrypt information from packet using this key. Since decrypt_len - is an integral multiple of eight bytes, it will probably be null- - padded. */ - des_pcbc_encrypt(message->encrypted, decrypt, decrypt_len, - sched, key, DES_DECRYPT); - - /* Extract the plain text and encrypted ID fields from the decrypted - packet information. */ - /* Since the decrypted information starts with the plain-text ID - followed by a null, if the decryption worked, this will only - copy the plain text part of the decrypted information. It is - important that strncpy be used because if we are not using the - correct key, there is no guarantee that a null will occur - anywhere in the string. */ - strncpy(idnumber, decrypt, decrypt_len); - /* Check that the idnumber of a mismatched decryption doesn't overflow - * the buffer. */ - if (strlen(idnumber) != 9) - return FAILURE; - - /* Point temp to the end of the plain text ID number. */ - temp = decrypt + strlen(idnumber) + 1; - /* Find out how much more packet there is. */ - len = message->encrypted_len - (temp - decrypt); - /* Copy the next CRYPT_LEN bytes of the decrypted information into - hashid if there are CRYPT_LEN more bytes to copy. There will be - if we have the right key. */ - strncpy(hashid, temp, min(len, CRYPT_LEN)); - /* Point temp to the end of the encrypted ID field */ - temp += strlen(hashid) + 1; - /* Find out how much more room there is. */ - len = message->encrypted_len - (temp - decrypt); - - /* Now compare encrypted ID and clear text ID for a match. */ - if (strcmp(hashid, data->mit_id) && - strcmp(idnumber, data->mit_id)) - status = FAILURE; - - if (status == SUCCESS) - { - /* We made it. Now we can finish initializing message. */ - /* Point leftover to whatever is left over! */ - message->leftover = temp; - message->leftover_len = len; - /* Since we know we have the right user, fill in the information - from the Moira database. */ - message->db.reg_status = data->reg_status; - strncpy(message->db.uid, data->uid, sizeof(message->db.uid)); - strncpy(message->db.mit_id, data->mit_id, sizeof(message->db.mit_id)); - strncpy(message->db.login, data->login, sizeof(message->db.login)); - } - - return status; -} - -/* This function is called by mr_query after each tuple found. It is - used by find_user to cache information about each user found. */ -int db_callproc(int argc, char **argv, void*queue) -{ - struct db_data *data; /* Structure to store the information in */ - int status = SUCCESS; /* Error status */ - - if (argc != U_END) - { - critical_alert(FAIL_INST, "Wrong number of arguments returned " - "from get_user_account_by_name."); - status = MR_ABORT; - } - else - { - /* extract the needed information from the results of the Moira query */ - data = malloc(sizeof(struct db_data)); - data->reg_status = atoi(argv[U_STATE]); - strncpy(data->login, argv[U_NAME], sizeof(data->login)); - strncpy(data->mit_id, argv[U_MITID], sizeof(data->mit_id)); - strncpy(data->uid, argv[U_UID], sizeof(data->uid)); - sq_save_data(queue, data); - } - - return status; -} - -/* This routine verifies that a user is allowed to register by finding - him/her in the Moira database. It returns the status of the Moira - query that it calls. */ -int find_user(struct msg *message) -{ -#define GUBN_ARGS 2 /* Arguements needed by get_user_by_name */ - char *q_name; /* Name of Moira query */ - int q_argc; /* Number of arguments for query */ - char *q_argv[GUBN_ARGS]; /* Arguments to query */ - int status = SUCCESS; /* Query return status */ - - struct save_queue *queue; /* Queue to hold Moira data */ - struct db_data *data; /* Structure for data for one tuple */ - short verified = FALSE; /* Have we verified the user? */ - - /* Zero the mit_id field in the formatted packet structure. This - being zeroed means that no user was found. */ - memset(message->db.mit_id, 0, sizeof(message->db.mit_id)); - - if (status == SUCCESS) - { - /* Get ready to make a Moira query */ - q_name = "get_user_account_by_name"; - q_argc = GUBN_ARGS; /* #defined in this routine */ - q_argv[0] = message->first; - q_argv[1] = message->last; - - /* Create queue to hold information */ - queue = sq_create(); - - /* Do it */ - status = mr_query(q_name, q_argc, q_argv, db_callproc, queue); - - if (status == MR_SUCCESS) - { - /* Traverse the list, freeing data as we go. If sq_get_data() - returns zero if there is no more data on the queue. */ - while (sq_get_data(queue, &data)) - { - if (!verified) - /* parse_encrypted returns zero on success */ - verified = (parse_encrypted(message, data) == SUCCESS); - free(data); - } - } - - /* Destroy the queue */ - sq_destroy(queue); - } - - return status; -} - -/* This routine determines whether a user is in the databse and returns - his state so that other routines can figure out whether he is the - correct state for various transactions. */ -int verify_user(struct msg *message, char *retval) -{ - int status = SUCCESS; /* Return status */ - - /* Log that we are about to veryify user */ - com_err(whoami, 0, "verifying user %s %s", message->first, message->last); - - /* Figure out what user (if any) can be found based on the - encrypted information in the packet. (See the comment on - parse_encrypted().) */ - - status = find_user(message); - - /* If Moira coudn't find the user */ - if (status == MR_NO_MATCH) - status = UREG_USER_NOT_FOUND; - else if (status == MR_SUCCESS) - { - /* If the information sent over in the packet did not point to a - valid user, the mit_id field in the formatted packet structure - will be empty. */ - if (message->db.mit_id[0] == '\0') - status = UREG_USER_NOT_FOUND; - /* If the user was found but the registration has already started, - use this as the status */ - else - { - switch (message->db.reg_status) - { - case US_NO_LOGIN_YET: - status = SUCCESS; - break; - case US_REGISTERED: - status = UREG_ALREADY_REGISTERED; - break; - case US_NO_PASSWD: - status = UREG_NO_PASSWD_YET; - break; - case US_DELETED: - status = UREG_DELETED; - break; - case US_NOT_ALLOWED: - status = UREG_NOT_ALLOWED; - break; - case US_ENROLLED: - status = UREG_ENROLLED; - break; - case US_ENROLL_NOT_ALLOWED: - status = UREG_ENROLL_NOT_ALLOWED; - break; - case US_HALF_ENROLLED: - status = UREG_HALF_ENROLLED; - break; - default: - status = UREG_MISC_ERROR; - critical_alert(FAIL_INST, "Bad user state %d for login %s.", - message->db.reg_status, message->db.login); - break; - } - /* Set retval to the login name so that the client can use - it in the error message it will give the user. */ - strcpy(retval, message->db.login); - } - } - - if (status) - com_err(whoami, status, " returned from verify_user"); - else - com_err(whoami, 0, "User verified"); - - return status; -} - -int ureg_kadm_init(void) -{ - unsigned int status = SUCCESS; /* Return status */ - static char krbrealm[REALM_SZ]; /* kerberos realm name */ - static char *host; /* local hostname in principal fmt */ - static int inited = 0; - char *p; - struct utsname uts; - - if (!inited) - { - inited++; - memset(krbrealm, 0, sizeof(krbrealm)); - if ((status = krb_get_lrealm(krbrealm, 1))) - { - status += krb_err_base; - com_err(whoami, status, " fetching kerberos realm"); - exit(1); - } - if (uname(&uts) < 0) - com_err(whoami, errno, "getting local hostname"); - host = canonicalize_hostname(strdup(uts.nodename)); - for (p = host; *p && *p != '.'; p++) - { - if (isupper(*p)) - *p = tolower(*p); - } - *p = 0; - } - - /* Get keys for interacting with Kerberos admin server. */ - /* principal, instance, realm, service, service instance, life, file */ - if ((status = krb_get_svc_in_tkt(MOIRA_SNAME, host, krbrealm, PWSERV_NAME, - KADM_SINST, 1, KEYFILE))) - status += krb_err_base; - - if (status != SUCCESS) - com_err(whoami, status, " while get admin tickets"); - else - { - if ((status = kadm_init_link(PWSERV_NAME, KADM_SINST, krbrealm)) != - KADM_SUCCESS) - com_err(whoami, status, " while initializing kadmin connection"); - } - - return status; -} - -/* - * This routine reserves a principal in kerberos by setting up a - * principal with a random initial key. - */ -int reserve_krb(char *login) -{ - int status = SUCCESS; - Kadm_vals new; - des_cblock key; - u_long *lkey = (u_long *)key; - - if ((status = ureg_kadm_init()) == SUCCESS) - { - memset(&new, 0, sizeof(new)); - SET_FIELD(KADM_DESKEY, new.fields); - SET_FIELD(KADM_NAME, new.fields); - - des_random_key(key); - new.key_low = htonl(lkey[0]); - new.key_high = htonl(lkey[1]); - strcpy(new.name, login); - - com_err(whoami, 0, "Creating kerberos principal for %s", login); - status = kadm_add(&new); - if (status != KADM_SUCCESS) - com_err(whoami, status, " while reserving principal"); - - memset(&new, 0, sizeof(new)); - } - - dest_tkt(); - - return status; -} - -/* - * This routine reserves a principal in kerberos by setting up a - * principal with a random initial key. - */ -int setpass_krb(char *login, char *password) -{ - int status = SUCCESS; - Kadm_vals new; - des_cblock key; - u_long *lkey = (u_long *)key; - - if ((status = ureg_kadm_init()) == SUCCESS) - { - memset(&new, 0, sizeof(new)); - SET_FIELD(KADM_DESKEY, new.fields); - SET_FIELD(KADM_NAME, new.fields); - - des_string_to_key(password, key); - new.key_low = htonl(lkey[0]); - new.key_high = htonl(lkey[1]); - strcpy(new.name, login); - - com_err(whoami, 0, "Setting password for %s", login); - /* First arguement is not used if user has modify privileges */ - if ((status = kadm_mod(&new, &new)) != KADM_SUCCESS) - { - if (status == KADM_NOENTRY) - { - com_err(whoami, 0, "kerberos principal doesn't exist; creating"); - if ((status = kadm_add(&new)) != KADM_SUCCESS) - com_err(whoami, status, " while creating kerberos principal"); - } - else - com_err(whoami, status, " while setting password"); - } - } - - dest_tkt(); - return status; -} - -int reserve_user(struct msg *message, char *retval) -{ - int q_argc; /* Number of arguments to query */ - char *q_argv[3]; /* Arguments to Moira query */ - char *q_name; /* Name of Moira query */ - int status = SUCCESS; /* General purpose error status */ - char fstype_buf[7]; /* Buffer to hold fs_type, a 16 bit number */ - char *login; /* The login name the user wants */ - int i; /* A counter */ - - /* Log that we are about to reserve a user. */ - com_err(whoami, 0, "reserving user %s %s", message->first, message->last); - - /* Check to make sure that we can verify this user. */ - if ((status = verify_user(message, retval)) == SUCCESS) - { - /* Get the requested login name from leftover packet information. */ - login = message->leftover; - - /* Check the login name for validity. The login name is currently - is allowed to contain lowercase letters in any position and - and numbers and underscore characters in any position but the - first. */ - if ((strlen(login) < MIN_UNAME) || (strlen(login) > MAX_UNAME)) - status = UREG_INVALID_UNAME; - } - if (status == SUCCESS) - if ((login[0] == '_') || isdigit(login[0])) - status = UREG_INVALID_UNAME; - - if (status == SUCCESS) - { - for (i = 0; i < strlen(login); i++) - { - if (!islower(login[i]) && !isdigit(login[i]) && - (login[i] != '_')) - { - status = UREG_INVALID_UNAME; - break; - } - } - } - if (status == SUCCESS) - { - /* Now that we have a valid user with a valid login... */ - - /* First, try to reserve the user in Moira. */ - sprintf(fstype_buf, "%d", MR_FS_STUDENT); - q_name = "register_user"; - q_argv[0] = message->db.uid; - q_argv[1] = login; - q_argv[2] = fstype_buf; - q_argc = 3; - status = mr_query(q_name, q_argc, q_argv, NULL, NULL); - switch (status) - { - case MR_SUCCESS: - status = SUCCESS; - break; - case MR_IN_USE: - status = UREG_LOGIN_USED; - break; - case MR_DEADLOCK: - status = UREG_MISC_ERROR; - break; - default: - critical_alert(FAIL_INST, "%s returned from register_user.", - error_message(status)); - status = UREG_MISC_ERROR; - break; - } - } - - if (status == SUCCESS) - { - /* - * Moira login was successfully created; try to reserve kerberos - * principal. - * - * If this routine fails, store the login in the retval so - * that it can be used in the client-side error message. - */ - if ((status = reserve_krb(login)) != SUCCESS) - strcpy(retval, login); - } - - if (status) - com_err(whoami, status, " returned from reserve_user"); - else - com_err(whoami, 0, "User reserved"); - - return status; -} - -/* This routine updates a user's registration status to fully - registered. */ -int set_final_status(struct msg *message) -{ - char *login; - char *q_name; /* Name of Moira query */ - int q_argc; /* Number of arguments for Moira query */ - char *q_argv[2]; /* Arguments to get user by uid */ - char state[7]; /* Can hold a 16 bit integer */ - int status; /* Error status */ - - if (message->request == UREG_SET_PASSWORD) - sprintf(state, "%d", US_REGISTERED); - else if (message->db.reg_status == US_NO_LOGIN_YET) - sprintf(state, "%d", US_ENROLLED); - else - sprintf(state, "%d", US_ENROLL_NOT_ALLOWED); - - login = message->db.login; - com_err(whoami, 0, "Setting final status for %s to %s", login, state); - - q_name = "update_user_status"; - q_argc = 2; - q_argv[0] = login; - q_argv[1] = state; - if ((status = mr_query(q_name, q_argc, q_argv, NULL, NULL)) - != MR_SUCCESS) - { - if (status == MR_DEADLOCK) - status = UREG_MISC_ERROR; - else - critical_alert(FAIL_INST, "%s returned from update_user_status.", - error_message(status)); - } - if (status) - com_err(whoami, status, " returned from set_final_status"); - else - com_err(whoami, 0, "Final status set"); - return status; -} - - -/* This routine is used to set the initial password for the new user. */ -int set_password(struct msg *message, char *retval) -{ - int status = SUCCESS; /* Return status */ - char *passwd; /* User's password */ - - com_err(whoami, 0, "setting password %s %s", message->first, message->last); - - status = verify_user(message, retval); - - /* Don't set the password unless the registration status of the user - is that he exists and has no password. */ - if (status == SUCCESS) - status = UREG_NO_LOGIN_YET; - if ((message->request == UREG_SET_PASSWORD && - status == UREG_NO_PASSWD_YET) || - (message->request == UREG_GET_KRB && - status == UREG_HALF_ENROLLED)) - { - /* User is in proper state for this transaction. */ - - passwd = message->leftover; - - /* Set password. */ - if ((status = setpass_krb(message->db.login, passwd)) != SUCCESS) - /* If failure, allow login name to be used in client error message */ - strcpy(retval, message->db.login); - else - /* Otherwise, mark user as finished. */ - status = set_final_status(message); - } - - if (status) - com_err(whoami, status, " returned from set_passwd"); - else - com_err(whoami, 0, "Password set"); - - return status; -} - - -int getuserinfo(int argc, char **argv, void *qa) -{ - char **qargv = qa; - int status = SUCCESS; - int i; - - if (argc != U_END) - { - critical_alert(FAIL_INST, - "Wrong number of args returned from get_user_by_uid"); - status = MR_ABORT; - } - else - { - qargv[U_NAME] = strdup(argv[U_NAME]); - for (i = 1; i < U_MODTIME; i++) - qargv[i + 1] = strdup(argv[i]); - qargv[U_MODTIME + 1] = NULL; - } - return status; -} - - -int set_identity(struct msg *message, char *retval) -{ - char *q_argv[U_END]; /* Arguments to Moira query */ - int status = SUCCESS; /* General purpose error status */ - char *login; /* The login name the user wants */ - int i; /* A counter */ - - /* Log that we are about to reserve a user. */ - com_err(whoami, 0, "setting identity %s %s", - message->first, message->last); - - /* Check to make sure that we can verify this user. */ - status = verify_user(message, retval); - if (status == SUCCESS || status == UREG_NOT_ALLOWED) - { - status = SUCCESS; - /* Get the requested login name from leftover packet information. */ - login = message->leftover; - - /* Check the login name for validity. The login name is currently - is allowed to contain lowercase letters in any position and - and numbers and underscore characters in any position but the - first. */ - if ((strlen(login) < MIN_UNAME) || (strlen(login) > MAX_UNAME)) - status = UREG_INVALID_UNAME; - } - if (status == SUCCESS) - { - if ((login[0] == '_') || isdigit(login[0])) - status = UREG_INVALID_UNAME; - } - if (status == SUCCESS) - { - for (i = 0; i < strlen(login); i++) - { - if (!islower(login[i]) && !isdigit(login[i]) && - (login[i] != '_')) - { - status = UREG_INVALID_UNAME; - break; - } - } - } - if (status == SUCCESS) - { - /* Now that we have a valid user with a valid login... */ - - q_argv[0] = message->db.uid; - status = mr_query("get_user_account_by_uid", 1, q_argv, - getuserinfo, q_argv); - if (status != SUCCESS) - { - com_err(whoami, status, " while getting user info"); - return status; - } - q_argv[U_NAME + 1] = login; - q_argv[U_STATE + 1] = "7"; - q_argv[U_SIGNATURE + 1] = ""; - status = mr_query("update_user_account", U_MODTIME + 1, q_argv, - NULL, NULL); - switch (status) - { - case MR_SUCCESS: - status = SUCCESS; - break; - case MR_IN_USE: - case MR_NOT_UNIQUE: - status = UREG_LOGIN_USED; - break; - case MR_DEADLOCK: - status = UREG_MISC_ERROR; - break; - default: - critical_alert(FAIL_INST, "%s returned from update_user_account.", - error_message(status)); - status = UREG_MISC_ERROR; - break; - } - } - if (status == SUCCESS) - { - /* Moira login was successfully created; try to reserve kerberos - principal. */ - /* If this routine fails, store the login in the retval so - that it can be used in the client-side error message. */ - if ((status = reserve_krb(login)) != SUCCESS) - strcpy(retval, login); - } - - if (status) - com_err(whoami, status, " returned from set_identity"); - else - com_err(whoami, 0, "Identity set"); - - return status; -} - - -/* Find out if someone's secure instance password is set. - * Returns UREG_ALREADY_REGISTERED if set, SUCCESS (0) if not. - */ - -int get_secure(struct msg *message, char *retval) -{ - int status; - char *argv[U_END]; - - com_err(whoami, 0, "checking status of secure password for %s", - message->first); - argv[0] = message->first; - status = mr_query("get_user_account_by_login", 1, argv, getuserinfo, argv); - if (status != SUCCESS) - { - com_err(whoami, status, " while getting user info"); - return status; - } - if (atoi(argv[U_SECURE + 1])) - return UREG_ALREADY_REGISTERED; - return SUCCESS; -} - - -/* Set someone's secure instance password. */ - -int set_secure(struct msg *message, char *retval) -{ - int status; - char *argv[U_END], *bp, buf[512], *passwd, *id; - KTEXT_ST creds; - AUTH_DAT auth; - C_Block key; - Key_schedule keys; - Kadm_vals kv; - u_long *lkey = (u_long *)key; - struct timeval now; - static int inited = 0; - static char *host; - struct utsname uts; - - if (!inited) - { - inited++; - if (uname(&uts) < 0) - com_err(whoami, errno, "getting local hostname"); - host = strdup(krb_get_phost(uts.nodename)); - } - - com_err(whoami, 0, "setting secure passwd for %s", message->first); - argv[0] = message->first; - status = mr_query("get_user_account_by_login", 1, argv, getuserinfo, argv); - if (status != SUCCESS) - { - com_err(whoami, status, " while getting user info"); - return status; - } - if (atoi(argv[U_SECURE + 1])) - { - com_err(whoami, UREG_ALREADY_REGISTERED, "in set_secure()"); - return UREG_ALREADY_REGISTERED; - } - - bp = message->encrypted; - /* round up to word boundary */ - bp = (char *)((((u_long)bp + 3) >> 2) << 2); - - creds.length = ntohl(*((int *)bp)); - bp += sizeof(int); - memcpy(creds.dat, bp, creds.length); - creds.mbz = 0; - bp += creds.length; - - status = krb_rd_req(&creds, "changepw", host, cur_req_sender(), &auth, ""); - if (status) - { - status += krb_err_base; - com_err(whoami, status, " verifying credentials in set_secure()"); - return status; - } - - message->leftover_len = ntohl(*((int *)(bp))); - bp += sizeof(int); - message->leftover = bp; - - des_key_sched(auth.session, keys); - des_pcbc_encrypt(message->leftover, buf, message->leftover_len, - keys, auth.session, 0); - - id = buf; - passwd = strchr(buf, ','); - *passwd++ = 0; - - if (strcmp(id, argv[U_MITID + 1])) - { - char buf[32]; - - EncryptID(buf, id, argv[U_FIRST + 1], argv[U_LAST + 1]); - if (strcmp(buf, argv[U_MITID + 1])) - { - status = UREG_USER_NOT_FOUND; - com_err(whoami, status, "IDs mismatch: %s (%s), %s", id, buf, - argv[U_MITID + 1]); - return status; - } - } - - /* now do actual password setting stuff */ - - if ((status = ureg_kadm_init()) != SUCCESS) - { - com_err(whoami, status, "initing kadm stuff"); - return status; - } - - memset(&kv, 0, sizeof(kv)); - SET_FIELD(KADM_DESKEY, kv.fields); - SET_FIELD(KADM_NAME, kv.fields); - SET_FIELD(KADM_INST, kv.fields); - des_string_to_key(passwd, key); - kv.key_low = htonl(lkey[0]); - kv.key_high = htonl(lkey[1]); - strcpy(kv.name, message->first); - strcpy(kv.instance, "extra"); - - if ((status = kadm_add(&kv)) != KADM_SUCCESS) - { - com_err(whoami, status, " while creating kerberos principal"); - return status; - } - - argv[0] = message->first; - argv[1] = buf; - gettimeofday(&now, NULL); - sprintf(buf, "%ld", now.tv_sec); - status = mr_query("update_user_security_status", 2, argv, getuserinfo, argv); - if (status != SUCCESS) - { - com_err(whoami, status, " while updating user status"); - return status; - } - return SUCCESS; -} diff --git a/reg_svr/reg_svr.h b/reg_svr/reg_svr.h index a5f0d54e..a4d26e20 100644 --- a/reg_svr/reg_svr.h +++ b/reg_svr/reg_svr.h @@ -1,77 +1,77 @@ -/* - * $Source$ - * $Author$ - * $Header$ - * - * Copyright (C) 1987 by the Massachusetts Institute of Technology - * - * Server for user registration with Moira and Kerberos. - * - * This file contains all the information needed by all source - * files for the user registration server. - */ +#include +#include -#include -#include -#include "ureg_err.h" -#define REG_SVR -#include "ureg_proto.h" +typedef struct reg_client { + int fd; /* socket */ + int clientid; /* client id */ + unsigned char *buf; /* buffer for data read */ + int nread, nmax; /* # of bytes cur/max in buffer */ + time_t lastmod; /* last time we sent/received */ + int encrypted; /* set if sched is valid */ + des_key_schedule sched; /* DES key schedule */ + long uid; /* uid of user. set by RIFO */ + char *id; /* stored MIT ID if "six words" are + needed. NULL otherwise. Set by + RIFO, cleared by SWRD */ + char *suggestions; /* suggested usernames */ + char *username; /* desired username. set by LOGN */ + int reserved_username; /* if username wasn't picked by user */ + unsigned short *random; /* random state */ +} reg_client; -#ifndef TRUE -#define TRUE 1 -#endif +void RIFO(reg_client *rc, int argc, char **argv); +void SWRD(reg_client *rc, int argc, char **argv); +void LOGN(reg_client *rc, int argc, char **argv); +void PSWD(reg_client *rc, int argc, char **argv); +void QUIT(reg_client *rc, int argc, char **argv); -#ifndef FALSE -#define FALSE 0 -#endif +#define REG_SVR_PRINCIPAL "sms" +#define REG_SVR_INSTANCE "" -#define FAIL_INST "reg_svr" /* Instance for failure zephyrgrams */ +#define REG_SVR_RSA_KEY "/moira/reg_svr/rsa_key" +#define REG_SVR_HMAC_KEY "/moira/reg_svr/hmac_key" -#define CUR_UREG_VERSION 1 /* Version for the register protocol */ -#define SUCCESS 0 /* General purpose success code */ -#define FAILURE 1 /* To use when any non-zero number will work */ -#define min(a,b) ((a) > (b) ? (b) : (a)) -#define MIN_UNAME 3 /* Username must be between three and */ -#define MAX_UNAME 8 /* eight characters long. */ -#define CRYPT_LEN 14 /* crypt() returns a 13 char string */ -#define LOGIN_LEN MAX_UNAME + 1 /* Leave room for a null */ -#define UID_LEN 7 /* Allow room for a 16 bit number */ +#define REG_SVR_ERROR_MESSAGES "/moira/reg_svr/errors" +enum { NO_MESSAGE, INTERNAL_ERROR, PROTOCOL_ERROR, DATABASE_CLOSED, + ENCRYPT_KEY, NOT_FOUND_IN_DATABASE, ALREADY_REGISTERED, + ACCOUNT_DELETED, NOT_ELIGIBLE, FOUND, FORCED_USERNAME, + BAD_SIX_WORDS, BAD_USERNAME, USERNAME_UNAVAILABLE, + RESERVED_USERNAME_UNAVAILABLE, USERNAME_OK, PASSWORD_SHORT, + PASSWORD_SIMPLE, PASSWORD_SAMPLE, KADM_ERROR, DONE, + NUM_REG_ERRORS }; -extern char *whoami; /* Name of program - used by libraries */ +#define TIMEOUT 300 /* 5 minutes */ -/* This structure holds information from the Moira database that will be - worth holding on to. An instance of it appears in the formatted - packet structure. */ -struct db_data -{ - char mit_id[CRYPT_LEN]; /* Encrypted MIT ID */ - int reg_status; /* Registration status */ - char uid[UID_LEN]; /* Reserved uid */ - char login[LOGIN_LEN]; /* Login (username) */ -}; +#define REG_RSA_ENCRYPTED_KEY 0x43 +#define REG_ENCRYPTED 0x45 +#define REG_UNENCRYPTED 0x50 -/* This structure stores information sent over in the packet in a - more convenient format and also stores some information obtained - from the database that will be needed for each transaction. It - initialized from format_pkt() and find_user(). */ -struct msg -{ - U_32BIT version; /* User registration protocol version */ - U_32BIT request; /* Request */ - char *first; /* First name */ - char *last; /* Last name */ - char *encrypted; /* Encrypted information in packet */ - int encrypted_len; /* Length of encrypted information in packet */ - char *leftover; /* Leftover information sent in the packet */ - int leftover_len; /* Length of leftover information */ - struct db_data db; /* Information from the Moira database */ -}; +/* Prototypes from kerberos.c */ +long init_kerberos(void); +long check_kerberos(char *username); +long register_kerberos(char *username, char *password); -/* For logging successful database transactions */ -extern FILE *journal; +/* Prototypes from procotol.c */ +int read_rsa_key(void); +int read_errors(void); +void reply(reg_client *rc, int msg, char *state, char *clean, char *data, + ...); +void parse_packet(reg_client *rc, int type, long len, char *buf, int sleeping); -/* prototypes from requests.c */ -void req_initialize(void); -void get_request(struct msg *message); -void report(int status, char *message); -u_long cur_req_sender(void); +/* prototypes from reg_svr.pc */ +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(char *); + +/* Prototypes from words.c */ +int read_hmac_key(void); +unsigned short *init_rand(void *); +void getwordlist(char *input, char *words[]); + + +/* XXX Stuff for testing */ +#define KERBEROS_TEST_REALM "ZONE.MIT.EDU" +#undef REG_SVR_PRINCIPAL +#define REG_SVR_PRINCIPAL "rcmd" +#undef REG_SVR_INSTANCE +#define REG_SVR_INSTANCE "this-too-shall-pass" diff --git a/reg_svr/reg_svr.pc b/reg_svr/reg_svr.pc new file mode 100644 index 00000000..5dcdba6e --- /dev/null +++ b/reg_svr/reg_svr.pc @@ -0,0 +1,847 @@ +/* $Id$ + * + * Server for user registration with Moira and Kerberos. + * + * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology + * For copying and distribution information, please see the file + * . + */ + +#include +#include +#include +#include +#include +#include "reg_svr.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +EXEC SQL INCLUDE sqlca; + +RCSID("$Header$"); + +char *whoami, *hostname, *shorthostname; + +char *find_usernames(char *first, char *middle, char *last); +void fixname(char *name); +int register_user(int uid, char *username); +void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar); +void sigshut(int); + +reg_client *cl = NULL; +enum { RS_RUNNING, RS_SLEEPING, RS_EXITING } state = RS_RUNNING; + +int main(int argc, char **argv) +{ + int listener, nfds, i, clientid = 0; + fd_set readfds, xreadfds; + reg_client *clients; + int nclients, clientssize; + long status; + char *db = "moira"; + struct utsname uts; + struct hostent *h; + struct sigaction sa; + struct stat st; + + whoami = strrchr(argv[0], '/'); + whoami = whoami ? whoami + 1 : argv[0]; + + set_com_err_hook(mr_com_err); + + /* Read keys */ + if (!read_rsa_key()) + { + com_err(whoami, errno, "reading RSA key"); + exit(1); + } + if (!read_hmac_key()) + { + com_err(whoami, errno, "reading HMAC key"); + exit(1); + } + + /* Read error messages */ + if (!read_errors()) + { + com_err(whoami, errno, "reading error messages"); + exit(1); + } + + /* Connect to database */ + EXEC SQL CONNECT :db IDENTIFIED BY :db; + if (sqlca.sqlcode) + { + char err_msg[256]; + int bufsize = 256, msglength = 0; + + sqlglm(err_msg, &bufsize, &msglength); + err_msg[msglength] = 0; + com_err(whoami, 0, "SQL error connecting to DBMS:\n%s", err_msg); + exit(1); + } + + /* Get my hostname */ + uname(&uts); + h = gethostbyname(uts.nodename); + if (!h) + { + com_err(whoami, 0, "Couldn't resolve hostname %s", uts.nodename); + exit(1); + } + hostname = lowercase(xstrdup(h->h_name)); + shorthostname = xstrdup(hostname); + if (strchr(shorthostname, '.')) + *strchr(shorthostname, '.') = '\0'; + + /* Initialize kerberos */ + status = init_kerberos(); + if (status) + { + com_err(whoami, status, "initializing kerberos library"); + exit(1); + } + + /* Set up listening socket. */ + listener = mr_listen("moira_ureg"); + if (listener < 0) + { + com_err(whoami, errno, "couldn't create listening socket"); + exit(1); + } + FD_ZERO(&xreadfds); + FD_SET(listener, &xreadfds); + nfds = listener + 1; + + /* Initialize client array. */ + nclients = 0; + clientssize = 5; + clients = malloc(clientssize * sizeof(reg_client)); + if (!clients) + { + com_err(whoami, errno, "creating client array"); + exit(1); + } + + /* Set up signal handlers */ + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sa.sa_handler = sigshut; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, NULL); + + com_err(whoami, 0, "started (pid %d)", getpid()); + com_err(whoami, 0, rcsid); + + /* Main loop */ + while (state != RS_EXITING) + { + if (state == RS_RUNNING && stat(MOIRA_MOTD_FILE, &st) == 0) + { + state == RS_SLEEPING; + com_err(whoami, 0, "found motd. reg_svr is sleeping"); + } + else if (state == RS_SLEEPING && stat(MOIRA_MOTD_FILE, &st) == -1) + { + state == RS_RUNNING; + com_err(whoami, 0, "motd gone. reg_svr is running"); + } + + memcpy(&readfds, &xreadfds, sizeof(readfds)); + if (select(nfds, &readfds, NULL, NULL, NULL) == -1) + { + if (errno != EINTR) + com_err(whoami, errno, "in select"); + continue; + } + + if (FD_ISSET(listener, &readfds)) + { + int newconn, addrlen = sizeof(struct sockaddr_in); + struct sockaddr_in addr; + + newconn = accept(listener, (struct sockaddr *)&addr, &addrlen); + if (newconn < 0) + com_err(whoami, errno, "accepting new connection"); + else + { + nclients++; + if (nclients > clientssize) + { + clientssize = 2 * clientssize; + clients = xrealloc(clients, clientssize * + sizeof(reg_client)); + } + + cl = &clients[nclients - 1]; + memset(cl, 0, sizeof(reg_client)); + cl->fd = newconn; + cl->lastmod = time(NULL); + cl->clientid = ++clientid; + cl->random = init_rand(cl); + FD_SET(newconn, &xreadfds); + if (newconn >= nfds) + nfds = newconn + 1; + + com_err(whoami, 0, + "New connection from %s port %d (now %d client%s)", + inet_ntoa(addr.sin_addr), (int)ntohs(addr.sin_port), + nclients, nclients != 1 ? "s" : ""); + } + } + + for (i = 0; i < nclients; i++) + { + cl = &clients[i]; + if (FD_ISSET(cl->fd, &readfds)) + { + cl->lastmod = time(NULL); + if (!cl->buf) + { + /* We're just starting */ + cl->buf = malloc(3); + if (!cl->buf) + { + com_err(whoami, errno, "allocating read buffer"); + reply(cl, INTERNAL_ERROR, "INIT", "c", NULL, + "Out of memory"); + goto reap; + } + cl->nread = 0; + } + + if (cl->nread < 3) + { + /* We haven't read the length byte yet... */ + cl->nread += read(cl->fd, cl->buf + cl->nread, + 3 - cl->nread); + if (cl->nread == 3) + { + cl->nmax = cl->buf[1] * 256 + cl->buf[2] + 3; + cl->buf = realloc(cl->buf, cl->nmax + 3); + if (!cl->buf) + { + com_err(whoami, errno, "reallocating read buffer"); + reply(cl, INTERNAL_ERROR, "INIT", "c", NULL, + "Out of memory"); + goto reap; + } + } + else if (cl->nread == 0) + { + /* client has closed connection. setting + lastmod will cause it to be reaped */ + cl->lastmod = 0; + } + } + else + { + /* We know how long the packet is supposed to be */ + cl->nread += read(cl->fd, cl->buf + cl->nread, + cl->nmax - cl->nread); + if (cl->nread == cl->nmax) + { + parse_packet(cl, cl->buf[0], cl->nread - 3, cl->buf + 3, + state == RS_SLEEPING); + free(cl->buf); + cl->buf = NULL; + } + } + } + + reap: + if (cl->lastmod < time(NULL) - TIMEOUT) + { + com_err(whoami, 0, "Closed connection. (now %d client%s)", + nclients - 1, nclients != 2 ? "s" : ""); + shutdown(cl->fd, 2); + close(cl->fd); + FD_CLR(cl->fd, &xreadfds); + free(cl->buf); + free(cl->id); + free(cl->username); + free(cl->suggestions); + free(cl->random); + clients[i] = clients[--nclients]; + i--; + } + } + cl = NULL; + } + com_err(whoami, 0, "Exiting."); +} + +void RIFO(reg_client *rc, int argc, char **argv) +{ + EXEC SQL BEGIN DECLARE SECTION; + char *ufirst, *umiddle, *ulast, *id; + char login[USERS_LOGIN_SIZE], first[USERS_FIRST_SIZE]; + char middle[USERS_MIDDLE_SIZE], last[USERS_LAST_SIZE]; + char fullname[USERS_FIRST_SIZE + USERS_MIDDLE_SIZE + USERS_LAST_SIZE]; + char class[USERS_TYPE_SIZE]; + int uid, status, secure, sqlstatus; + EXEC SQL END DECLARE SECTION; + + if (rc->uid || argc != 4) + { + reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL); + return; + } + + ufirst = argv[0]; + umiddle = argv[1]; + ulast = argv[2]; + id = argv[3]; + + /* "ORDER BY status" so that if there's both a matching state 0 entry + and a matching state 3 entry, we'll get the former. */ + EXEC SQL DECLARE csr_id CURSOR FOR + SELECT login, unix_uid, status, secure, first, middle, last, type + FROM users WHERE clearid = :id ORDER BY status; + EXEC SQL OPEN csr_id; + while (1) + { + EXEC SQL FETCH csr_id INTO :login, :uid, :status, + :secure, :first, :middle, :last, :class; + if (sqlca.sqlcode) + break; + strtrim(login); + strtrim(first); + strtrim(middle); + strtrim(last); + strtrim(class); + + /* Check names, allowing for the possibility that Moira and the + user might have them split up differently. eg, Mary/Ann/Singleton + vs. Mary Ann/Singleton. */ + if (strcasecmp(last, ulast) && strncasecmp(last, ulast, strlen(last)) && + strncasecmp(last, ulast, strlen(ulast))) + continue; + if (strlen(last) > 3 && strlen(ulast) < 3) + continue; + if (strcasecmp(first, ufirst) && + strncasecmp(first, ufirst, strlen(first)) && + strncasecmp(first, ufirst, strlen(ufirst))) + continue; + if (strlen(first) > 3 && strlen(ufirst) < 3) + continue; + /* Ignore the middle name since Moira doesn't have those reliably */ + break; + } + sqlstatus = sqlca.sqlcode; + EXEC SQL CLOSE csr_id; + + if (sqlstatus) + { + reply(rc, NOT_FOUND_IN_DATABASE, "GETN", "d", NULL); + return; + } + + switch (status) + { + case US_REGISTERED: + case US_ENROLLED: + case US_ENROLL_NOT_ALLOWED: + reply(rc, ALREADY_REGISTERED, "INIT", "c", NULL, login); + return; + + case US_DELETED: + reply(rc, ACCOUNT_DELETED, "INIT", "c", NULL, login); + return; + + case US_NOT_ALLOWED: + reply(rc, NOT_ELIGIBLE, "INIT", "c", NULL); + return; + + default: + break; + } + + rc->uid = uid; + sprintf(fullname, "%s %s%s%s", first, middle, *middle ? " " : "", last); + if (!strcmp(class, "MITS")) + strcpy(class, "STAFF"); + if (secure == 1) + { + rc->id = strdup(id); + if (!rc->id) + { + com_err(whoami, errno, "in RIFO"); + reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory"); + return; + } + } + if (*login != '#') + { + rc->reserved_username = 1; + rc->username = strdup(login); + if (!rc->username) + { + com_err(whoami, errno, "in RIFO"); + reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory"); + return; + } + } + else + { + rc->suggestions = find_usernames(first, middle, last); + if (!rc->suggestions) + { + com_err(whoami, errno, "in RIFO"); + reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory"); + return; + } + } + + if (rc->id) + reply(rc, FOUND, "GETW", "c", NULL, fullname, class); + else if (!rc->username) + reply(rc, FOUND, "GETL", "c", rc->suggestions, fullname, class); + else + reply(rc, FORCED_USERNAME, "GETP", "c", NULL, fullname, class, + rc->username); +} + +void SWRD(reg_client *rc, int argc, char **argv) +{ + char *words[6]; + int i; + + if (!rc->id || argc != 6) + { + reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL); + return; + } + + getwordlist(rc->id, words); + for (i = 0; i < 6; i++) + { + if (strcasecmp(strtrim(argv[i]), words[i])) + break; + } + if (i != 6) + { + reply(rc, BAD_SIX_WORDS, "GETW", "d", NULL); + return; + } + + free(rc->id); + rc->id = NULL; + if (!rc->username) + reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions); + else + reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username); +} + +void LOGN(reg_client *rc, int argc, char **argv) +{ + int i; + char *login; + long status; + + if (!rc->uid || rc->id || rc->username || argc != 1) + { + reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL); + return; + } + + login = argv[0]; + + /* make sure someone's not trying to overrun reply */ + if (strlen(login) > 100) + { + com_err(whoami, 0, "Buffer overrun attempted? Closing connection"); + rc->lastmod = 0; + return; + } + + if ((strlen(login) < 3) || (strlen(login) > USERS_LOGIN_SIZE - 1) || + (login[0] == '_') || isdigit(login[0])) + { + reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login, + 3, USERS_LOGIN_SIZE - 1); + return; + } + + for (i = 0; i < strlen(login); i++) + { + if (!islower(login[i]) && !isdigit(login[i]) && (login[i] != '_')) + { + reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login, + 3, USERS_LOGIN_SIZE - 1); + return; + } + } + + status = check_kerberos(login); + if (status == MR_SUCCESS) + status = register_user(rc->uid, login); + + if (status == MR_IN_USE) + { + if (rc->reserved_username) + { + reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL, + rc->username); + return; + } + reply(rc, USERNAME_UNAVAILABLE, "GETL", "c", rc->suggestions); + return; + } + else if (status == MR_DOWN) + { + reply(rc, DATABASE_CLOSED, "INIT", "c", NULL); + return; + } + else if (status != MR_SUCCESS) + { + reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(status)); + return; + } + + rc->username = strdup(login); + if (!rc->username) + { + com_err(whoami, errno, "in LOGN"); + reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory"); + return; + } + reply(rc, USERNAME_OK, "GETP", "c", NULL, login); +} + +int ctypes[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^@ - ^O */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^P - ^_ */ + 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* SPACE - / */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, /* 0 - ? */ + 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* : - O */ + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, /* P - _ */ + 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* ` - o */ + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, /* p - ^? */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +void PSWD(reg_client *rc, int argc, char **argv) +{ + long status; + char *password = argv[0], *p; + EXEC SQL BEGIN DECLARE SECTION; + char *login = rc->username; + EXEC SQL END DECLARE SECTION; + + if (!rc->username || rc->id || argc != 1) + { + reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL); + return; + } + + /* password quality checking */ + if (strlen(password) < 4) + { + reply(rc, PASSWORD_SHORT, "GETP", "c", NULL); + return; + } + + if (strlen(password) < 7) + { + for (p = password + 1; *p; p++) + { + if (ctypes[*p] != ctypes[*(p - 1)]) + break; + } + if (!*p) + { + reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL); + return; + } + } + + if (!strcasecmp(password, "GykoR-66") || + !strcasecmp(password, "slaRooBey") || + !strcasecmp(password, "krang-its") || + !strcasecmp(password, "2HotPeetzas") || + !strcasecmp(password, "ItzAGurl")) + { + reply(rc, PASSWORD_SAMPLE, "GETP", "c", NULL); + return; + } + + status = register_kerberos(rc->username, password); + if (status == MR_IN_USE) + { + reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL, + rc->username); + return; + } + else if (status) + { + com_err(whoami, status, "registering username with Kerberos"); + reply(rc, KADM_ERROR, "INIT", "c", NULL, error_message(status)); + return; + } + EXEC SQL UPDATE users SET status = 1 WHERE login = :login; + EXEC SQL COMMIT; + + reply(rc, DONE, "INIT", "c", NULL, rc->username); +} + +void QUIT(reg_client *rc, int argc, char **argv) +{ +} + +/* Register a user in Moira */ +int register_user(int uid, char *username) +{ + char uidbuf[10], *qargv[3], *motd = NULL; + long status; + + status = mr_connect(hostname); + if (status) + return status; + + status = mr_motd(&motd); + if (status || motd) + { + mr_disconnect(); + return MR_DOWN; + } + + status = krb_get_svc_in_tkt(REG_SVR_PRINCIPAL, REG_SVR_INSTANCE, + krb_realmofhost(hostname), MOIRA_SNAME, + shorthostname, 1, KEYFILE); + if (status) + status += ERROR_TABLE_BASE_krb; + else + status = mr_auth("reg_svr"); + if (status) + { + com_err(whoami, status, "authenticating to moira"); + mr_disconnect(); + return MR_INTERNAL; + } + + sprintf(uidbuf, "%d", uid); + qargv[0] = uidbuf; + qargv[1] = username; + qargv[2] = "0"; + status = mr_query("register_user", 3, qargv, NULL, NULL); + mr_disconnect(); + return status; +} + + +/* Find some typical available usernames */ + +char *uname_patterns[] = { + "FL", /* johndoe */ + "fmllllll", /* jmdoe... (last name truncated) */ + "flllllll", /* jdoe.... ("") */ + "llllllll", /* doe..... ("") */ + "fml", /* jmd */ + "Fl", /* johnd */ + "Lf", /* doej */ + "Lfm", /* doejm */ + "F", /* john */ +}; +int num_patterns = sizeof(uname_patterns) / sizeof(char *); + +char *find_usernames(char *first, char *middle, char *last) +{ + EXEC SQL BEGIN DECLARE SECTION; + char username[2 * USERS_LOGIN_SIZE]; + int count; + EXEC SQL END DECLARE SECTION; + int pat, len; + char *pp, *up, *fp, *mp, *lp, *unames = NULL; + + fixname(first); + fixname(middle); + fixname(last); + + for (pat = 0; pat < num_patterns; pat++) + { + up = username; + fp = first; + mp = middle; + lp = last; + for (pp = uname_patterns[pat]; *pp; pp++) + { + switch (*pp) + { + case 'f': + if (*fp) + *up++ = *fp++; + break; + + case 'F': + if (up - username + strlen(first) < USERS_LOGIN_SIZE) + up += sprintf(up, "%s", first); + else + goto nextpattern; + break; + + case 'm': + if (!*middle) + goto nextpattern; + if (*mp) + *up++ = *mp++; + break; + + case 'l': + if (*lp) + *up++ = *lp++; + break; + + case 'L': + if (up - username + strlen(last) < USERS_LOGIN_SIZE) + up += sprintf(up, "%s", last); + else + goto nextpattern; + break; + } + } + *up = '\0'; + + if (strlen(username) < 3 || strlen(username) > USERS_LOGIN_SIZE) + continue; + + EXEC SQL SELECT COUNT(login) INTO :count FROM users + WHERE login = :username; + if (sqlca.sqlcode) + return NULL; + if (count == 0) + { + EXEC SQL SELECT COUNT(name) INTO :count FROM list + WHERE name = :username; + if (sqlca.sqlcode) + return NULL; + } + if (count == 0) + { + EXEC SQL SELECT COUNT(label) INTO :count FROM filesys + WHERE label = :username; + if (sqlca.sqlcode) + return NULL; + } + + if (count == 0) + { + if (unames) + { + unames = realloc(unames, strlen(unames) + strlen(username) + 3); + if (!unames) + return NULL; + strcat(unames, ", "); + strcat(unames, username); + } + else + { + unames = strdup(username); + if (!unames) + return NULL; + } + } + + nextpattern: + ; + } + + return unames; +} + +void fixname(char *name) +{ + char *s, *d; + + for (s = d = name; *s; s++) + { + if (isalnum(*s)) + *d++ = tolower(*s); + } + *d = '\0'; +} + +void *xmalloc(size_t bytes) +{ + void *buf = malloc(bytes); + + if (buf) + return buf; + + com_err(whoami, errno, "in xmalloc"); + exit(1); +} + +void *xrealloc(void *ptr, size_t bytes) +{ + void *buf = realloc(ptr, bytes); + + if (buf) + return buf; + + com_err(whoami, errno, "in xrealloc"); + exit(1); +} + +char *xstrdup(char *str) +{ + char *buf = strdup(str); + + if (buf) + return buf; + + com_err(whoami, errno, "in xstrdup"); + exit(1); +} + +void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar) +{ + if (whoami) + { + fputs(whoami, stderr); + if (cl) + fprintf(stderr, "[#%d]", cl->clientid); + fputs(": ", stderr); + } + if (code) { + fputs(error_message(code), stderr); + fputs(" ", stderr); + } + if (fmt) + vfprintf(stderr, fmt, pvar); + putc('\n', stderr); +} + +void sigshut(int sig) +{ + state = RS_EXITING; +} diff --git a/reg_svr/requests.c b/reg_svr/requests.c deleted file mode 100644 index 56d26140..00000000 --- a/reg_svr/requests.c +++ /dev/null @@ -1,394 +0,0 @@ -/* $Id$ - * - * Server for user registration with Moira and Kerberos. - * - * This file handles the processing of requests for the register - * server. - * - * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology - * For copying and distribution information, please see the file - * . - */ - -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "reg_svr.h" - -RCSID("$Header$"); - -#define NUM_REQUESTS_SAVED 100 /* Number of transactions to save */ -#define CUR_REQ (requests[cur_request_index]) /* The current request */ -#define NEXT_INDEX(x) (x == NUM_REQUESTS_SAVED - 1) ? 0 : (x + 1) -#define PREV_INDEX(x) (x == 0) ? (NUM_REQUESTS_SAVED - 1) : (x - 1) - -static struct servent *sp; /* Service info from /etc/services */ -static int s; /* Socket descriptor */ -static struct sockaddr_in sin; /* Internet style socket address */ -static int addrlen; /* Size of socket address (sin) */ - -/* In order to elegantly handle multiple retransmissions, an instance - of this structure will be retained for the last NUM_REQUESTS_SAVED - transactions with the client. */ -struct request_save { - char out_pkt[BUFSIZ]; /* Buffer for outgoing packet */ - int out_pktlen; /* Length of outgoing packet */ - U_32BIT seqno; /* Sequence number for packet transmission */ - u_long ip_address; /* Internet address of client host */ - u_short cl_port; /* Port number client used */ -}; - -static struct request_save requests[NUM_REQUESTS_SAVED]; /* Saved packets */ -static int cur_request_index = 0; /* Index to the current request */ - -void clear_req(struct request_save *req); -void req_initialize(void); -int handle_retransmitted(void); -void respond(int status, char *text); -void get_request(struct msg *message); -void report(int status, char *message); -int format_pkt(char *packet, int *pktlenp, U_32BIT seqno, - int cl_status, char *message); -int ureg_validate_char(char *s); -int parse_pkt(char *packet, int pktlen, struct msg *message); -u_long cur_req_sender(void); - -void clear_req(struct request_save *req) -{ - req->seqno = 0; - req->ip_address = 0; - req->cl_port = 0; -} - -void req_initialize(void) -{ - int i; - - /* Get service information from /etc/services */ - if (!(sp = getservbyname("moira_ureg", "udp"))) - { - com_err(whoami, errno, " unknown service moira_ureg/udp"); - exit(1); - } - - /* Get an internet style datagram socket */ - if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) - { - com_err(whoami, errno, " socket"); - exit(1); - } - memset(&sin, 0, sizeof(sin)); - - sin.sin_family = AF_INET; - sin.sin_port = sp->s_port; - sin.sin_addr.s_addr = INADDR_ANY; - - /* Bind a name to the socket */ - if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) - { - com_err(whoami, errno, " bind"); - exit(1); - } - - for (i = 0; i < NUM_REQUESTS_SAVED; i++) - clear_req(&(requests[i])); -} - -int handle_retransmitted(void) -{ - int i; /* A counter */ - int status = FALSE; /* Return status */ - - for (i = PREV_INDEX(cur_request_index); i != cur_request_index; - i = PREV_INDEX(i)) - { - if ((requests[i].seqno == CUR_REQ.seqno) && - (requests[i].ip_address == sin.sin_addr.s_addr) && - (requests[i].cl_port == sin.sin_port)) - /* This is a retransmitted packet */ - { - status = TRUE; - sendto(s, requests[i].out_pkt, requests[i].out_pktlen, - 0, (struct sockaddr *)&sin, addrlen); - break; - } - } - - return status; -} - -/* This routine takes care of sending packets back to the client and - caching the necessary information for retransmission detection. - It is the only place in which cur_request_index should be - changed. */ -void respond(int status, char *text) -{ - CUR_REQ.out_pktlen = sizeof(CUR_REQ.out_pkt); - - if (format_pkt(CUR_REQ.out_pkt, &(CUR_REQ.out_pktlen), - CUR_REQ.seqno, status, text)) - com_err(whoami, 0, "Client error message was truncated."); - sendto(s, CUR_REQ.out_pkt, CUR_REQ.out_pktlen, 0, - (struct sockaddr *)&sin, addrlen); - - cur_request_index = NEXT_INDEX(cur_request_index); -} - -void get_request(struct msg *message) -{ - static char packet[BUFSIZ]; /* Buffer for incoming packet */ - int pktlen; /* Length of incoming packet */ - int status = FAILURE; /* Error status */ - - /* Sit around waiting for requests from the client. */ - for (;;) - { - com_err(whoami, 0, "*** Ready for next request ***"); - addrlen = sizeof(sin); - /* Receive a packet */ - if ((pktlen = recvfrom(s, packet, sizeof(packet), 0, - (struct sockaddr *)&sin, &addrlen)) < 0) - { - com_err(whoami, errno, " recvfrom"); - /* Don't worry if error is interrupted system call. */ - if (errno == EINTR) - continue; - exit(1); - } - - /* Store available information */ - CUR_REQ.seqno = 0; - CUR_REQ.ip_address = sin.sin_addr.s_addr; - CUR_REQ.cl_port = sin.sin_port; - - /* Parse a request packet and save sequence number */ - if ((status = parse_pkt(packet, pktlen, message)) != SUCCESS) - { - /* If error, format packet to send back to the client */ - respond(status, NULL); - } - else - { - /* Check for retransmitted packet. handle_retransmitted() - returns true if it handled a retransmitted packet. */ - if (!handle_retransmitted()) - break; - } - } -} - -void report(int status, char *message) -{ - respond(status, message); -} - -/* This routine prepares a packet to send back to the client. A - non-zero return status means that the client error message was - truncated. */ -int format_pkt(char *packet, int *pktlenp, U_32BIT seqno, - int cl_status, char *message) -{ - int len; /* Amount of message to send */ - int status = SUCCESS; /* Return status */ - - /* Convert byte order to network byte order */ - U_32BIT vers = htonl((U_32BIT)CUR_UREG_VERSION); - cl_status = htonl((U_32BIT)cl_status); - /* Put current user registration protocol version into the packet */ - memcpy(packet, &vers, sizeof(U_32BIT)); - /* Put sequence number into the packet */ - memcpy(packet + sizeof(U_32BIT), &seqno, sizeof(U_32BIT)); - /* Put error status into the packet */ - memcpy(packet + 2 * sizeof(U_32BIT), &cl_status, sizeof(U_32BIT)); - - /* Find out how much of the message to copy; truncate if too short. */ - /* How much room is there left? */ - len = *pktlenp - sizeof(U_32BIT) * 3; - if (!message) - message = ""; - if (len < strlen(message) + 1) /* Room for null terminator */ - { - status = FAILURE; /* Message was truncated */ - /* Truncate the message */ - message[len - 1] = '\0'; - } - - /* Copy the message into the packet */ - strcpy(packet + 3 * sizeof(U_32BIT), message); - *pktlenp = 3 * sizeof(U_32BIT) + strlen(message); - - return status; -} - -/* The ureg_validate_char variable and routine were taken verbatim - out of server/qsupport.qc where they are called - validate_chars. At some point, it may be desirable - to put this functionality in one place. */ - -/* ureg_validate_char: verify that there are no illegal characters in - * the string. Legal characters are printing chars other than - * ", *, ?, \, [ and ]. - */ -static int illegalchars[] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */ - 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ - O */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -}; - -int ureg_validate_char(char *s) -{ - while (*s) - { - if (illegalchars[(int)*s++]) - return FAILURE; - } - return SUCCESS; -} - -/* This routine checks a packet and puts the information in it in - a structure if it is valid. It also saves the sequence number - in the list of saved requests. */ -int parse_pkt(char *packet, int pktlen, struct msg *message) -{ - int status = SUCCESS; /* Error status */ - - com_err(whoami, 0, "Packet received"); - - if (pktlen < sizeof(U_32BIT)) - status = UREG_BROKEN_PACKET; - if (status == SUCCESS) - { - /* Extract the user registration protocol version from the packet */ - memcpy(&message->version, packet, sizeof(long)); - /* Convert byte order from network to host */ - message->version = ntohl(message->version); - /* Verify version */ - if (message->version != CUR_UREG_VERSION) - status = UREG_WRONG_VERSION; - } - - if (status == SUCCESS) - { - packet += sizeof(U_32BIT); - pktlen -= sizeof(U_32BIT); - - if (pktlen < sizeof(U_32BIT)) - status = UREG_BROKEN_PACKET; - } - - if (status == SUCCESS) - { - /* Extract the sequence number from the packet */ - memcpy(&CUR_REQ.seqno, packet, sizeof(long)); - - packet += sizeof(U_32BIT); - pktlen -= sizeof(U_32BIT); - - if (pktlen < sizeof(U_32BIT)) - status = UREG_BROKEN_PACKET; - } - - if (status == SUCCESS) - { - /* Extract the request from the packet */ - memcpy(&message->request, packet, sizeof(U_32BIT)); - message->request = ntohl(message->request); - packet += sizeof(U_32BIT); - pktlen -= sizeof(U_32BIT); - - /* Make sure that the packet contains only valid characters up - to the next null */ - if (ureg_validate_char(packet) != SUCCESS) - { - com_err(whoami, 0, "Packet contains invalid characters."); - status = UREG_USER_NOT_FOUND; - } - else - { - /* Extract first name from the packet */ - message->first = packet; - - /* Scan forward until null appears in the packet or there - is no more packet! */ - for (; *packet && pktlen > 0; --pktlen, ++packet) - continue; - if (pktlen <= 0) - status = UREG_BROKEN_PACKET; - } - } - - if (status == SUCCESS) - { - /* Skip over the null */ - packet++, pktlen--; - - if (ureg_validate_char(packet) != SUCCESS) - { - com_err(whoami, 0, "Packet contains invalid characters."); - status = UREG_USER_NOT_FOUND; - } - else - { - /* Extract last name from the packet */ - message->last = packet; - - for (; *packet && pktlen > 0; --pktlen, ++packet) - continue; - if (pktlen <= 0) - status = UREG_BROKEN_PACKET; - } - } - - if (status == SUCCESS) - { - packet++, pktlen--; - - if (pktlen <= 0) - status = UREG_BROKEN_PACKET; - } - - /* Extract encrypted information from packet */ - message->encrypted = packet; - message->encrypted_len = pktlen; - - if (status == SUCCESS) - { - com_err(whoami, status, "Request %d for %s %s", message->request, - message->first, message->last); - } - else - com_err(whoami, status, " - parse packet failed."); - - return status; -} - - -u_long cur_req_sender(void) -{ - return CUR_REQ.ip_address; -} diff --git a/reg_svr/words.c b/reg_svr/words.c new file mode 100644 index 00000000..29564249 --- /dev/null +++ b/reg_svr/words.c @@ -0,0 +1,414 @@ +/* $Id$ + * + * 6 Magic Words generation + * + * Copyright (C) 1998 by the Massachusetts Institute of Technology + * For copying and distribution information, please see the file + * . + * + */ + +#include +#include +#include "reg_svr.h" + +#include +#include + +#include +#include +#include +#include + +/* RSARef includes */ +#include "global.h" +#include "rsaref.h" + +RCSID("$Header$"); + +/* One-Time Password dictionary, as specified by RFC 1938 */ + +char *dict[] = +{ "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD", +"AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY", +"AN", "ANA", "AND", "ANN", "ANT", "ANY", "APE", "APS", +"APT", "ARC", "ARE", "ARK", "ARM", "ART", "AS", "ASH", +"ASK", "AT", "ATE", "AUG", "AUK", "AVE", "AWE", "AWK", +"AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM", +"BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", +"BEN", "BET", "BEY", "BIB", "BID", "BIG", "BIN", "BIT", +"BOB", "BOG", "BON", "BOO", "BOP", "BOW", "BOY", "BUB", +"BUD", "BUG", "BUM", "BUN", "BUS", "BUT", "BUY", "BY", +"BYE", "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT", +"CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", +"COW", "COY", "CRY", "CUB", "CUE", "CUP", "CUR", "CUT", +"DAB", "DAD", "DAM", "DAN", "DAR", "DAY", "DEE", "DEL", +"DEN", "DES", "DEW", "DID", "DIE", "DIG", "DIN", "DIP", +"DO", "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB", +"DUD", "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL", +"EGG", "EGO", "ELI", "ELK", "ELM", "ELY", "EM", "END", +"EST", "ETC", "EVA", "EVE", "EWE", "EYE", "FAD", "FAN", +"FAR", "FAT", "FAY", "FED", "FEE", "FEW", "FIB", "FIG", +"FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR", +"FRY", "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", +"GAM", "GAP", "GAS", "GAY", "GEE", "GEL", "GEM", "GET", +"GIG", "GIL", "GIN", "GO", "GOT", "GUM", "GUN", "GUS", +"GUT", "GUY", "GYM", "GYP", "HA", "HAD", "HAL", "HAM", +"HAN", "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM", +"HEN", "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP", +"HIS", "HIT", "HO", "HOB", "HOC", "HOE", "HOG", "HOP", +"HOT", "HOW", "HUB", "HUE", "HUG", "HUH", "HUM", "HUT", +"I", "ICY", "IDA", "IF", "IKE", "ILL", "INK", "INN", +"IO", "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT", +"ITS", "IVY", "JAB", "JAG", "JAM", "JAN", "JAR", "JAW", +"JAY", "JET", "JIG", "JIM", "JO", "JOB", "JOE", "JOG", +"JOT", "JOY", "JUG", "JUT", "KAY", "KEG", "KEN", "KEY", +"KID", "KIM", "KIN", "KIT", "LA", "LAB", "LAC", "LAD", +"LAG", "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE", +"LEG", "LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN", +"LIP", "LIT", "LO", "LOB", "LOG", "LOP", "LOS", "LOT", +"LOU", "LOW", "LOY", "LUG", "LYE", "MA", "MAC", "MAD", +"MAE", "MAN", "MAO", "MAP", "MAT", "MAW", "MAY", "ME", +"MEG", "MEL", "MEN", "MET", "MEW", "MID", "MIN", "MIT", +"MOB", "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", +"MUD", "MUG", "MUM", "MY", "NAB", "NAG", "NAN", "NAP", +"NAT", "NAY", "NE", "NED", "NEE", "NET", "NEW", "NIB", +"NIL", "NIP", "NIT", "NO", "NOB", "NOD", "NON", "NOR", +"NOT", "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF", +"OAK", "OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT", +"OH", "OIL", "OK", "OLD", "ON", "ONE", "OR", "ORB", +"ORE", "ORR", "OS", "OTT", "OUR", "OUT", "OVA", "OW", +"OWE", "OWL", "OWN", "OX", "PA", "PAD", "PAL", "PAM", +"PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG", +"PEN", "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", +"PIN", "PIT", "PLY", "PO", "POD", "POE", "POP", "POT", +"POW", "PRO", "PRY", "PUB", "PUG", "PUN", "PUP", "PUT", +"QUO", "RAG", "RAM", "RAN", "RAP", "RAT", "RAW", "RAY", +"REB", "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM", +"RIO", "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", +"ROY", "RUB", "RUE", "RUG", "RUM", "RUN", "RYE", "SAC", +"SAD", "SAG", "SAL", "SAM", "SAN", "SAP", "SAT", "SAW", +"SAY", "SEA", "SEC", "SEE", "SEN", "SET", "SEW", "SHE", +"SHY", "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY", +"SLY", "SO", "SOB", "SOD", "SON", "SOP", "SOW", "SOY", +"SPA", "SPY", "SUB", "SUD", "SUE", "SUM", "SUN", "SUP", +"TAB", "TAD", "TAG", "TAN", "TAP", "TAR", "TEA", "TED", +"TEE", "TEN", "THE", "THY", "TIC", "TIE", "TIM", "TIN", +"TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP", +"TOW", "TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO", +"UN", "UP", "US", "USE", "VAN", "VAT", "VET", "VIE", +"WAD", "WAG", "WAR", "WAS", "WAY", "WE", "WEB", "WED", +"WEE", "WET", "WHO", "WHY", "WIN", "WIT", "WOK", "WON", +"WOO", "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE", +"YEA", "YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE", +"ABUT", "ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM", +"ADDS", "ADEN", "AFAR", "AFRO", "AGEE", "AHEM", "AHOY", "AIDA", +"AIDE", "AIDS", "AIRY", "AJAR", "AKIN", "ALAN", "ALEC", "ALGA", +"ALIA", "ALLY", "ALMA", "ALOE", "ALSO", "ALTO", "ALUM", "ALVA", +"AMEN", "AMES", "AMID", "AMMO", "AMOK", "AMOS", "AMRA", "ANDY", +"ANEW", "ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH", +"AREA", "ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS", +"ATOM", "AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS", "AVON", +"AVOW", "AWAY", "AWRY", "BABE", "BABY", "BACH", "BACK", "BADE", +"BAIL", "BAIT", "BAKE", "BALD", "BALE", "BALI", "BALK", "BALL", +"BALM", "BAND", "BANE", "BANG", "BANK", "BARB", "BARD", "BARE", +"BARK", "BARN", "BARR", "BASE", "BASH", "BASK", "BASS", "BATE", +"BATH", "BAWD", "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR", +"BEAT", "BEAU", "BECK", "BEEF", "BEEN", "BEER", "BEET", "BELA", +"BELL", "BELT", "BEND", "BENT", "BERG", "BERN", "BERT", "BESS", +"BEST", "BETA", "BETH", "BHOY", "BIAS", "BIDE", "BIEN", "BILE", +"BILK", "BILL", "BIND", "BING", "BIRD", "BITE", "BITS", "BLAB", +"BLAT", "BLED", "BLEW", "BLOB", "BLOC", "BLOT", "BLOW", "BLUE", +"BLUM", "BLUR", "BOAR", "BOAT", "BOCA", "BOCK", "BODE", "BODY", +"BOGY", "BOHR", "BOIL", "BOLD", "BOLO", "BOLT", "BOMB", "BONA", +"BOND", "BONE", "BONG", "BONN", "BONY", "BOOK", "BOOM", "BOON", +"BOOT", "BORE", "BORG", "BORN", "BOSE", "BOSS", "BOTH", "BOUT", +"BOWL", "BOYD", "BRAD", "BRAE", "BRAG", "BRAN", "BRAY", "BRED", +"BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD", "BUFF", "BULB", +"BULK", "BULL", "BUNK", "BUNT", "BUOY", "BURG", "BURL", "BURN", +"BURR", "BURT", "BURY", "BUSH", "BUSS", "BUST", "BUSY", "BYTE", +"CADY", "CAFE", "CAGE", "CAIN", "CAKE", "CALF", "CALL", "CALM", +"CAME", "CANE", "CANT", "CARD", "CARE", "CARL", "CARR", "CART", +"CASE", "CASH", "CASK", "CAST", "CAVE", "CEIL", "CELL", "CENT", +"CERN", "CHAD", "CHAR", "CHAT", "CHAW", "CHEF", "CHEN", "CHEW", +"CHIC", "CHIN", "CHOU", "CHOW", "CHUB", "CHUG", "CHUM", "CITE", +"CITY", "CLAD", "CLAM", "CLAN", "CLAW", "CLAY", "CLOD", "CLOG", +"CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA", "COCK", "COCO", +"CODA", "CODE", "CODY", "COED", "COIL", "COIN", "COKE", "COLA", +"COLD", "COLT", "COMA", "COMB", "COME", "COOK", "COOL", "COON", +"COOT", "CORD", "CORE", "CORK", "CORN", "COST", "COVE", "COWL", +"CRAB", "CRAG", "CRAM", "CRAY", "CREW", "CRIB", "CROW", "CRUD", +"CUBA", "CUBE", "CUFF", "CULL", "CULT", "CUNY", "CURB", "CURD", +"CURE", "CURL", "CURT", "CUTS", "DADE", "DALE", "DAME", "DANA", +"DANE", "DANG", "DANK", "DARE", "DARK", "DARN", "DART", "DASH", +"DATA", "DATE", "DAVE", "DAVY", "DAWN", "DAYS", "DEAD", "DEAF", +"DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED", "DEEM", "DEER", +"DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK", "DIAL", "DICE", +"DIED", "DIET", "DIME", "DINE", "DING", "DINT", "DIRE", "DIRT", +"DISC", "DISH", "DISK", "DIVE", "DOCK", "DOES", "DOLE", "DOLL", +"DOLT", "DOME", "DONE", "DOOM", "DOOR", "DORA", "DOSE", "DOTE", +"DOUG", "DOUR", "DOVE", "DOWN", "DRAB", "DRAG", "DRAM", "DRAW", +"DREW", "DRUB", "DRUG", "DRUM", "DUAL", "DUCK", "DUCT", "DUEL", +"DUET", "DUKE", "DULL", "DUMB", "DUNE", "DUNK", "DUSK", "DUST", +"DUTY", "EACH", "EARL", "EARN", "EASE", "EAST", "EASY", "EBEN", +"ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT", "EDNA", "EGAN", +"ELAN", "ELBA", "ELLA", "ELSE", "EMIL", "EMIT", "EMMA", "ENDS", +"ERIC", "EROS", "EVEN", "EVER", "EVIL", "EYED", "FACE", "FACT", +"FADE", "FAIL", "FAIN", "FAIR", "FAKE", "FALL", "FAME", "FANG", +"FARM", "FAST", "FATE", "FAWN", "FEAR", "FEAT", "FEED", "FEEL", +"FEET", "FELL", "FELT", "FEND", "FERN", "FEST", "FEUD", "FIEF", +"FIGS", "FILE", "FILL", "FILM", "FIND", "FINE", "FINK", "FIRE", +"FIRM", "FISH", "FISK", "FIST", "FITS", "FIVE", "FLAG", "FLAK", +"FLAM", "FLAT", "FLAW", "FLEA", "FLED", "FLEW", "FLIT", "FLOC", +"FLOG", "FLOW", "FLUB", "FLUE", "FOAL", "FOAM", "FOGY", "FOIL", +"FOLD", "FOLK", "FOND", "FONT", "FOOD", "FOOL", "FOOT", "FORD", +"FORE", "FORK", "FORM", "FORT", "FOSS", "FOUL", "FOUR", "FOWL", +"FRAU", "FRAY", "FRED", "FREE", "FRET", "FREY", "FROG", "FROM", +"FUEL", "FULL", "FUME", "FUND", "FUNK", "FURY", "FUSE", "FUSS", +"GAFF", "GAGE", "GAIL", "GAIN", "GAIT", "GALA", "GALE", "GALL", +"GALT", "GAME", "GANG", "GARB", "GARY", "GASH", "GATE", "GAUL", +"GAUR", "GAVE", "GAWK", "GEAR", "GELD", "GENE", "GENT", "GERM", +"GETS", "GIBE", "GIFT", "GILD", "GILL", "GILT", "GINA", "GIRD", +"GIRL", "GIST", "GIVE", "GLAD", "GLEE", "GLEN", "GLIB", "GLOB", +"GLOM", "GLOW", "GLUE", "GLUM", "GLUT", "GOAD", "GOAL", "GOAT", +"GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG", "GOOD", "GOOF", +"GORE", "GORY", "GOSH", "GOUT", "GOWN", "GRAB", "GRAD", "GRAY", +"GREG", "GREW", "GREY", "GRID", "GRIM", "GRIN", "GRIT", "GROW", +"GRUB", "GULF", "GULL", "GUNK", "GURU", "GUSH", "GUST", "GWEN", +"GWYN", "HAAG", "HAAS", "HACK", "HAIL", "HAIR", "HALE", "HALF", +"HALL", "HALO", "HALT", "HAND", "HANG", "HANK", "HANS", "HARD", +"HARK", "HARM", "HART", "HASH", "HAST", "HATE", "HATH", "HAUL", +"HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR", "HEAT", "HEBE", +"HECK", "HEED", "HEEL", "HEFT", "HELD", "HELL", "HELM", "HERB", +"HERD", "HERE", "HERO", "HERS", "HESS", "HEWN", "HICK", "HIDE", +"HIGH", "HIKE", "HILL", "HILT", "HIND", "HINT", "HIRE", "HISS", +"HIVE", "HOBO", "HOCK", "HOFF", "HOLD", "HOLE", "HOLM", "HOLT", +"HOME", "HONE", "HONK", "HOOD", "HOOF", "HOOK", "HOOT", "HORN", +"HOSE", "HOST", "HOUR", "HOVE", "HOWE", "HOWL", "HOYT", "HUCK", +"HUED", "HUFF", "HUGE", "HUGH", "HUGO", "HULK", "HULL", "HUNK", +"HUNT", "HURD", "HURL", "HURT", "HUSH", "HYDE", "HYMN", "IBIS", +"ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH", "INTO", "IONS", +"IOTA", "IOWA", "IRIS", "IRMA", "IRON", "ISLE", "ITCH", "ITEM", +"IVAN", "JACK", "JADE", "JAIL", "JAKE", "JANE", "JAVA", "JEAN", +"JEFF", "JERK", "JESS", "JEST", "JIBE", "JILL", "JILT", "JIVE", +"JOAN", "JOBS", "JOCK", "JOEL", "JOEY", "JOHN", "JOIN", "JOKE", +"JOLT", "JOVE", "JUDD", "JUDE", "JUDO", "JUDY", "JUJU", "JUKE", +"JULY", "JUNE", "JUNK", "JUNO", "JURY", "JUST", "JUTE", "KAHN", +"KALE", "KANE", "KANT", "KARL", "KATE", "KEEL", "KEEN", "KENO", +"KENT", "KERN", "KERR", "KEYS", "KICK", "KILL", "KIND", "KING", +"KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW", "KNIT", "KNOB", +"KNOT", "KNOW", "KOCH", "KONG", "KUDO", "KURD", "KURT", "KYLE", +"LACE", "LACK", "LACY", "LADY", "LAID", "LAIN", "LAIR", "LAKE", +"LAMB", "LAME", "LAND", "LANE", "LANG", "LARD", "LARK", "LASS", +"LAST", "LATE", "LAUD", "LAVA", "LAWN", "LAWS", "LAYS", "LEAD", +"LEAF", "LEAK", "LEAN", "LEAR", "LEEK", "LEER", "LEFT", "LEND", +"LENS", "LENT", "LEON", "LESK", "LESS", "LEST", "LETS", "LIAR", +"LICE", "LICK", "LIED", "LIEN", "LIES", "LIEU", "LIFE", "LIFT", +"LIKE", "LILA", "LILT", "LILY", "LIMA", "LIMB", "LIME", "LIND", +"LINE", "LINK", "LINT", "LION", "LISA", "LIST", "LIVE", "LOAD", +"LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE", "LOIS", "LOLA", +"LONE", "LONG", "LOOK", "LOON", "LOOT", "LORD", "LORE", "LOSE", +"LOSS", "LOST", "LOUD", "LOVE", "LOWE", "LUCK", "LUCY", "LUGE", +"LUKE", "LULU", "LUND", "LUNG", "LURA", "LURE", "LURK", "LUSH", +"LUST", "LYLE", "LYNN", "LYON", "LYRA", "MACE", "MADE", "MAGI", +"MAID", "MAIL", "MAIN", "MAKE", "MALE", "MALI", "MALL", "MALT", +"MANA", "MANN", "MANY", "MARC", "MARE", "MARK", "MARS", "MART", +"MARY", "MASH", "MASK", "MASS", "MAST", "MATE", "MATH", "MAUL", +"MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK", "MEET", "MELD", +"MELT", "MEMO", "MEND", "MENU", "MERT", "MESH", "MESS", "MICE", +"MIKE", "MILD", "MILE", "MILK", "MILL", "MILT", "MIMI", "MIND", +"MINE", "MINI", "MINK", "MINT", "MIRE", "MISS", "MIST", "MITE", +"MITT", "MOAN", "MOAT", "MOCK", "MODE", "MOLD", "MOLE", "MOLL", +"MOLT", "MONA", "MONK", "MONT", "MOOD", "MOON", "MOOR", "MOOT", +"MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH", "MOVE", "MUCH", +"MUCK", "MUDD", "MUFF", "MULE", "MULL", "MURK", "MUSH", "MUST", +"MUTE", "MUTT", "MYRA", "MYTH", "NAGY", "NAIL", "NAIR", "NAME", +"NARY", "NASH", "NAVE", "NAVY", "NEAL", "NEAR", "NEAT", "NECK", +"NEED", "NEIL", "NELL", "NEON", "NERO", "NESS", "NEST", "NEWS", +"NEWT", "NIBS", "NICE", "NICK", "NILE", "NINA", "NINE", "NOAH", +"NODE", "NOEL", "NOLL", "NONE", "NOOK", "NOON", "NORM", "NOSE", +"NOTE", "NOUN", "NOVA", "NUDE", "NULL", "NUMB", "OATH", "OBEY", +"OBOE", "ODIN", "OHIO", "OILY", "OINT", "OKAY", "OLAF", "OLDY", +"OLGA", "OLIN", "OMAN", "OMEN", "OMIT", "ONCE", "ONES", "ONLY", +"ONTO", "ONUS", "ORAL", "ORGY", "OSLO", "OTIS", "OTTO", "OUCH", +"OUST", "OUTS", "OVAL", "OVEN", "OVER", "OWLY", "OWNS", "QUAD", +"QUIT", "QUOD", "RACE", "RACK", "RACY", "RAFT", "RAGE", "RAID", +"RAIL", "RAIN", "RAKE", "RANK", "RANT", "RARE", "RASH", "RATE", +"RAVE", "RAYS", "READ", "REAL", "REAM", "REAR", "RECK", "REED", +"REEF", "REEK", "REEL", "REID", "REIN", "RENA", "REND", "RENT", +"REST", "RICE", "RICH", "RICK", "RIDE", "RIFT", "RILL", "RIME", +"RING", "RINK", "RISE", "RISK", "RITE", "ROAD", "ROAM", "ROAR", +"ROBE", "ROCK", "RODE", "ROIL", "ROLL", "ROME", "ROOD", "ROOF", +"ROOK", "ROOM", "ROOT", "ROSA", "ROSE", "ROSS", "ROSY", "ROTH", +"ROUT", "ROVE", "ROWE", "ROWS", "RUBE", "RUBY", "RUDE", "RUDY", +"RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE", "RUSH", "RUSK", +"RUSS", "RUST", "RUTH", "SACK", "SAFE", "SAGE", "SAID", "SAIL", +"SALE", "SALK", "SALT", "SAME", "SAND", "SANE", "SANG", "SANK", +"SARA", "SAUL", "SAVE", "SAYS", "SCAN", "SCAR", "SCAT", "SCOT", +"SEAL", "SEAM", "SEAR", "SEAT", "SEED", "SEEK", "SEEM", "SEEN", +"SEES", "SELF", "SELL", "SEND", "SENT", "SETS", "SEWN", "SHAG", +"SHAM", "SHAW", "SHAY", "SHED", "SHIM", "SHIN", "SHOD", "SHOE", +"SHOT", "SHOW", "SHUN", "SHUT", "SICK", "SIDE", "SIFT", "SIGH", +"SIGN", "SILK", "SILL", "SILO", "SILT", "SINE", "SING", "SINK", +"SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW", "SKID", "SKIM", +"SKIN", "SKIT", "SLAB", "SLAM", "SLAT", "SLAY", "SLED", "SLEW", +"SLID", "SLIM", "SLIT", "SLOB", "SLOG", "SLOT", "SLOW", "SLUG", +"SLUM", "SLUR", "SMOG", "SMUG", "SNAG", "SNOB", "SNOW", "SNUB", +"SNUG", "SOAK", "SOAR", "SOCK", "SODA", "SOFA", "SOFT", "SOIL", +"SOLD", "SOME", "SONG", "SOON", "SOOT", "SORE", "SORT", "SOUL", +"SOUR", "SOWN", "STAB", "STAG", "STAN", "STAR", "STAY", "STEM", +"STEW", "STIR", "STOW", "STUB", "STUN", "SUCH", "SUDS", "SUIT", +"SULK", "SUMS", "SUNG", "SUNK", "SURE", "SURF", "SWAB", "SWAG", +"SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM", "TACK", "TACT", +"TAIL", "TAKE", "TALE", "TALK", "TALL", "TANK", "TASK", "TATE", +"TAUT", "TEAL", "TEAM", "TEAR", "TECH", "TEEM", "TEEN", "TEET", +"TELL", "TEND", "TENT", "TERM", "TERN", "TESS", "TEST", "THAN", +"THAT", "THEE", "THEM", "THEN", "THEY", "THIN", "THIS", "THUD", +"THUG", "TICK", "TIDE", "TIDY", "TIED", "TIER", "TILE", "TILL", +"TILT", "TIME", "TINA", "TINE", "TINT", "TINY", "TIRE", "TOAD", +"TOGO", "TOIL", "TOLD", "TOLL", "TONE", "TONG", "TONY", "TOOK", +"TOOL", "TOOT", "TORE", "TORN", "TOTE", "TOUR", "TOUT", "TOWN", +"TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG", "TRIM", "TRIO", +"TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE", "TUCK", "TUFT", +"TUNA", "TUNE", "TUNG", "TURF", "TURN", "TUSK", "TWIG", "TWIN", +"TWIT", "ULAN", "UNIT", "URGE", "USED", "USER", "USES", "UTAH", +"VAIL", "VAIN", "VALE", "VARY", "VASE", "VAST", "VEAL", "VEDA", +"VEIL", "VEIN", "VEND", "VENT", "VERB", "VERY", "VETO", "VICE", +"VIEW", "VINE", "VISE", "VOID", "VOLT", "VOTE", "WACK", "WADE", +"WAGE", "WAIL", "WAIT", "WAKE", "WALE", "WALK", "WALL", "WALT", +"WAND", "WANE", "WANG", "WANT", "WARD", "WARM", "WARN", "WART", +"WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY", "WAYS", "WEAK", +"WEAL", "WEAN", "WEAR", "WEED", "WEEK", "WEIR", "WELD", "WELL", +"WELT", "WENT", "WERE", "WERT", "WEST", "WHAM", "WHAT", "WHEE", +"WHEN", "WHET", "WHOA", "WHOM", "WICK", "WIFE", "WILD", "WILL", +"WIND", "WINE", "WING", "WINK", "WINO", "WIRE", "WISE", "WISH", +"WITH", "WOLF", "WONT", "WOOD", "WOOL", "WORD", "WORE", "WORK", +"WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE", "YANG", "YANK", +"YARD", "YARN", "YAWL", "YAWN", "YEAH", "YEAR", "YELL", "YOGA", +"YOKE" }; + +void hmac_md5(unsigned char *text, int text_len, + unsigned char *key, int key_len, + unsigned char *digest); +char *hmac_key; + +int read_hmac_key(void) +{ + struct stat statbuf; + int fd; + + if (stat(REG_SVR_HMAC_KEY, &statbuf)) + return 0; + + fd = open(REG_SVR_HMAC_KEY, O_RDONLY); + if (!fd) + return 0; + + hmac_key = malloc(statbuf.st_size); + if (!hmac_key) + return 0; + + if (read(fd, hmac_key, statbuf.st_size) != statbuf.st_size) + return 0; + + close(fd); + if (hmac_key[statbuf.st_size - 1] == '\n') + hmac_key[statbuf.st_size - 1] = '\0'; + return 1; +} + +/* This isn't amazingly random, but then, it doesn't especially need to be */ +unsigned short *init_rand(void *foo) +{ + struct tms tms; + unsigned short *rand = xmalloc(16); + + tms.tms_cutime = times(&tms); + tms.tms_cstime = (long)foo; + hmac_md5((char *)&tms, sizeof(tms), hmac_key, strlen(hmac_key), + (unsigned char *)rand); + return rand; +} + +void getwordlist(char *input, char *words[]) +{ + unsigned char digest[16]; + int i; + + memset(digest, 0, sizeof(digest)); + hmac_md5(input, strlen(input), hmac_key, strlen(hmac_key), digest); + + /* "Fold" the digest onto itself... */ + for(i = 0; i < 8; i++) + digest[i] ^= digest[i+8]; + + words[0] = dict[(digest[0] << 1) | (digest[1] >> 7)]; + words[1] = dict[((digest[1] & 0x7f) << 4) | (digest[2] >> 4)]; + words[2] = dict[((digest[2] & 0x0f) << 7) | (digest[3] >> 1)]; + words[3] = dict[((digest[3] & 0x01) << 10) | (digest[4] << 2) + | (digest[5] >> 6)]; + words[4] = dict[((digest[5] & 0x3f) << 5) | (digest[6] >> 3)]; + words[5] = dict[((digest[6] & 0x03) << 8) | digest[7]]; +} + + +/* HMAC MD5 (RFC 2104) */ + +void hmac_md5(unsigned char *text, int text_len, + unsigned char *key, int key_len, + unsigned char *digest) +{ + MD5_CTX context; + unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */ + unsigned char k_opad[65]; /* outer padding - key XORd with opad */ + unsigned char tk[16]; + int i; + + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + MD5_CTX tctx; + + MD5Init(&tctx); + MD5Update(&tctx, key, key_len); + MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + memset(k_ipad, 0, sizeof(k_ipad)); + memset(k_opad, 0, sizeof(k_opad)); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i = 0; i < 64; i++) + { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* perform inner MD5 */ + MD5Init(&context); + MD5Update(&context, k_ipad, 64); + MD5Update(&context, text, text_len); + MD5Final(digest, &context); + + /* perform outer MD5 */ + MD5Init(&context); + MD5Update(&context, k_opad, 64); + MD5Update(&context, digest, 16); + MD5Final(digest, &context); +} -- 2.45.2