X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/blobdiff_plain/35c8b547dde529693875087d67fa60bf88319d2b..8187950bc47575533beff8a9492c1c2f5e08cba3:/server/keyexch.c diff --git a/server/keyexch.c b/server/keyexch.c index 1527a297..d26ac787 100644 --- a/server/keyexch.c +++ b/server/keyexch.c @@ -75,13 +75,6 @@ * Switch received. Committed; send data; move to @KXS_SWITCH@. */ -/*----- Tunable parameters ------------------------------------------------*/ - -#define T_VALID SEC(20) /* Challenge validity period */ -#define T_RETRY SEC(10) /* Challenge retransmit interval */ - -#define VALIDP(kx, now) ((now) < (kx)->t_valid) - /*----- Static tables -----------------------------------------------------*/ static const char *const pkname[] = { @@ -90,6 +83,17 @@ static const char *const pkname[] = { /*----- Various utilities -------------------------------------------------*/ +/* --- @VALIDP@ --- * + * + * Arguments: @const keyexch *kx@ = key exchange state + * @time_t now@ = current time in seconds + * + * Returns: Whether the challenge in the key-exchange state is still + * valid or should be regenerated. + */ + +#define VALIDP(kx, now) ((now) < (kx)->t_valid) + /* --- @hashge@ --- * * * Arguments: @ghash *h@ = pointer to hash context @@ -269,24 +273,102 @@ static void timer(struct timeval *tv, void *v) /* --- @settimer@ --- * * * Arguments: @keyexch *kx@ = pointer to key exchange context - * @time_t t@ = when to set the timer for + * @struct timeval *tv@ = when to set the timer for * * Returns: --- * * Use: Sets the timer for the next key exchange attempt. */ -static void settimer(keyexch *kx, time_t t) +static void settimer(keyexch *kx, struct timeval *tv) { - struct timeval tv; - if (kx->f & KXF_TIMER) - sel_rmtimer(&kx->t); - tv.tv_sec = t; - tv.tv_usec = 0; - sel_addtimer(&sel, &kx->t, &tv, timer, kx); + if (kx->f & KXF_TIMER) sel_rmtimer(&kx->t); + sel_addtimer(&sel, &kx->t, tv, timer, kx); kx->f |= KXF_TIMER; } +/* --- @f2tv@ --- * + * + * Arguments: @struct timeval *tv@ = where to write the timeval + * @double t@ = a time as a floating point number + * + * Returns: --- + * + * Use: Converts a floating-point time into a timeval. + */ + +static void f2tv(struct timeval *tv, double t) +{ + tv->tv_sec = t; + tv->tv_usec = (t - tv->tv_sec)*MILLION; +} + +/* --- @wobble@ --- * + * + * Arguments: @double t@ = a time interval + * + * Returns: The same time interval, with a random error applied. + */ + +static double wobble(double t) +{ + uint32 r = rand_global.ops->word(&rand_global); + double w = (r/F_2P32) - 0.5; + return (t + t*w*T_WOBBLE); +} + +/* --- @rs_time@ --- * + * + * Arguments: @retry *rs@ = current retry state + * @struct timeval *tv@ = where to write the result + * @const struct timeval *now@ = current time, or null + * + * Returns: --- + * + * Use: Computes a time at which to retry sending a key-exchange + * packet. This algorithm is subject to change, but it's + * currently a capped exponential backoff, slightly randomized + * to try to keep clients from hammering a server that's only + * just woken up. + * + * If @now@ is null then the function works out the time for + * itself. + */ + +static void rs_time(retry *rs, struct timeval *tv, const struct timeval *now) +{ + double t; + struct timeval rtv; + + if (!rs->t) + t = SEC(2); + else { + t = (rs->t * 5)/4; + if (t > MIN(5)) t = MIN(5); + } + rs->t = t; + + if (!now) { + now = tv; + gettimeofday(tv, 0); + } + f2tv(&rtv, wobble(t)); + TV_ADD(tv, now, &rtv); +} + +/* --- @retry_reset@ --- * + * + * Arguments: @retry *rs@ = retry state + * + * Returns: -- + * + * Use: Resets a retry state to indicate that progress has been + * made. Also useful for initializing the state in the first + * place. + */ + +static void rs_reset(retry *rs) { rs->t = 0; } + /*----- Challenge management ----------------------------------------------*/ /* --- Notes on challenge management --- * @@ -371,6 +453,7 @@ static kxchal *kxc_new(keyexch *kx) kxc->kx = kx; kxc->f = 0; kx->r[i] = kxc; + rs_reset(&kxc->rs); return (kxc); } @@ -465,7 +548,7 @@ static void kxc_answer(keyexch *kx, kxchal *kxc) if (kxc->f & KXF_TIMER) sel_rmtimer(&kxc->t); gettimeofday(&tv, 0); - tv.tv_sec += T_RETRY; + rs_time(&kxc->rs, &tv, &tv); sel_addtimer(&sel, &kxc->t, &tv, kxc_timer, kxc); kxc->f |= KXF_TIMER; } @@ -734,6 +817,7 @@ static void resend(keyexch *kx) kxchal *kxc; buf bb; stats *st = p_stats(kx->p); + struct timeval tv; buf *b; switch (kx->s) { @@ -776,8 +860,10 @@ static void resend(keyexch *kx) p_txend(kx->p); } - if (kx->s < KXS_SWITCH) - settimer(kx, time(0) + T_RETRY); + if (kx->s < KXS_SWITCH) { + rs_time(&kx->rs, &tv, 0); + settimer(kx, &tv); + } } /* --- @decryptrest@ --- * @@ -919,8 +1005,13 @@ bad: static void kxfinish(keyexch *kx) { kxchal *kxc = kx->r[0]; + struct timeval now, tv; + ks_activate(kxc->ks); - settimer(kx, ks_tregen(kxc->ks)); + gettimeofday(&now, 0); + f2tv(&tv, wobble(T_REGEN)); + TV_ADD(&tv, &now, &tv); + settimer(kx, &tv); kx->s = KXS_SWITCH; a_notify("KXDONE", "?PEER", kx->p, A_END); p_stats(kx->p)->t_kx = time(0); @@ -1176,23 +1267,26 @@ void kx_start(keyexch *kx, int forcep) void kx_message(keyexch *kx, unsigned msg, buf *b) { - time_t now = time(0); + struct timeval now, tv; stats *st = p_stats(kx->p); size_t sz = BSZ(b); int rc; + gettimeofday(&now, 0); + rs_reset(&kx->rs); if (kx->f & KXF_CORK) { - start(kx, now); - settimer(kx, now + T_RETRY); + start(kx, now.tv_sec); + rs_time(&kx->rs, &tv, &now); + settimer(kx, &tv); a_notify("KXSTART", A_END); } if (checkpub(kx)) return; - if (!VALIDP(kx, now)) { + if (!VALIDP(kx, now.tv_sec)) { stop(kx); - start(kx, now); + start(kx, now.tv_sec); } T( trace(T_KEYEXCH, "keyexch: processing %s packet from `%s'", msg < KX_NMSG ? pkname[msg] : "unknown", p_name(kx->p)); ) @@ -1383,11 +1477,11 @@ newkeys: int kx_init(keyexch *kx, peer *p, keyset **ks, unsigned f) { - if ((kx->kpriv = km_findpriv(tag_priv)) == 0) goto fail_0; + if ((kx->kpriv = km_findpriv(p_privtag(p))) == 0) goto fail_0; if ((kx->kpub = km_findpub(p_tag(p))) == 0) goto fail_1; - if (!km_samealgsp(kx->kpriv, kx->kpub)) { - a_warn("KX", "?PEER", kx->p, "algorithms-mismatch", - "local-private-key", "%s", tag_priv, + if (!group_samep(kx->kpriv->g, kx->kpub->g)) { + a_warn("KX", "?PEER", kx->p, "group-mismatch", + "local-private-key", "%s", p_privtag(p), "peer-public-key", "%s", p_tag(p), A_END); goto fail_2; @@ -1396,6 +1490,7 @@ int kx_init(keyexch *kx, peer *p, keyset **ks, unsigned f) kx->ks = ks; kx->p = p; kx->f = KXF_DEAD | KXF_PUBKEY | f; + rs_reset(&kx->rs); if (!(kx->f & KXF_CORK)) { start(kx, time(0)); resend(kx);