@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@
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) $<
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)
--- /dev/null
+# 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!
--- /dev/null
+/* $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
+ * <mit-copyright.h>.
+ *
+ */
+
+#include <mit-copyright.h>
+#include <moira.h>
+#include "reg_svr.h"
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <com_err.h>
+
+/* 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++;
+ }
+}
--- /dev/null
+/* $Id$
+ *
+ * Kerberos routines for registration server
+ *
+ * Copyright (C) 1998 by the Massachusetts Institute of Technology
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ */
+
+#include <mit-copyright.h>
+#include <moira.h>
+#include "reg_svr.h"
+
+#if !defined(KRB4) && !defined(KRB5)
+#define KRB5
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include <com_err.h>
+
+#ifdef KRB4
+#include <des.h>
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb.h>
+#endif
+
+#ifdef KRB5
+#include <kadm5/admin.h>
+#include <krb5.h>
+#include <krb.h>
+
+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
--- /dev/null
+/* $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
+ * <mit-copyright.h>.
+ *
+ */
+
+#include <mit-copyright.h>
+#include <moira.h>
+#include "reg_svr.h"
+
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <com_err.h>
+#include <des.h>
+
+/* 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");
+}
+++ /dev/null
-/* $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
- * <mit-copyright.h>.
- */
-
-#include <mit-copyright.h>
-#include <moira.h>
-#include <moira_site.h>
-
-#include <sys/utsname.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <des.h>
-#include <kadm.h>
-#include <kadm_err.h>
-#include <krb.h>
-
-#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;
-}
-/*
- * $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 <sys/time.h>
+#include <des.h>
-#include <sys/types.h>
-#include <ctype.h>
-#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"
--- /dev/null
+/* $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
+ * <mit-copyright.h>.
+ */
+
+#include <mit-copyright.h>
+#include <moira.h>
+#include <mr_private.h>
+#include <moira_schema.h>
+#include <moira_site.h>
+#include "reg_svr.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <com_err.h>
+#include <krb.h>
+
+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;
+}
+++ /dev/null
-/* $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
- * <mit-copyright.h>.
- */
-
-#include <mit-copyright.h>
-#include <moira.h>
-#include <moira_site.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#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;
-}
--- /dev/null
+/* $Id$
+ *
+ * 6 Magic Words generation
+ *
+ * Copyright (C) 1998 by the Massachusetts Institute of Technology
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ */
+
+#include <mit-copyright.h>
+#include <moira.h>
+#include "reg_svr.h"
+
+#include <sys/stat.h>
+#include <sys/times.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* 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);
+}