chiark / gitweb /
Greetings and challenges.
[tripe] / admin.c
diff --git a/admin.c b/admin.c
index 987de7abd385a98685beae6d4bd02bfdafa9f49c..da7ccad665f8b59c0cd63f90f10b435b5bb7e175 100644 (file)
--- a/admin.c
+++ b/admin.c
@@ -38,11 +38,12 @@ const trace_opt tr_opts[] = {
   { 't',       T_TUNNEL,       "tunnel events" },
   { 'r',       T_PEER,         "peer events" },
   { 'a',       T_ADMIN,        "admin interface" },
-  { 'p',       T_PACKET,       "packet contents" },
-  { 'c',       T_CRYPTO,       "crypto details" },
   { 's',       T_KEYSET,       "symmetric keyset management" },
   { 'x',       T_KEYEXCH,      "key exchange" },
   { 'm',       T_KEYMGMT,      "key management" },
+  { 'l',       T_CHAL,         "challenge management" },
+  { 'p',       T_PACKET,       "packet contents" },
+  { 'c',       T_CRYPTO,       "crypto details" },
   { 'A',       T_ALL,          "all of the above" },
   { 0,         0,              0 }
 };
@@ -512,6 +513,25 @@ static long a_parsetime(const char *p)
   return (t);    
 }
 
+/* --- @a_findpeer@ --- *
+ *
+ * Arguments:  @admin *a@ = admin connection
+ *             @const char *pn@ = peer name
+ *
+ * Returns:    The peer, or null if not there.
+ *
+ * Use:                Finds a peer, reporting an error if it failed.
+ */
+
+static peer *a_findpeer(admin *a, const char *pn)
+{
+  peer *p;
+
+  if ((p = p_find(pn)) == 0)
+    a_fail(a, "unknown-peer %s", pn);
+  return (p);
+}
+
 /*----- Backgrounded operations -------------------------------------------*/
 
 #define BGTAG(bg)                                                      \
@@ -604,108 +624,192 @@ static void a_bgadd(admin *a, admin_bgop *bg, const char *tag,
   if (tag) a_write(a, "DETACH", tag, 0);
 }
 
-/*----- Adding peers ------------------------------------------------------*/
+/*----- Name resolution operations ----------------------------------------*/
 
-/* --- @a_addfree@ --- *
+/* --- @a_resolved@ --- *
  *
- * Arguments:  @admin_addop *add@ = operation block
+ * Arguments:  @struct hostent *h@ = pointer to resolved hostname
+ *             @void *v@ = pointer to resolver operation
  *
  * Returns:    ---
  *
- * Use:                Frees an add operation.
+ * Use:                Handles a completed name resolution.
  */
 
-static void a_addfree(admin_addop *add)
+static void a_resolved(struct hostent *h, void *v)
 {
-  T( trace(T_ADMIN, "admin: free add op %s", BGTAG(add)); )
-  if (add->peer.name) xfree(add->peer.name);
-  if (add->paddr) xfree(add->paddr);
-}
+  admin_resop *r = v;
+
+  T( trace(T_ADMIN, "admin: resop %s resolved", BGTAG(r)); )
+  TIMER;
+  if (!h) {
+    a_bgfail(&r->bg, "resolve-error %s", r->addr);
+    r->func(r, ARES_FAIL);
+  } else {
+    memcpy(&r->sa.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
+    r->func(r, ARES_OK);
+  }
+  sel_rmtimer(&r->t);
+  xfree(r->addr);
+  a_bgrelease(&r->bg);
+} 
 
-/* --- @a_addcancel@ --- *
+/* --- @a_restimer@ --- *
  *
- * Arguments:  @admin_bgop *bg@ = background operation
+ * Arguments:  @struct timeval *tv@ = timer
+ *             @void *v@ = pointer to resolver operation
  *
  * Returns:    ---
  *
- * Use:                Cancels an add operation.
+ * Use:                Times out a resolver.
  */
 
-static void a_addcancel(admin_bgop *bg)
+static void a_restimer(struct timeval *tv, void *v)
 {
-  admin_addop *add = (admin_addop *)bg;
-
-  T( trace(T_ADMIN, "admin: cancel add op %s", BGTAG(add)); )
-  sel_rmtimer(&add->t);
-  bres_abort(&add->r);
-  a_addfree(add);
+  admin_resop *r = v;
+
+  T( trace(T_ADMIN, "admin: resop %s timeout", BGTAG(r)); )
+  a_bgfail(&r->bg, "resolver-timeout %s\n", r->addr);
+  r->func(r, ARES_FAIL);
+  bres_abort(&r->r);
+  xfree(r->addr);
+  a_bgrelease(&r->bg);
 }
 
-/* --- @a_doadd@ --- *
+/* --- @a_rescancel@ --- *
  *
- * Arguments:  @admin_addop *add@ = operation block
+ * Arguments:  @admin_bgop *bg@ = background operation
  *
  * Returns:    ---
  *
- * Use:                Does the peer add thing.
+ * Use:                Cancels an add operation.
  */
 
-static void a_doadd(admin_addop *add)
+static void a_rescancel(admin_bgop *bg)
 {
-  if (p_find(add->peer.name))
-    a_bgfail(&add->bg, "peer-exists %s", add->peer.name);
-  else if (!p_create(&add->peer))
-    a_bgfail(&add->bg, "peer-create-fail %s", add->peer.name);
-  else
-    a_bgok(&add->bg);
+  admin_resop *r = (admin_resop *)bg;
+
+  T( trace(T_ADMIN, "admin: cancel resop %s", BGTAG(r)); )
+  r->func(r, ARES_FAIL);
+  sel_rmtimer(&r->t);
+  xfree(r->addr);
+  bres_abort(&r->r);
 }
-/* --- @a_addresolve@ --- *
+
+/* --- @a_resolve@ --- *
  *
- * Arguments:  @struct hostent *h@ = pointer to resolved hostname
- *             @void *v@ = pointer to add operation
+ * Arguments:  @admin *a@ = administration connection
+ *             @admin_resop *r@ = resolver operation to run
+ *             @const char *tag@ = background operation tag
+ *             @void (*func)(struct admin_resop *, int@ = handler function
+ *             @unsigned ac@ = number of remaining arguments
+ *             @char *av[]@ = pointer to remaining arguments
  *
  * Returns:    ---
  *
- * Use:                Handles a completed name resolution.
+ * Use:                Cranks up a resolver job.
  */
 
-static void a_addresolve(struct hostent *h, void *v)
+static void a_resolve(admin *a, admin_resop *r, const char *tag,
+                     void (*func)(struct admin_resop *, int),
+                     unsigned ac, char *av[])
 {
-  admin_addop *add = v;
+  struct timeval tv;
+  unsigned long pt;
+  char *p;
+  int i = 0;
 
-  T( trace(T_ADMIN, "admin: add op %s resolved", BGTAG(add)); )
-  TIMER;
-  if (!h)
-    a_bgfail(&add->bg, "resolve-error %s", add->paddr);
-  else {
-    memcpy(&add->peer.sa.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
-    a_doadd(add);
+  /* --- Fill in the easy bits of address --- */
+
+  r->addr = 0;
+  r->func = func;
+  if (mystrieq(av[i], "inet")) i++;
+  if (ac - i != 2) {
+    a_fail(a, "bad-addr-syntax [inet] ADDRESS PORT");
+    goto fail;
+  }
+  r->sa.sin.sin_family = AF_INET;
+  r->sasz = sizeof(r->sa.sin);
+  r->addr = xstrdup(av[i]);
+  pt = strtoul(av[i + 1], &p, 0);
+  if (*p) {
+    struct servent *s = getservbyname(av[i + 1], "udp");
+    if (!s) {
+      a_fail(a, "unknown-service %s", av[i + 1]);
+      goto fail;
+    }
+    pt = ntohs(s->s_port);
   }
-  sel_rmtimer(&add->t);
-  a_addfree(add);
-  a_bgrelease(&add->bg);
+  if (pt == 0 || pt >= 65536) {
+    a_fail(a, "invalid-port %lu", pt);
+    goto fail;
+  }
+  r->sa.sin.sin_port = htons(pt);
+
+  /* --- Report backgrounding --- *
+   *
+   * Do this for consistency of interface, even if we're going to get the
+   * answer straight away.
+   */
+
+  a_bgadd(a, &r->bg, tag, a_rescancel);
+  T( trace(T_ADMIN, "admin: %u, resop %s, hostname `%s'",
+          a->seq, BGTAG(r), r->addr); )
+
+  /* --- If the name is numeric, do it the easy way --- */
+  
+  if (inet_aton(av[i], &r->sa.sin.sin_addr)) {
+    T( trace(T_ADMIN, "admin: resop %s done the easy way", BGTAG(r)); )
+    func(r, ARES_OK);
+    xfree(r->addr);
+    a_bgrelease(&r->bg);
+    return;
+  }
+
+  /* --- Store everything for later and crank up the resolver --- */
+
+  gettimeofday(&tv, 0);
+  tv.tv_sec += T_RESOLVE;
+  sel_addtimer(&sel, &r->t, &tv, a_restimer, r);
+  bres_byname(&r->r, r->addr, a_resolved, r);
+  return;
+
+fail:
+  func(r, ARES_FAIL);
+  if (r->addr) xfree(r->addr);
+  xfree(r);
 }
 
-/* --- @a_addtimer@ --- *
+/*----- Adding peers ------------------------------------------------------*/
+
+/* --- @a_doadd@ --- *
  *
- * Arguments:  @struct timeval *tv@ = timer
- *             @void *v@ = pointer to add operation
+ * Arguments:  @admin_resop *r@ = resolver operation
+ *             @int rc@ = how it worked
  *
  * Returns:    ---
  *
- * Use:                Times out a resolver.
+ * Use:                Handles a completed resolution.
  */
 
-static void a_addtimer(struct timeval *tv, void *v)
+static void a_doadd(admin_resop *r, int rc)
 {
-  admin_addop *add = v;
+  admin_addop *add = (admin_addop *)r;
+
+  T( trace(T_ADMIN, "admin: done add op %s", BGTAG(add)); )
+
+  if (rc == ARES_OK) {
+    add->peer.sasz = add->r.sasz;
+    add->peer.sa = add->r.sa;
+    if (p_find(add->peer.name))
+      a_bgfail(&add->r.bg, "peer-exists %s", add->peer.name);
+    else if (!p_create(&add->peer))
+      a_bgfail(&add->r.bg, "peer-create-fail %s", add->peer.name);
+    else
+      a_bgok(&add->r.bg);
+  }
 
-  T( trace(T_ADMIN, "admin: add op %s timeout", BGTAG(add)); )
-  a_bgfail(&add->bg, "resolver-timeout %s\n", add->paddr);
-  bres_abort(&add->r);
-  a_addfree(add);
-  a_bgrelease(&add->bg);
+  xfree(add->peer.name);
 }
 
 /* --- @acmd_add@ --- *
@@ -721,27 +825,23 @@ static void a_addtimer(struct timeval *tv, void *v)
 
 static void acmd_add(admin *a, unsigned ac, char *av[])
 {
-  unsigned long pt;
-  struct timeval tv;
   unsigned i, j;
-  char *p;
   const char *tag = 0;
   admin_addop *add = 0;
 
-  /* --- Make sure someone's not got there already --- */
-
-  if (p_find(av[0])) {
-    a_fail(a, "peer-exists %s", av[0]);
-    goto fail;
-  }
-
   /* --- Set stuff up --- */
 
   add = xmalloc(sizeof(*add));
   add->peer.name = xstrdup(av[0]);
   add->peer.t_ka = 0;
   add->peer.tops = tun_default;
-  add->paddr = 0;
+
+  /* --- Make sure someone's not got there already --- */
+
+  if (p_find(av[0])) {
+    a_fail(a, "peer-exists %s", av[0]);
+    goto fail;
+  }
 
   /* --- Parse options --- */
 
@@ -757,7 +857,7 @@ static void acmd_add(admin *a, unsigned ac, char *av[])
       for (j = 0;; j++) {
        if (!tunnels[j]) {
          a_fail(a, "unknown-tunnel %s", av[i]);
-         return;
+         goto fail;
        }
        if (mystrieq(av[i], tunnels[j]->name)) {
          add->peer.tops = tunnels[j];
@@ -769,7 +869,7 @@ static void acmd_add(admin *a, unsigned ac, char *av[])
       if (!av[++i]) goto bad_syntax;
       if ((t = a_parsetime(av[i])) < 0) {
        a_fail(a, "bad-time-spec %s", av[i]);
-       return;
+       goto fail;
       }
       add->peer.t_ka = t;
     } else if (mystrieq(av[i], "--")) {
@@ -780,66 +880,18 @@ static void acmd_add(admin *a, unsigned ac, char *av[])
     i++;
   }
 
-  /* --- Fill in the easy bits of address --- */
-
-  if (mystrieq(av[i], "inet")) i++;
-  if (ac - i != 2) {
-    a_fail(a, "bad-syntax -- add PEER [OPTIONS] [inet] ADDRESS PORT");
-    goto fail;
-  }
-  add->peer.sa.sin.sin_family = AF_INET;
-  add->peer.sasz = sizeof(add->peer.sa.sin);
-  add->paddr = xstrdup(av[i]);
-  pt = strtoul(av[i + 1], &p, 0);
-  if (*p) {
-    struct servent *s = getservbyname(av[i + 1], "udp");
-    if (!s) {
-      a_fail(a, "unknown-service %s", av[i + 1]);
-      goto fail;
-    }
-    pt = ntohs(s->s_port);
-  }
-  if (pt == 0 || pt >= 65536) {
-    a_fail(a, "invalid-port %lu", pt);
-    goto fail;
-  }
-  add->peer.sa.sin.sin_port = htons(pt);
-
-  /* --- Report backgrounding --- *
-   *
-   * Do this for consistency of interface, even if we're going to get the
-   * answer straight away.
-   */
+  /* --- Crank up the resolver --- */
 
-  a_bgadd(a, &add->bg, tag, a_addcancel);
-  T( trace(T_ADMIN, "admin: %u, add op %s resolving hostname `%s'",
-          a->seq, BGTAG(add), add->paddr); )
-
-  /* --- If the name is numeric, do it the easy way --- */
-  
-  if (inet_aton(av[i], &add->peer.sa.sin.sin_addr)) {
-    T( trace(T_ADMIN, "admin: add op %s done the easy way", BGTAG(add)); )
-    a_doadd(add);
-    a_addfree(add);
-    a_bgrelease(&add->bg);
-    return;
-  }
-
-  /* --- Store everything for later and crank up the resolver --- */
-
-  gettimeofday(&tv, 0);
-  tv.tv_sec += T_RESOLVE;
-  sel_addtimer(&sel, &add->t, &tv, a_addtimer, add);
-  bres_byname(&add->r, add->paddr, a_addresolve, add);
+  a_resolve(a, &add->r, tag, a_doadd, ac - i, av + i);
   return;
 
+  /* --- Clearing up --- */
+
 bad_syntax:
   a_fail(a, "bad-syntax -- add PEER [OPTIONS] ADDR ...");
 fail:
-  if (add) {
-    a_addfree(add);
-    xfree(add);
-  }
+  xfree(add->peer.name);
+  xfree(add);
   return;
 }
 
@@ -942,10 +994,8 @@ static void a_ping(admin *a, unsigned ac, char *av[],
   }
 
   if (!av[i]) goto bad_syntax;
-  if ((p = p_find(av[i])) == 0) {
-    a_fail(a, "unknown-peer %s", av[i]);
+  if ((p = a_findpeer(a, av[i])) == 0)
     return;
-  }
   pg = xmalloc(sizeof(*pg));
   gettimeofday(&pg->pingtime, 0);
   a_bgadd(a, &pg->bg, tag, a_pingcancel);
@@ -1115,22 +1165,61 @@ static void acmd_ifname(admin *a, unsigned ac, char *av[])
 {
   peer *p;
 
-  if ((p = p_find(av[0])) == 0)
-    a_fail(a, "unknown-peer %s", av[0]);
-  else {
+  if ((p = a_findpeer(a, av[0])) != 0) {
     a_info(a, "%s", p_ifname(p));
     a_ok(a);
   }
 }
 
+static void acmd_getchal(admin *a, unsigned ac, char *av[])
+{
+  buf b;
+
+  buf_init(&b, buf_i, PKBUFSZ);
+  c_new(&b);
+  a_info(a, "%s", b64_encode(BBASE(&b), BLEN(&b)));
+  a_ok(a);
+}
+
+static void acmd_checkchal(admin *a, unsigned ac, char *av[])
+{
+  base64_ctx b64;
+  buf b;
+  dstr d = DSTR_INIT;
+
+  base64_init(&b64);
+  base64_decode(&b64, av[0], strlen(av[0]), &d);
+  base64_decode(&b64, 0, 0, &d);
+  buf_init(&b, d.buf, d.len);
+  if (c_check(&b) || BBAD(&b) || BLEFT(&b))
+    a_fail(a, "invalid-challenge");
+  else
+    a_ok(a);
+  dstr_destroy(&d);
+}
+
+static void acmd_greet(admin *a, unsigned ac, char *av[])
+{
+  peer *p;
+  base64_ctx b64;
+  dstr d = DSTR_INIT;
+
+  if ((p = a_findpeer(a, av[0])) != 0) {
+    base64_init(&b64);
+    base64_decode(&b64, av[1], strlen(av[1]), &d);
+    base64_decode(&b64, 0, 0, &d);
+    p_greet(p, d.buf, d.len);
+    dstr_destroy(&d);
+    a_ok(a);
+  }
+}
+
 static void acmd_addr(admin *a, unsigned ac, char *av[])
 {
   peer *p;
   const addr *ad;
 
-  if ((p = p_find(av[0])) == 0)
-    a_fail(a, "unknown-peer %s", av[0]);
-  else {
+  if ((p = a_findpeer(a, av[0])) != 0) {
     ad = p_addr(p);
     assert(ad->sa.sa_family == AF_INET);
     a_info(a, "INET %s %u",
@@ -1145,15 +1234,12 @@ static void acmd_peerinfo(admin *a, unsigned ac, char *av[])
   peer *p;
   const peerspec *ps;
 
-  if ((p = p_find(av[0])) == 0) {
-    a_fail(a, "unknown-peer %s", av[0]);
-    return;
+  if ((p = a_findpeer(a, av[0])) != 0) {
+    ps = p_spec(p);
+    a_info(a, "tunnel=%s", ps->tops->name);
+    a_info(a, "keepalive=%lu", ps->t_ka);
+    a_ok(a);
   }
-
-  ps = p_spec(p);
-  a_info(a, "tunnel=%s", ps->tops->name);
-  a_info(a, "keepalive=%lu", ps->t_ka);
-  a_ok(a);
 }
 
 static void acmd_servinfo(admin *a, unsigned ac, char *av[])
@@ -1169,10 +1255,8 @@ static void acmd_stats(admin *a, unsigned ac, char *av[])
   peer *p;
   stats *st;
 
-  if ((p = p_find(av[0])) == 0) {
-    a_fail(a, "unknown-peer %s", av[0]);
+  if ((p = a_findpeer(a, av[0])) == 0)
     return;
-  }
 
   st = p_stats(p);
   a_info(a, "start-time=%s", timestr(st->t_start));
@@ -1196,9 +1280,7 @@ static void acmd_stats(admin *a, unsigned ac, char *av[])
 static void acmd_kill(admin *a, unsigned ac, char *av[])
 {
   peer *p;
-  if ((p = p_find(av[0])) == 0)
-    a_fail(a, "unknown-peer %s", av[0]);
-  else {
+  if ((p = a_findpeer(a, av[0])) != 0) {
     p_destroy(p);
     a_ok(a);
   }
@@ -1207,9 +1289,7 @@ static void acmd_kill(admin *a, unsigned ac, char *av[])
 static void acmd_forcekx(admin *a, unsigned ac, char *av[])
 {
   peer *p;
-  if ((p = p_find(av[0])) == 0)
-    a_fail(a, "unknown-peer %s", av[0]);
-  else {
+  if ((p = a_findpeer(a, av[0])) != 0) {
     kx_start(&p->kx, 1);
     a_ok(a);
   }
@@ -1255,9 +1335,12 @@ static const acmd acmdtab[] = {
   { "add",     "add PEER [OPTIONS] ADDR ...",
                                        2,      0xffff, acmd_add },
   { "addr",    "addr PEER",            1,      1,      acmd_addr },
+  { "checkchal", "checkchal CHAL",     1,      1,      acmd_checkchal },
   { "daemon",  "daemon",               0,      0,      acmd_daemon },
   { "eping",   "eping [OPTIONS] PEER", 1,      0xffff, acmd_eping },
   { "forcekx", "forcekx PEER",         1,      1,      acmd_forcekx },
+  { "getchal", "getchal",              0,      0,      acmd_getchal },
+  { "greet",   "greet PEER CHAL",      2,      2,      acmd_greet },
   { "help",    "help",                 0,      0,      acmd_help },
   { "ifname",  "ifname PEER",          1,      1,      acmd_ifname },
   { "kill",    "kill PEER",            1,      1,      acmd_kill },