]> andersk Git - moira.git/commitdiff
New reg_svr
authordanw <danw>
Wed, 22 Jul 1998 14:55:23 +0000 (14:55 +0000)
committerdanw <danw>
Wed, 22 Jul 1998 14:55:23 +0000 (14:55 +0000)
reg_svr/Makefile.in
reg_svr/errors [new file with mode: 0644]
reg_svr/genkey.c [new file with mode: 0644]
reg_svr/kerberos.c [new file with mode: 0644]
reg_svr/protocol.c [new file with mode: 0644]
reg_svr/reg_svr.c [deleted file]
reg_svr/reg_svr.h
reg_svr/reg_svr.pc [new file with mode: 0644]
reg_svr/requests.c [deleted file]
reg_svr/words.c [new file with mode: 0644]

index 8f430fd7a9fa3b2d5bc50dc99ecc7befd1d30e99..ed222507b23413b881e9bfc2b344dc8a43fc50ae 100644 (file)
@@ -4,13 +4,18 @@ SHELL=/bin/sh
 @SET_MAKE@
 
 CC=@CC@
-CPPFLAGS=@CPPFLAGS@
+CPPFLAGS=@CPPFLAGS@ @RSAREF_CPPFLAGS@
 CFLAGS=@CFLAGS@
 DEFS=@DEFS@
 ALL_CFLAGS=$(CPPFLAGS) $(CFLAGS) $(DEFS)
+PRO_C=@PRO_C@
+PRO_C_DEFS=@PRO_C_DEFS@
+PRO_C_INCLUDES=@PRO_C_INCLUDES@
+PRO_C_OPTS=@PRO_C_OPTS@
+PRO_C_FLAGS=$(PRO_C_OPTS) $(PRO_C_DEFS) $(PRO_C_INCLUDES)
 LDFLAGS=@LDFLAGS@
 SQL_LIBS=@SQL_LIBS@
-LIBS=-lkadm @LIBS@
+LIBS=-lkadm @RSAREF_LIBS@ @LIBS@
 INSTALL=@INSTALL@
 INSTALL_PROGRAM=@INSTALL_PROGRAM@
 
@@ -20,10 +25,18 @@ SRCTOP=@top_srcdir@
 BUILDTOP=..
 mrbindir=@mrbindir@
 
-REG_OBJS=reg_svr.o requests.o
-START_OBJS=startreg.o
+REG_OBJS=      kerberos.o protocol.o reg_svr.o words.o
+START_OBJS=    startreg.o
+GEN_OBJS=      genkey.o
 
-TARGET=reg_svr startreg
+CFILES=                reg_svr.c
+
+TARGET=                reg_svr startreg genkey
+
+.SUFFIXES: .pc
+
+.pc.c:
+       $(PRO_C) $(PRO_C_FLAGS) INAME=$< ONAME=$@
 
 .c.o:
        $(CC) -c $(ALL_CFLAGS) $<
@@ -31,16 +44,19 @@ TARGET=reg_svr startreg
 all: $(TARGET)
 
 clean:
-       rm -f $(REG_OBJS) $(START_OBJS) $(TARGET)
+       rm -f $(REG_OBJS) $(START_OBJS) $(CFILES) $(TARGET)
 
-depend:
+depend: $(CFILES)
 
 install: all
        $(INSTALL_PROGRAM) reg_svr $(mrbindir)
        $(INSTALL_PROGRAM) startreg $(mrbindir)
 
 reg_svr: $(REG_OBJS)
-       $(CC) -o $@ $(LDFLAGS) $(REG_OBJS) ../server/libmrglue.a $(SQL_LIBS) $(LIBS)
+       $(CC) -o $@ $(LDFLAGS) $(REG_OBJS) $(SQL_LIBS) $(LIBS)
 
 startreg: $(START_OBJS)
        $(CC) -o $@ $(LDFLAGS) $(START_OBJS) $(LIBS)
+
+genkey: $(GEN_OBJS)
+       $(CC) -o $@ $(LDFLAGS) $(GEN_OBJS) $(LIBS)
diff --git a/reg_svr/errors b/reg_svr/errors
new file mode 100644 (file)
index 0000000..ddb2a47
--- /dev/null
@@ -0,0 +1,95 @@
+# 1 INTERNAL_ERROR
+An internal error has occurred, and the registration applet is unable
+to continue. The error is: "%s".
+
+Please try again later. If this problem persists, contact the Athena
+User Accounts Office in N42-105A, x3-1325.
+# 2 PROTOCOL_ERROR
+A protocol error has occurred, and the registration applet is unable
+to continue. This may indicate that you are using an out-of-date
+version of the registration applet.
+
+If you are unable to fix this problem, contact the Athena User
+Accounts Office in N42-105A, x3-1325.
+# 3 DATABASE_CLOSED
+Sorry, it appears that the Moira database (which is needed for account
+registration) is currently closed. Please try again later.
+# 4 ENCRYPT_KEY
+The registration server is unable to decrypt the data from the
+registration applet. This probably means that you are using an
+out-of-date version of the registration applet.
+
+If you are unable to fix this problem, contact the Athena User
+Accounts Office in N42-105A, x3-1325.
+# 5 NOT_FOUND_IN_DATABASE
+The registration server was unable to locate you in the accounts
+database. Please make sure you typed in your name and MIT ID number
+correctly.
+
+If you are a new staff member, this may mean that you have not been
+entered into the database yet. Try again tomorrow. If this problem
+persists, contact the Athena User Accounts Office in N42-105A,
+x3-1325.
+# 6 ALREADY_REGISTERED
+You have already registered for an account. The username you selected
+is "%s".
+
+If you don't remember your password, or you do not think that this is
+your account, contact the Athena User Accounts Office in N42-105A,
+x3-1325.
+# 7 ACCOUNT_DELETED
+Your account, "%s", has been deactivated, and you are not listed as
+eligible for another account. If you have any questions, contact
+the Athena User Accounts Office in N42-105A, x3-1325.
+# 8 NOT_ELIGIBLE
+You were found in the database, but are listed as ineligible for an
+account. If you have any questions, contact the Athena User Accounts
+Office in N42-105A, x3-1325.
+# 9 FOUND
+OK. I've found you in the database: %s, account class %s.
+# 10 FORCED_USERNAME
+OK. I've found you in the database: %s, account class %s.
+
+The username "%s" has previously been reserved for this account.
+# 11 BAD_SIX_WORDS
+At least one of the six words you typed was incorrect. Please make
+sure you typed in all six of the words correctly and in order.
+
+If you have lost your Athena Account Coupon, you will need to
+get a new one, by going to either the Student Services Center in
+Building 11, or the Athena User Accounts Office in N42-105A, x3-1325.
+# 12 BAD_USERNAME
+"%s" is not a valid username. Your username must be between %d and %d
+characters long, must start with a lowercase letter, and must consist
+entirely of lowercase letters, digits 0-9, and the underscore (_)
+character.
+# 13 USERNAME_UNAVAILABLE
+Sorry, that username is already taken. Please select another.
+# 14 RESERVED_USERNAME_UNAVAILABLE
+Oops. Your account was reserved with the username "%s", but that
+username is already taken. You will not be able to register your
+account until this problem is resolved. Please contact the Athena User
+Accounts Office in N42-105A, x3-1325.
+# 15 USERNAME_OK
+OK. I've reserved the username "%s" for you.
+# 16 PASSWORD_SHORT
+That password is too short! Please use a longer password.
+# 17 PASSWORD_SIMPLE
+That password is too simple. Please use a password with a mix of
+different kinds of characters.
+# 18 PASSWORD_SAMPLE
+Those were just examples! You should invent your own secret password,
+that no one else knows.
+# 19 KADM_ERROR
+An unexpected error occurred while trying to register your username
+and password with the Kerberos server. The error was: "%s".
+
+Please try again later. If this problem persists, contact the Athena
+User Accounts Office in N42-105A, x3-1325.
+# 20 DONE
+Your account has been registered and will become active later tonight.
+
+You should be able to log in by tomorrow morning using your Athena
+username (%s) and the password you chose.
+
+Welcome to Athena!
diff --git a/reg_svr/genkey.c b/reg_svr/genkey.c
new file mode 100644 (file)
index 0000000..228272b
--- /dev/null
@@ -0,0 +1,134 @@
+/* $Id$
+ *
+ * Utility program to generate a public/private key pair
+ *
+ * Copyright (C) 1998 by the Massachusetts Institute of Technology
+ * For copying and distribution information, please see the file
+ * <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++;
+    }
+}
diff --git a/reg_svr/kerberos.c b/reg_svr/kerberos.c
new file mode 100644 (file)
index 0000000..0f68a45
--- /dev/null
@@ -0,0 +1,208 @@
+/* $Id$
+ *
+ * Kerberos routines for registration server
+ *
+ * Copyright (C) 1998 by the Massachusetts Institute of Technology
+ * For copying and distribution information, please see the file
+ * <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
diff --git a/reg_svr/protocol.c b/reg_svr/protocol.c
new file mode 100644 (file)
index 0000000..a5a7f01
--- /dev/null
@@ -0,0 +1,329 @@
+/* $Id$
+ *
+ * Reg_svr protocol and encryption/decryption routines
+ *
+ * Copyright (C) 1998 by the Massachusetts Institute of Technology
+ * For copying and distribution information, please see the file
+ * <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");
+}
diff --git a/reg_svr/reg_svr.c b/reg_svr/reg_svr.c
deleted file mode 100644 (file)
index daef5ab..0000000
+++ /dev/null
@@ -1,968 +0,0 @@
-/* $Id$
- *
- * Server for user registration with Moira and Kerberos.
- *
- * This program is a client of the Kerberos admin_server and a
- * server for the userreg program.  It is not a client of the
- * Moira server as it is linked with libmoiraglue which bypasses
- * the network protocol.
- *
- * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
- * For copying and distribution information, please see the file
- * <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;
-}
index a5f0d54e949dab08677e78bfafcfb61f5f23c742..a4d26e20554551643f297d8b5f8b733dd9a2d355 100644 (file)
@@ -1,77 +1,77 @@
-/*
- *      $Source$
- *      $Author$
- *      $Header$
- *
- *      Copyright (C) 1987 by the Massachusetts Institute of Technology
- *
- *      Server for user registration with Moira and Kerberos.
- *
- *      This file contains all the information needed by all source
- *      files for the user registration server.
- */
+#include <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"
diff --git a/reg_svr/reg_svr.pc b/reg_svr/reg_svr.pc
new file mode 100644 (file)
index 0000000..5dcdba6
--- /dev/null
@@ -0,0 +1,847 @@
+/* $Id$
+ *
+ * Server for user registration with Moira and Kerberos.
+ *
+ * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
+ * For copying and distribution information, please see the file
+ * <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;
+}
diff --git a/reg_svr/requests.c b/reg_svr/requests.c
deleted file mode 100644 (file)
index 56d2614..0000000
+++ /dev/null
@@ -1,394 +0,0 @@
-/* $Id$
- *
- * Server for user registration with Moira and Kerberos.
- *
- * This file handles the processing of requests for the register
- * server.
- *
- * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
- * For copying and distribution information, please see the file
- * <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;
-}
diff --git a/reg_svr/words.c b/reg_svr/words.c
new file mode 100644 (file)
index 0000000..2956424
--- /dev/null
@@ -0,0 +1,414 @@
+/* $Id$
+ *
+ * 6 Magic Words generation
+ *
+ * Copyright (C) 1998 by the Massachusetts Institute of Technology
+ * For copying and distribution information, please see the file
+ * <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);
+}
This page took 0.158969 seconds and 5 git commands to generate.