*
* %$\cookie{kx-switch-ok}, E_K(u_A))$%
* Switch received. Committed; send data; move to @KXS_SWITCH@.
+ *
+ * %$\cookie{kx-token-request}, u, E_L(n)$%
+ * %$L = H(u, u^\alpha)$%, and %$n$% is a string of the form
+ * `[PEER.]KEYTAG'. Expect %$\cookie{kx-token}$% by return.
+ *
+ * %$\cookie{kx-token}, v, E_{L'}(t)$%
+ * %$L' = H(v, v^\alpha)$%, and %$t$% is a token associated with %$n$%
+ * (see %$\cookie{kx-token-request}$% above).
+ *
+ * %$\cookie{kx-knock}, u, E_L(n, t), r_A$%
+ * %$L$%, %$n$% and %$t$% are as %$\cookie{kx-token}$% and
+ * %$\cookie{kx-token-request}$%; %$r_A$% is as in
+ * %$\cookie{kx-pre-challenge}$%. If the token %$t$% doesn't match
+ * %$n$%, then warn and discard. If a peer named PEER (or KEYTAG)
+ * exists then proceed as for %$\cookie{kx-pre-challenge}$%. Otherwise
+ * issue a notification `NOTE KNOCK PEER ADDR...' and discard.
*/
/*----- Static tables -----------------------------------------------------*/
static const char *const pkname[] = {
- "pre-challenge", "challenge", "reply", "switch-rq", "switch-ok"
+ "pre-challenge", "challenge", "reply", "switch-rq", "switch-ok",
+ "token-rq", "token", "knock"
};
/*----- Various utilities -------------------------------------------------*/
/*----- Individual message handlers ---------------------------------------*/
+static ratelim unauth_limit;
+
+/* --- @dotokenrq@ --- *
+ *
+ * Arguments: @const addr *a@ = sender's address
+ * @buf *b@ = buffer containing the packet
+ *
+ * Returns: ---
+ *
+ * Use: Processes a token-request message.
+ */
+
+static void dotokenrq(const addr *a, buf *b)
+{
+ uint32 id;
+ kdata *kpriv = 0, *kpub = 0;
+ char *pname;
+ const char *tag;
+ size_t sz;
+ buf bb, bbb;
+
+ /* --- Check if we're in danger of overloading --- */
+
+ if (ratelim_withdraw(&unauth_limit, 1)) goto done;
+
+ /* --- Start building the reply --- */
+
+ buf_init(&bbb, buf_o, sizeof(buf_o));
+ buf_putu8(&bbb, MSG_KEYEXCH | KX_TOKEN);
+
+ /* --- Fetch and copy the challenge string --- */
+
+ if (buf_getbuf16(b, &bb)) goto done;
+ buf_putmem16(&bbb, BBASE(&bb), BSZ(&bb));
+
+ /* --- Make our own challenge for the response --- */
+
+ buf_init(&bb, buf_t, sizeof(buf_t));
+ c_new(0, 0, &bb); assert(BOK(&bb)); buf_putbuf16(&bbb, &bb);
+
+ /* --- Figure out which private key I'm supposed to use --- */
+
+ if (buf_getu32(b, &id)) goto done;
+ if ((kpriv = km_findprivbyid(id)) == 0) goto done;
+
+ /* --- Decrypt the message --- */
+
+ buf_init(&bb, buf_t, sizeof(buf_t));
+ if (ies_decrypt(kpriv, MSG_KEYEXCH | KX_TOKENRQ, b, &bb) || BLEFT(b))
+ goto done;
+
+ /* --- Parse the token request and find the sender's public key --- */
+
+ assert(BOK(&bb)); buf_flip(&bb);
+ if ((pname = buf_getmem16(&bb, &sz)) == 0 || memchr(pname, 0, sz))
+ goto done;
+ assert(sz < sizeof(buf_t) - ((const octet *)pname - buf_t));
+ pname[sz] = 0;
+ if ((tag = strchr(pname, '.')) != 0) tag++;
+ else tag = pname;
+ if ((kpub = km_findpub(tag)) == 0) goto done;
+
+ /* --- Build and encrypt the token --- */
+
+ buf_init(&bb, buf_i, sizeof(buf_i));
+ c_new(pname, sz, &bb);
+ assert(BOK(&bb)); buf_flip(&bb);
+ if (ies_encrypt(kpub, MSG_KEYEXCH | KX_TOKEN, &bb, &bbb)) goto done;
+ assert(BOK(&bbb));
+
+ /* --- Send the response -- or at least give it a try --- */
+
+ p_txaddr(a, BBASE(&bbb), BLEN(&bbb));
+
+ /* --- All done --- */
+
+done:
+ if (kpriv) km_unref(kpriv);
+ if (kpub) km_unref(kpub);
+}
+
+/* --- @dotoken@ --- *
+ *
+ * Arguments: @keyexch *kx@ = pointer to key exchange block
+ * @buf *b@ = buffer containing the packet
+ *
+ * Returns: Zero if OK, nonzero of the packet was rejected.
+ *
+ * Use: Processes a token message.
+ */
+
+static int dotoken(keyexch *kx, buf *b)
+{
+ buf bb;
+ buf *bbb;
+ const dhgrp *g = kx->kpriv->grp;
+ octet *p;
+ size_t sz;
+
+ /* --- Make sure this is a sensible message to have received --- */
+
+ if (!kx->p->spec.knock) return (-1);
+
+ /* --- First, collect and verify our challenge --- */
+
+ if (buf_getbuf16(b, &bb) || c_check(0, 0, &bb) || BLEFT(&bb)) return (-1);
+
+ /* --- Start building the knock message from here --- */
+
+ bbb = p_txstart(kx->p, MSG_KEYEXCH | KX_KNOCK);
+
+ /* --- Copy the peer's challenge --- */
+
+ if (buf_getbuf16(b, &bb)) return (-1);
+ buf_putmem16(bbb, BBASE(&bb), BSZ(&bb));
+
+ /* --- Add the key indicator --- */
+
+ buf_putu32(bbb, kx->kpub->id);
+
+ /* --- Building the knock payload --- */
+
+ buf_init(&bb, buf_t, sizeof(buf_t));
+ buf_putstr16(&bb, kx->p->spec.knock);
+ sz = BLEN(&bb)%64; if (sz) sz = 64 - sz;
+ if (ies_decrypt(kx->kpriv, MSG_KEYEXCH | KX_TOKEN, b, &bb)) return (-1);
+ p = buf_get(&bb, sz); assert(p); memset(p, 0, sz);
+ assert(BOK(&bb)); buf_flip(&bb);
+ if (ies_encrypt(kx->kpub, MSG_KEYEXCH | KX_KNOCK, &bb, bbb)) return (-1);
+
+ /* --- Finally, the pre-challenge group element --- */
+
+ g->ops->stge(g, bbb, kx->C, DHFMT_VAR);
+
+ /* --- And we're done --- */
+
+ if (BBAD(bbb)) return (-1);
+ update_stats_tx(kx, BLEN(bbb));
+ p_txend(kx->p);
+ return (0);
+}
+
/* --- @doprechallenge@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange block
return (-1);
}
+/* --- @doknock@ --- *
+ *
+ * Arguments: @const addr *a@ = sender's address
+ * @buf *b@ = buffer containing the packet
+ *
+ * Returns: ---
+ *
+ * Use: Processes a knock message.
+ */
+
+static void doknock(const addr *a, buf *b)
+{
+ keyexch *kx;
+ peer *p;
+ uint32 id;
+ kdata *kpriv = 0;
+ char *pname;
+ size_t sz, msgsz = BLEN(b);
+ buf bb;
+ int rc;
+
+ /* --- Read and check the challenge --- */
+
+ buf_getbuf16(b, &bb);
+ if (c_check(0, 0, &bb)) goto done;
+
+ /* --- Figure out which private key I'm supposed to use --- */
+
+ if (buf_getu32(b, &id)) goto done;
+ if ((kpriv = km_findprivbyid(id)) == 0) goto done;
+
+ /* --- Decrypt and check the peer's name against the token --- */
+
+ buf_init(&bb, buf_t, sizeof(buf_t));
+ if (ies_decrypt(kpriv, MSG_KEYEXCH | KX_KNOCK, b, &bb)) goto done;
+ assert(BOK(&bb)); buf_flip(&bb);
+ if ((pname = buf_getmem16(&bb, &sz)) == 0 ||
+ memchr(pname, 0, sz) ||
+ c_check(pname, sz, &bb))
+ goto done;
+ assert(sz < sizeof(buf_t) - ((const octet *)pname - buf_t));
+ pname[sz] = 0;
+
+ /* --- If we can't find the peer, then issue a notification --- */
+
+ if ((p = p_find(pname)) == 0) {
+ a_notify("KNOCK", "%s", pname, "?ADDR", a, A_END);
+ goto done;
+ }
+
+ /* --- Update the peer's address --- */
+
+ kx = &p->kx;
+ p_updateaddr(kx->p, a);
+
+ /* --- Now treat the remainder of the message as a pre-challenge --- */
+
+ notice_message(kx);
+ rc = doprechallenge(kx, b);
+ update_stats_rx(kx, !rc, msgsz);
+
+ /* --- All done: clean up --- */
+
+done:
+ if (kpriv) km_unref(kpriv);
+}
+
/* --- @respond@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange block
buf bb;
struct timeval tv;
const dhgrp *g = kx->kpriv->grp;
+ octet *p;
+ size_t sz;
buf *b;
switch (kx->s) {
case KXS_CHAL:
- T( trace(T_KEYEXCH, "keyexch: sending prechallenge to `%s'",
- p_name(kx->p)); )
- b = p_txstart(kx->p, MSG_KEYEXCH | KX_PRECHAL);
- g->ops->stge(g, b, kx->C, DHFMT_VAR);
+ if (!kx->p->spec.knock) {
+ T( trace(T_KEYEXCH, "keyexch: sending prechallenge to `%s'",
+ p_name(kx->p)); )
+ b = p_txstart(kx->p, MSG_KEYEXCH | KX_PRECHAL);
+ g->ops->stge(g, b, kx->C, DHFMT_VAR);
+ } else {
+ T( trace(T_KEYEXCH, "keyexch: sending token-request to `%s'",
+ p_name(kx->p)); )
+ b = p_txstart(kx->p, MSG_KEYEXCH | KX_TOKENRQ);
+
+ buf_init(&bb, buf_t, sizeof(buf_t));
+ c_new(0, 0, &bb); assert(BOK(&bb)); buf_putbuf16(b, &bb);
+
+ buf_putu32(b, kx->kpub->id);
+
+ buf_init(&bb, buf_t, sizeof(buf_t));
+ buf_putstr16(&bb, kx->p->spec.knock);
+ sz = BLEN(&bb)%64; if (sz) sz = 64 - sz;
+ p = buf_get(&bb, sz); assert(p); memset(p, 0, sz);
+ assert(BOK(&bb)); buf_flip(&bb);
+ if (ies_encrypt(kx->kpub, MSG_KEYEXCH | KX_TOKENRQ, &bb, b))
+ buf_break(b);
+ }
break;
case KXS_COMMIT:
T( trace(T_KEYEXCH, "keyexch: sending switch request to `%s'",
/* --- @kx_message@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange context
+ * @const addr *a@ = sender's IP address and port
* @unsigned msg@ = the message code
* @buf *b@ = pointer to buffer containing the packet
*
- * Returns: ---
+ * Returns: Nonzero if the sender's address was unknown.
*
* Use: Reads a packet containing key exchange messages and handles
* it.
*/
-void kx_message(keyexch *kx, unsigned msg, buf *b)
+int kx_message(keyexch *kx, const addr *a, unsigned msg, buf *b)
{
size_t sz = BSZ(b);
int rc;
- if (notice_message(kx)) return;
+ T( trace(T_KEYEXCH, "keyexch: processing %s packet from %c%s%c",
+ msg < KX_NMSG ? pkname[msg] : "unknown",
+ kx ? '`' : '<', kx ? p_name(kx->p) : "nil", kx ? '\'' : '>'); )
+
+ switch (msg) {
+ case KX_TOKENRQ: dotokenrq(a, b); return (0);
+ case KX_KNOCK: doknock(a, b); return (0);
+ }
- T( trace(T_KEYEXCH, "keyexch: processing %s packet from `%s'",
- msg < KX_NMSG ? pkname[msg] : "unknown", p_name(kx->p)); )
+ if (!kx) return (-1);
+ if (notice_message(kx)) return (0);
switch (msg) {
- case KX_PRECHAL:
- rc = doprechallenge(kx, b);
- break;
- case KX_CHAL:
- rc = dochallenge(kx, b);
- break;
- case KX_REPLY:
- rc = doreply(kx, b);
- break;
- case KX_SWITCH:
- rc = doswitch(kx, b);
- break;
- case KX_SWITCHOK:
- rc = doswitchok(kx, b);
- break;
+ case KX_TOKEN: rc = dotoken(kx, b); break;
+ case KX_PRECHAL: rc = doprechallenge(kx, b); break;
+ case KX_CHAL: rc = dochallenge(kx, b); break;
+ case KX_REPLY: rc = doreply(kx, b); break;
+ case KX_SWITCH: rc = doswitch(kx, b); break;
+ case KX_SWITCHOK: rc = doswitchok(kx, b); break;
default:
a_warn("KX", "?PEER", kx->p, "unknown-message", "0x%02x", msg, A_END);
rc = -1;
}
update_stats_rx(kx, !rc, sz);
+ return (0);
}
/* --- @kx_free@ --- *
return (-1);
}
+/* --- @kx_init@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Initializes the key-exchange logic.
+ */
+
+void kx_init(void)
+ { ratelim_init(&unauth_limit, 20, 500); }
+
/*----- That's all, folks -------------------------------------------------*/