server/admin.c: Remove spurious `ping' in usage message.
[tripe] / server / keymgmt.c
index 9e4bc5b..33fa7e2 100644 (file)
@@ -9,19 +9,18 @@
  *
  * This file is part of Trivial IP Encryption (TrIPE).
  *
- * TrIPE is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * TrIPE is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
  *
- * TrIPE is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * TrIPE is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with TrIPE; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with TrIPE.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 /*----- Header files ------------------------------------------------------*/
@@ -146,7 +145,7 @@ typedef struct keyhalf {
   const char *kind;
   int (*load)(key_file *, key *, key_data *,
              const dhops *, kdata *, dstr *, dstr *);
-  const char *kr;
+  char *kr;
   key_file *kf;
   fwatch w;
   sym_table tab;
@@ -203,6 +202,8 @@ static int kh_loadpriv(key_file *kf, key *k, key_data *d,
 {
   int rc;
   const char *err;
+  dhge *K;
+  int ok;
 
   if ((rc = dh->ldpriv(kf, k, d, kd, t, e)) != 0)
     goto fail_0;
@@ -211,6 +212,13 @@ static int kh_loadpriv(key_file *kf, key *k, key_data *d,
     a_format(e, "bad-group", "%s", err, A_END);
     goto fail_1;
   }
+  K = kd->grp->ops->mul(kd->grp, kd->k, 0);
+  ok = kd->grp->ops->eq(kd->grp, kd->K, K);
+  kd->grp->ops->freege(kd->grp, K);
+  if (!ok) {
+    a_format(e, "incorrect-public-key", A_END);
+    goto fail_1;
+  }
   return (0);
 
 fail_1:
@@ -267,18 +275,15 @@ static int kh_reopen(keyhalf *kh)
   key_file *kf = CREATE(key_file);
 
   if (key_open(kf, kh->kr, KOPEN_READ, keymoan, kh)) {
-    a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", kh->kr,
-          "io-error", "?ERRNO", A_END);
     DESTROY(kf);
     return (-1);
-  } else {
-    if (kh->kf) {
-      key_close(kh->kf);
-      DESTROY(kh->kf);
-    }
-    kh->kf = kf;
-    return (0);
   }
+  if (kh->kf) {
+    key_close(kh->kf);
+    DESTROY(kh->kf);
+  }
+  kh->kf = kf;
+  return (0);
 }
 
 /* --- @kh_init@ --- *
@@ -286,22 +291,21 @@ static int kh_reopen(keyhalf *kh)
  * Arguments:  @keyhalf *kh@ = pointer to keyhalf structure to set up
  *             @const char *kr@ = name of the keyring file
  *
- * Returns:    ---
+ * Returns:    Zero on success, @-1@ on error.
  *
  * Use:                Initialize a keyhalf structure, maintaining the private or
  *             public keys.  Intended to be called during initialization:
  *             exits if there's some kind of problem.
  */
 
-static void kh_init(keyhalf *kh, const char *kr)
+static int kh_init(keyhalf *kh, const char *kr)
 {
-  kh->kr = kr;
+  if (kh->kf) return (0);
+  kh->kr = xstrdup(kr);
+  if (kh_reopen(kh)) return (-1);
   fwatch_init(&kh->w, kr);
   sym_create(&kh->tab);
-  kh->kf = 0;
-
-  if (kh_reopen(kh))
-    die(EXIT_FAILURE, "failed to load %s keyring `%s'", kh->kind, kr);
+  return (0);
 }
 
 /* --- @kh_load@ --- *
@@ -369,7 +373,7 @@ founddh:
   }
 
   if (algs_get(&kd->algs, &e, kh->kf, k) ||
-      (kd->k && algs_check(&kd->algs, &e, kd->grp))) {
+      algs_check(&kd->algs, &e, kd->grp)) {
     a_warn("KEYMGMT", "%s-keyring", kh->kind,
           "%s", kh->kr, "key", "%s", t.buf,
           "*%s", e.buf, A_END);
@@ -379,6 +383,7 @@ founddh:
   kd->tag = xstrdup(t.buf);
   kd->ref = 1;
   kd->kn = 0;
+  kd->id = k->id;
   kd->t_exp = k->exp;
 
   IF_TRACING(T_KEYMGMT, {
@@ -389,6 +394,8 @@ founddh:
       if (kd->k)
        trace(T_CRYPTO, "crypto: k = %s", g->ops->scstr(g, kd->k));
       trace(T_CRYPTO, "crypto: K = %s", g->ops->gestr(g, kd->K));
+      trace(T_CRYPTO, "crypto: bulk transform = %s",
+           kd->algs.bulk->ops->name);
       kd->algs.bulk->ops->tracealgs(kd->algs.bulk);
     })
   })
@@ -525,10 +532,33 @@ static int kh_refresh(keyhalf *kh)
   return (changep);
 }
 
+/* --- @kh_clear@ --- *
+ *
+ * Arguments:  @keyhalf *kh@ = pointer to keyhalf structure
+ *
+ * Returns:    ---
+ *
+ * Use:                Clears out the keyhalf's keyring and flushes the cache.
+ */
+
+static void kh_clear(keyhalf *kh)
+{
+  sym_iter i;
+  knode *kn;
+
+  if (!kh->kf) return;
+  for (sym_mkiter(&i, &kh->tab); (kn = sym_next(&i)) != 0; )
+    if (kn->kd) km_unref(kn->kd);
+  sym_destroy(&kh->tab);
+  key_close(kh->kf);
+  xfree(kh->kr);
+  kh->kf = 0;
+}
+
 /*----- Main code ---------------------------------------------------------*/
 
-const char *tag_priv;
-kdata *master;
+char *tag_priv = 0;
+kdata *master = 0;
 
 /* --- @km_init@ --- *
  *
@@ -536,28 +566,37 @@ kdata *master;
  *             @const char *pubkr@ = public keyring file
  *             @const char *ptag@ = default private-key tag
  *
- * Returns:    ---
+ * Returns:    Zero on success, @-1@ on failure.
  *
  * Use:                Initializes the key-management machinery, loading the
  *             keyrings and so on.
  */
 
-void km_init(const char *privkr, const char *pubkr, const char *ptag)
+int km_init(const char *privkr, const char *pubkr, const char *ptag)
 {
   const gchash *const *hh;
+  kdata *kd;
 
   for (hh = ghashtab; *hh; hh++) {
     if ((*hh)->hashsz > MAXHASHSZ) {
-      die(EXIT_FAILURE, "INTERNAL ERROR: %s hash length %lu > MAXHASHSZ %d",
-         (*hh)->name, (unsigned long)(*hh)->hashsz, MAXHASHSZ);
+      a_warn("ABORT", "hash-size-too-large", "hash",
+            "%s", (*hh)->name, "size", "%lu", (*hh)->hashsz,
+            "limit", "%d", MAXHASHSZ, A_END);
+      abort();
     }
   }
 
-  kh_init(&priv, privkr);
-  kh_init(&pub, pubkr);
+  if (kh_init(&priv, privkr) || kh_init(&pub, pubkr))
+    return (-1);
+
+  tag_priv = ptag ? xstrdup(ptag) : 0;
+  kh_refresh(&priv);
 
-  tag_priv = ptag;
-  if ((master = km_findpriv(ptag)) == 0) exit(EXIT_FAILURE);
+  if ((kd = km_findpriv(tag_priv)) == 0) return (-1);
+  if (master) km_unref(master);
+  master = kd;
+
+  return (0);
 }
 
 /* --- @km_reload@ --- *
@@ -588,6 +627,26 @@ int km_reload(void)
   return (changep);
 }
 
+/* --- @km_clear@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Forget the currently loaded keyrings.  The @master@ key will
+ *             be cleared, but other keys already loaded will continue to
+ *             exist until their reference count drops to zero.  Call
+ *             @km_init@ to make everything work again.
+ */
+
+void km_clear(void)
+{
+  kh_clear(&priv);
+  kh_clear(&pub);
+  if (master) { km_unref(master); master = 0; }
+  if (tag_priv) { xfree(tag_priv); tag_priv = 0; }
+}
+
 /* --- @km_findpub@, @km_findpriv@ --- *
  *
  * Arguments:  @const char *tag@ = key tag to load
@@ -608,6 +667,41 @@ kdata *km_findpriv(const char *tag)
   else return (kh_find(&priv, tag ? tag : "tripe-dh", 1));
 }
 
+/* --- @km_findpubbyid@, @km_findprivbyid@ --- *
+ *
+ * Arguments:  @uint32 id@ = key id to load
+ *
+ * Returns:    Pointer to the kdata object if successful, or null on error.
+ *
+ * Use:                Fetches a public or private key from the keyring given its
+ *             numeric id.
+ */
+
+static kdata *findbyid(keyhalf *kh, uint32 id)
+{
+  key *k;
+  kdata *kd;
+
+  k = key_byid(kh->kf, id); if (!k) goto notfound;
+  kd = kh_find(kh, k->tag, 1); if (!kd) goto notfound;
+  if (kd->id != id) { km_unref(kd); goto notfound; }
+  return (kd);
+
+notfound:
+  a_warn("KX", "%s-keyring", kh->kind, "%s", kh->kr,
+        "unknown-key-id", "0x%08lx", (unsigned long)id,
+        A_END);
+  return (0);
+}
+
+kdata *km_findpubbyid(uint32 id) { return (findbyid(&pub, id)); }
+
+kdata *km_findprivbyid(uint32 id)
+{
+  if (id == master->id) { km_ref(master); return (master); }
+  else return findbyid(&priv, id);
+}
+
 /* --- @km_tag@ --- *
  *
  * Arguments:  @kdata *kd@ - pointer to the kdata object