Support elliptic curves, and bigger hashes.
[become] / src / daemon.c
index 8d69510..3320512 100644 (file)
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * $Id: daemon.c,v 1.12 2003/10/12 00:14:55 mdw Exp $
+ * $Id: daemon.c,v 1.18 2004/04/17 10:46:08 mdw Exp $
  *
  * Running a `become' daemon
  *
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-/*----- Revision history --------------------------------------------------*
- *
- * $Log: daemon.c,v $
- * Revision 1.12  2003/10/12 00:14:55  mdw
- * Major overhaul.  Now uses DSA signatures rather than the bogus symmetric
- * encrypt-and-hope thing.  Integrated with mLib and Catacomb.
- *
- * Revision 1.11  1999/05/04 16:17:12  mdw
- * Change to header file name for parser.  See log for `parse.h' for
- * details.
- *
- * Revision 1.10  1998/04/23  13:23:09  mdw
- * Support new interface to configuration file parser.
- *
- * Revision 1.9  1998/01/12 16:45:59  mdw
- * Fix copyright date.
- *
- * Revision 1.8  1997/09/26 09:14:58  mdw
- * Merged blowfish branch into trunk.
- *
- * Revision 1.7.2.1  1997/09/26 09:08:05  mdw
- * Use the Blowfish encryption algorithm instead of IDEA.  This is partly
- * because I prefer Blowfish (without any particularly strong evidence) but
- * mainly because IDEA is patented and Blowfish isn't.
- *
- * Revision 1.7  1997/09/17 10:23:23  mdw
- * Fix a typo.  Port numbers are in network order now, so don't change them.
- *
- * Revision 1.6  1997/09/09 18:17:06  mdw
- * Allow default port to be given as a service name or port number.
- *
- * Revision 1.5  1997/08/20  16:17:10  mdw
- * More sensible restart routine: `_reinit' functions replaced by `_end' and
- * `_init' functions.
- *
- * Revision 1.4  1997/08/07 10:00:37  mdw
- * (Log entry for previous version is bogus.)  Read netgroups database.
- * Give up privileges permanently on startup.
- *
- * Revision 1.2  1997/08/04 10:24:21  mdw
- * Sources placed under CVS control.
- *
- * Revision 1.1  1997/07/21  13:47:50  mdw
- * Initial revision
- *
- */
-
 /*----- Header files ------------------------------------------------------*/
 
 /* --- ANSI headers --- */
 /* --- Catacomb headers --- */
 
 #include <catacomb/buf.h>
-#include <catacomb/dsa.h>
+#include <catacomb/gdsa.h>
 #include <catacomb/key.h>
+#include <catacomb/dh.h>
+#include <catacomb/ec-keys.h>
 #include <catacomb/mp.h>
 #include <catacomb/noise.h>
 #include <catacomb/paranoia.h>
 
 /*----- Arbitrary constants -----------------------------------------------*/
 
-#define daemon__awakeEvery (5 * 60)    /* Awaken this often to rescan */
+#define daemon__awakeEvery (10)                /* Awaken this often to rescan */
 
 /*----- Static variables --------------------------------------------------*/
 
 static int daemon__port = -1;          /* No particular port yet */
-static int daemon__readKey = 0;                /* Have I read a key? */
-static fwatch daemon__watch;
+static fwatch daemon__cwatch, daemon__kwatch; /* Watching key/config files */
 static sel_timer daemon__timer;                /* Timer for reading */
 static sel_state daemon__sel;          /* Select context */
 static sel_file daemon__listen;                /* Listening socket selector */
 static const char *daemon__config;     /* Configuration file for daemon */
-static dsa_priv daemon__key;           /* The key data */
+static const char *daemon__keyfile;    /* Keyring file for daemon */
+static gdsa daemon__key;               /* The key data */
 
 /*----- Main code ---------------------------------------------------------*/
 
@@ -176,6 +131,8 @@ void daemon_usePort(int port)
 static void daemon__moan(const char *f, int line, const char *msg, void *p)
 {
   syslog(LOG_ERR, "key file error: %s: %d: %s", f, line, msg);
+  T( trace(TRACE_DAEMON, "daemon: key file error: %s: %d: %s",
+          f, line, msg); )
 }
  
 /* --- @daemon_readKey@ --- *
@@ -189,32 +146,95 @@ static void daemon__moan(const char *f, int line, const char *msg, void *p)
 
 void daemon_readKey(const char *kf)
 {
-  key_packstruct kps[DSA_PRIVFETCHSZ];
   key_packdef *kp;
   key_file f;
   key *k;
   int err;
+  const char *sn;
+  const char *hn;
+  const char *errmsg;
+  gdsa g;
 
-  if (daemon__readKey)
+  if (daemon__keyfile)
     return;
+  T( trace(TRACE_DAEMON, "daemon: reading key from `%s'", kf); )
   if (key_open(&f, kf, KOPEN_READ, daemon__moan, 0))
-    return;
-  kp = key_fetchinit(dsa_privfetch, kps, &daemon__key);
-  if ((k = key_bytype(&f, "become-dsa")) == 0)
-    err = KERR_NOTFOUND;
-  else
-    err = key_fetch(kp, k);
-  if (err)
-    syslog(LOG_ERR, "couldn't load key: %s", key_strerror(err));
-  else {
-    mp_copy(daemon__key.dp.p);
-    mp_copy(daemon__key.dp.q);
-    mp_copy(daemon__key.dp.g);
-    mp_copy(daemon__key.x);
-    mp_copy(daemon__key.y);
+    goto fail_0;
+  if ((k = key_bytype(&f, "become")) == 0) {
+    syslog(LOG_ERR, "no key of type `become' found");
+    goto fail_1;
+  }
+  if ((hn = key_getattr(&f, k, "hash")) == 0)
+    hn = "sha";
+  sn = key_getattr(&f, k, "sig");
+  g.r = &rand_global;
+  if ((g.h = ghash_byname(hn)) == 0) {
+    syslog(LOG_ERR, "key uses unknown hash algorithm `%s'", hn);
+    goto fail_1;
   }
+  if (!sn || strcmp(sn, "dsa") == 0) {
+    dh_priv dp;
+    kp = key_fetchinit(dh_privfetch, 0, &dp);
+    if ((err = key_fetch(kp, k)) != 0) {
+      syslog(LOG_ERR, "error loading key: %s", key_strerror(err));
+      goto fail_2;
+    }
+    if ((g.g = group_prime(&dp.dp)) == 0) {
+      syslog(LOG_ERR, "bad prime group in key");
+      goto fail_3;
+    }
+    g.p = G_CREATE(g.g);
+    if (G_FROMINT(g.g, g.p, dp.y)) {
+      syslog(LOG_ERR, "bad public key");
+      goto fail_4;
+    }
+    g.u = mp_copy(dp.x);
+  } else if (strcmp(sn, "ecdsa") == 0) {
+    ec_priv ep;
+    ec_info ei;
+    kp = key_fetchinit(ec_privfetch, 0, &ep);
+    if ((err = key_fetch(kp, k)) != 0) {
+      syslog(LOG_ERR, "error loading key: %s", key_strerror(err));
+      goto fail_2;
+    }
+    if ((errmsg = ec_getinfo(&ei, ep.cstr)) != 0) {
+      syslog(LOG_ERR, "bad curve in key: %s", errmsg);
+      goto fail_3;
+    }
+    g.g = group_ec(&ei);
+    g.p = G_CREATE(g.g);
+    if (G_FROMEC(g.g, g.p, &ep.p)) {
+      syslog(LOG_ERR, "bad public point");
+      goto fail_4;
+    }
+    g.u = mp_copy(ep.x);
+  } else {
+    syslog(LOG_ERR, "key uses unknown signature scheme `%s'", sn);
+    goto fail_1;
+  }
+  key_fetchdone(kp);
+  daemon__keyfile = kf;
+  key_close(&f);
+  if (daemon__key.g) {
+    mp_drop(daemon__key.u);
+    G_DESTROY(daemon__key.g, daemon__key.p);
+    G_DESTROYGROUP(daemon__key.g);
+  }
+  daemon__key = g;
+  T( trace(TRACE_DAEMON, "daemon: key read ok"); )
+  return;
+
+fail_4:
+  G_DESTROY(g.g, g.p);
+fail_3:
+  G_DESTROYGROUP(g.g);
+fail_2:
   key_fetchdone(kp);
+fail_1:
   key_close(&f);
+fail_0:
+  T( trace(TRACE_DAEMON, "daemon: failed to read key"); )
+  return;
 }
 
 /* --- @daemon__readConfig@ --- *
@@ -230,13 +250,13 @@ static int daemon__readConfig(const char *cf)
 {
   FILE *fp;
 
-  daemon__readKey = 0;
+  daemon__keyfile = 0;
   if ((fp = fopen(cf, "r")) == 0)
     return (-1);
   lexer_scan(fp);
   parse();
   fclose(fp);
-  if (!daemon__readKey)
+  if (!daemon__keyfile)
     daemon_readKey(file_KEY);
   T( trace(TRACE_DAEMON, "daemon: read config file"); )
   return (0);
@@ -258,15 +278,14 @@ void daemon__read(int fd, unsigned mode, void *p)
   unsigned char buff[65536];           /* Buffer for incoming packets */
   struct sockaddr_in sin;              /* Address of packet sender */
   char sender[64];                     /* Sender's hostname (resolved) */
-  octet h[SHA_HASHSZ];                 /* Hash of the transmission buffer */
-  sha_ctx hc;                          /* Hashing context */
+  ghash *h;                            /* Hash context */
   request rq;                          /* Request buffer for verification */
   ssize_t sz;                          /* Length of incoming message */
   socklen_t slen;                      /* Length of incoming address */
   uint32 u;                            /* Scratch integer */
   uint16 ul;                           /* And another */
   struct hostent *he;                  /* Resolve structure */
-  mp *m, *k, *r, *s;                   /* Integers for signing */
+  gdsa_sig s = GDSA_SIG_INIT;          /* Signature block */
   int ans;                             /* Answer from the check */
   buf b;                               /* Buffer for parsing request */
 
@@ -294,11 +313,19 @@ void daemon__read(int fd, unsigned mode, void *p)
   syslog(LOG_DEBUG, "packet received from %s", sender);
   T( trace(TRACE_DAEMON, "daemon: received request from %s", sender); )
 
+  /* --- Sanity check --- */
+
+  if (!daemon__keyfile) {
+    syslog(LOG_NOTICE, "no key file: ignoring request");
+    T( trace(TRACE_DAEMON, "daemon: no key file: ignoring request"); )
+    return;
+  }
+
   /* --- Unpack the block --- */
 
   rq.host = sin.sin_addr;
   buf_init(&b, buff, sz);
-  if (buf_ensure(&b, SHA_HASHSZ)) goto fail;  BSTEP(&b, SHA_HASHSZ);
+  if (buf_ensure(&b, 64)) goto fail; BSTEP(&b, 64);
   if (buf_getu32(&b, &u)) goto fail;  rq.from = u;
   if (buf_getu32(&b, &u)) goto fail;  rq.to = u;
   if (buf_getu16(&b, &ul) || buf_ensure(&b, ul) || ul >= sizeof(rq.cmd))
@@ -310,9 +337,8 @@ void daemon__read(int fd, unsigned mode, void *p)
 
   /* --- Hash the request block --- */
 
-  sha_init(&hc);
-  sha_hash(&hc, buff, sz);
-  sha_done(&hc, h);
+  h = GH_INIT(daemon__key.h);
+  GH_HASH(h, buff, sz);
 
   /* --- Build a reply block --- */
 
@@ -320,27 +346,20 @@ void daemon__read(int fd, unsigned mode, void *p)
   syslog(LOG_INFO, "request from %s for %i to become %i to run %s %s",
         sender, rq.from, rq.to, rq.cmd, ans ? "granted" : "denied");
   buf_init(&b, buff, sizeof(buff));
-  if (buf_put(&b, h, sizeof(h)) || buf_putbyte(&b, ans))
-    goto fail;
+  buf_put(&b, GH_DONE(h, 0), GH_CLASS(h)->hashsz);
+  buf_putbyte(&b, ans);
+  if (BBAD(&b)) goto fail;
 
   /* --- Sign the reply block --- */
 
-  sha_init(&hc);
-  sha_hash(&hc, BBASE(&b), BLEN(&b));
-  sha_done(&hc, h);
-  m = mp_loadb(MP_NEW, h, sizeof(h));
-  rand_get(RAND_GLOBAL, h, sizeof(h));
-  k = mp_loadb(MP_NEWSEC, h, sizeof(h));
-  r = s = MP_NEW;
-  dsa_mksig(&daemon__key.dp, daemon__key.x, m, k, &r, &s);
-  buf_putmp(&b, r);
-  buf_putmp(&b, s);
-  mp_drop(m);
-  mp_drop(k);
-  mp_drop(r);
-  mp_drop(s);
-  if (BBAD(&b))
-    goto fail;
+  h = gdsa_beginhash(&daemon__key);
+  GH_HASH(h, BBASE(&b), BLEN(&b));
+  gdsa_endhash(&daemon__key, h);
+  gdsa_sign(&daemon__key, &s, GH_DONE(h, 0), 0);
+  GH_DESTROY(h);
+  buf_putmp(&b, s.r); buf_putmp(&b, s.s);
+  mp_drop(s.r); mp_drop(s.s);
+  if (BBAD(&b)) goto fail;
 
   /* --- Send the reply off --- */
 
@@ -408,7 +427,6 @@ static void daemon__rescan(int n, void *p)
   rule_end();
   netg_end();
   userdb_end();
-  dsa_privfree(&daemon__key);
   userdb_init();
   userdb_local();
   userdb_yp();
@@ -419,7 +437,8 @@ static void daemon__rescan(int n, void *p)
     syslog(LOG_ERR, "error reading configuration file");
   sel_rmtimer(&daemon__timer);
   daemon__setTimer();
-  fwatch_update(&daemon__watch, daemon__config);
+  fwatch_update(&daemon__cwatch, daemon__config);
+  fwatch_update(&daemon__kwatch, daemon__keyfile);
 }
 
 /* --- @daemon__wakeUp@ --- *
@@ -434,22 +453,30 @@ static void daemon__rescan(int n, void *p)
 
 static void daemon__wakeUp(struct timeval *tv, void *p)
 {
+  T( trace(TRACE_DAEMON, "daemon: interval timer"); )
   rand_seed(RAND_GLOBAL, 160);
-  if (fwatch_update(&daemon__watch, daemon__config))
+  daemon__setTimer();
+  if (fwatch_update(&daemon__cwatch, daemon__config))
     daemon__rescan(0, 0);
+  else if (fwatch_update(&daemon__kwatch, daemon__keyfile)) {
+    const char *kf = daemon__keyfile;
+    daemon__keyfile = 0;
+    daemon_readKey(kf);
+  }
 }
 
 /* --- @daemon_init@ --- *
  *
  * Arguments:  @const char *cf@ = pointer to name of configuration file
  *             @int port@ = port to listen to, or %$-1$% for default
+ *             @unsigned f@ = various flags
  *
  * Returns:    Never.
  *
  * Use:                Starts `become' up in daemon mode.
  */
 
-void daemon_init(const char *cf, int port)
+void daemon_init(const char *cf, int port, unsigned f)
 {
   int s;
   int i;
@@ -496,7 +523,8 @@ void daemon_init(const char *cf, int port)
 
   if (daemon__readConfig(daemon__config))
     die(1, "couldn't read configuration file");
-  fwatch_init(&daemon__watch, daemon__config);
+  fwatch_init(&daemon__cwatch, daemon__config);
+  fwatch_init(&daemon__kwatch, daemon__keyfile);
 
   /* --- Decide on a port to use --- *
    *
@@ -506,9 +534,10 @@ void daemon_init(const char *cf, int port)
 
   if (daemon__port == 0) {
     struct servent *se = getservbyname(quis(), "udp");
-    if (!se)
-      die(1, "no idea which port to listen to");
-    daemon__port = se->s_port;
+    if (se)
+      daemon__port = se->s_port;
+    else
+      daemon__port = htons(SERVER_PORT);
   }
 
   /* --- Now set up a socket --- */
@@ -529,8 +558,7 @@ void daemon_init(const char *cf, int port)
 
   /* --- Fork off into the sunset --- */
 
-#ifdef NDEBUG
-  {
+  if (!(f & df_nofork)) {
     int pid = fork();
     FILE *fp;
 
@@ -553,7 +581,6 @@ void daemon_init(const char *cf, int port)
     }
     T( trace(TRACE_DAEMON, "daemon: forked to pid %li", (long)getpid()); )
   }
-#endif
 
   /* --- Set signal handlers --- */