#define CASES_MSG3_KNOWN LABEL_MSG3: case LABEL_MSG3BIS
+struct msg;
+
int32_t site_max_start_pad = 4*4;
static cstring_t state_name(uint32_t state)
#define LOG_DUMP 0x00000100
#define LOG_ERROR 0x00000400
#define LOG_PEER_ADDRS 0x00000800
+#define LOG_SIGKEYS 0x00001000
static struct flagstr log_event_table[]={
{ "unexpected", LOG_UNEXPECTED },
{ "dump-packets", LOG_DUMP },
{ "errors", LOG_ERROR },
{ "peer-addrs", LOG_PEER_ADDRS },
+ { "sigkeys", LOG_SIGKEYS },
{ "default", LOG_SETUP_INIT|LOG_SETUP_TIMEOUT|
- LOG_ACTIVATE_KEY|LOG_TIMEOUT_KEY|LOG_SEC|LOG_ERROR },
+ LOG_ACTIVATE_KEY|LOG_TIMEOUT_KEY|LOG_SEC|LOG_ERROR|LOG_SIGKEYS },
{ "all", 0xffffffff },
{ NULL, 0 }
};
case LOG_DUMP: return M_DEBUG;
case LOG_ERROR: return M_ERR;
case LOG_PEER_ADDRS: return M_DEBUG;
+ case LOG_SIGKEYS: return M_INFO;
default: return M_ERR;
}
}
+static uint32_t slog_start(struct site *st, uint32_t event)
+{
+ uint32_t class=event_log_priority(st, event);
+ if (class) {
+ slilog_part(st->log,class,"%s: ",st->tunname);
+ }
+ return class;
+}
+
static void vslog(struct site *st, uint32_t event, cstring_t msg, va_list ap)
FORMAT(printf,3,0);
static void vslog(struct site *st, uint32_t event, cstring_t msg, va_list ap)
{
uint32_t class;
- class=event_log_priority(st, event);
+ class=slog_start(st,event);
if (class) {
- slilog_part(st->log,class,"%s: ",st->tunname);
vslilog_part(st->log,class,msg,ap);
slilog_part(st->log,class,"\n");
}
static void enter_state_run(struct site *st);
static bool_t enter_state_resolve(struct site *st);
static void decrement_resolving_count(struct site *st, int by);
-static bool_t enter_new_state(struct site *st,uint32_t next);
+static bool_t enter_new_state(struct site *st,uint32_t next,
+ const struct msg *prompt
+ /* may be 0 for SENTMSG1 */);
static void enter_state_wait(struct site *st);
static void activate_new_key(struct site *st);
/* Build any of msg1 to msg4. msg5 and msg6 are built from the inside
out using a transform of config data supplied by netlink */
-static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what)
+static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what,
+ const struct msg *prompt
+ /* may be 0 for MSG1 */)
{
string_t dhpub;
unsigned minor;
return False;
}
-static bool_t generate_msg1(struct site *st)
+static bool_t kex_init(struct site *st)
{
st->random->generate(st->random->st,NONCELEN,st->localN);
- return generate_msg(st,LABEL_MSG1,"site:MSG1");
+ return True;
+}
+
+static bool_t generate_msg1(struct site *st, const struct msg *prompt_maybe_0)
+{
+ return
+ generate_msg(st,LABEL_MSG1,"site:MSG1",prompt_maybe_0);
}
static bool_t process_msg1(struct site *st, struct buffer_if *msg1,
- const struct comm_addr *src, struct msg *m)
+ const struct comm_addr *src,
+ const struct msg *m)
{
/* We've already determined we're in an appropriate state to
process an incoming MSG1, and that the MSG1 has correct values
return True;
}
-static bool_t generate_msg2(struct site *st)
+static bool_t generate_msg2(struct site *st,
+ const struct msg *prompt_may_be_null)
{
- st->random->generate(st->random->st,NONCELEN,st->localN);
- return generate_msg(st,LABEL_MSG2,"site:MSG2");
+ return
+ generate_msg(st,LABEL_MSG2,"site:MSG2",prompt_may_be_null);
}
static bool_t process_msg2(struct site *st, struct buffer_if *msg2,
- const struct comm_addr *src)
+ const struct comm_addr *src,
+ struct msg *m /* returned */)
{
- struct msg m;
cstring_t err;
- if (!unpick_msg(st,LABEL_MSG2,msg2,&m)) return False;
- if (!check_msg(st,LABEL_MSG2,&m,&err)) {
+ if (!unpick_msg(st,LABEL_MSG2,msg2,m)) return False;
+ if (!check_msg(st,LABEL_MSG2,m,&err)) {
slog(st,LOG_SEC,"msg2: %s",err);
return False;
}
- st->setup_session_id=m.source;
- st->remote_capabilities=m.remote_capabilities;
+ st->setup_session_id=m->source;
+ st->remote_capabilities=m->remote_capabilities;
/* Select the transform to use */
#undef CHOOSE_CRYPTO
- memcpy(st->remoteN,m.nR,NONCELEN);
+ memcpy(st->remoteN,m->nR,NONCELEN);
return True;
}
-static bool_t generate_msg3(struct site *st)
+static bool_t generate_msg3(struct site *st, const struct msg *prompt)
{
/* Now we have our nonce and their nonce. Think of a secret key,
and create message number 3. */
(st->remote_capabilities & CAPAB_TRANSFORM_MASK)
? LABEL_MSG3BIS
: LABEL_MSG3,
- "site:MSG3");
+ "site:MSG3",prompt);
}
static bool_t process_msg3_msg4(struct site *st, struct msg *m)
}
static bool_t process_msg3(struct site *st, struct buffer_if *msg3,
- const struct comm_addr *src, uint32_t msgtype)
+ const struct comm_addr *src, uint32_t msgtype,
+ struct msg *m /* returned */)
{
- struct msg m;
cstring_t err;
switch (msgtype) {
default: assert(0);
}
- if (!unpick_msg(st,msgtype,msg3,&m)) return False;
- if (!check_msg(st,msgtype,&m,&err)) {
+ if (!unpick_msg(st,msgtype,msg3,m)) return False;
+ if (!check_msg(st,msgtype,m,&err)) {
slog(st,LOG_SEC,"msg3: %s",err);
return False;
}
- uint32_t capab_adv_late = m.remote_capabilities
+ uint32_t capab_adv_late = m->remote_capabilities
& ~st->remote_capabilities & st->early_capabilities;
if (capab_adv_late) {
slog(st,LOG_SEC,"msg3 impermissibly adds early capability flag(s)"
" %#"PRIx32" (was %#"PRIx32", now %#"PRIx32")",
- capab_adv_late, st->remote_capabilities, m.remote_capabilities);
+ capab_adv_late, st->remote_capabilities, m->remote_capabilities);
return False;
}
int i; \
for (i=0; i<st->n##kind##s; i++) { \
iface=st->kind##s[i]; \
- if (iface->capab_bit == m.capab_##kind##num) \
+ if (iface->capab_bit == m->capab_##kind##num) \
goto kind##_found; \
} \
slog(st,LOG_SEC,"peer chose unknown-to-us " what " %d!", \
- m.capab_##kind##num); \
+ m->capab_##kind##num); \
return False; \
kind##_found: \
st->chosen_##kind=iface; \
#undef CHOSE_CRYPTO
- if (!process_msg3_msg4(st,&m))
+ if (!process_msg3_msg4(st,m))
return False;
/* Update our idea of the remote site's capabilities, now that we've
* doesn't change any of the bits we relied upon in the past, but it may
* also have set additional capability bits. We simply throw those away
* now, and use the authentic capabilities from this MSG3. */
- st->remote_capabilities=m.remote_capabilities;
+ st->remote_capabilities=m->remote_capabilities;
/* Terminate their DH public key with a '0' */
- m.pk[m.pklen]=0;
+ m->pk[m->pklen]=0;
/* Invent our DH secret key */
st->random->generate(st->random->st,st->dh->len,st->dhsecret);
/* Generate the shared key and set up the transform */
- if (!set_new_transform(st,m.pk)) return False;
+ if (!set_new_transform(st,m->pk)) return False;
return True;
}
-static bool_t generate_msg4(struct site *st)
+static bool_t generate_msg4(struct site *st, const struct msg *prompt)
{
/* We have both nonces, their public key and our private key. Generate
our public key, sign it and send it to them. */
- return generate_msg(st,LABEL_MSG4,"site:MSG4");
+ return generate_msg(st,LABEL_MSG4,"site:MSG4",prompt);
}
static bool_t process_msg4(struct site *st, struct buffer_if *msg4,
- const struct comm_addr *src)
+ const struct comm_addr *src,
+ struct msg *m /* returned */)
{
- struct msg m;
cstring_t err;
- if (!unpick_msg(st,LABEL_MSG4,msg4,&m)) return False;
- if (!check_msg(st,LABEL_MSG4,&m,&err)) {
+ if (!unpick_msg(st,LABEL_MSG4,msg4,m)) return False;
+ if (!check_msg(st,LABEL_MSG4,m,&err)) {
slog(st,LOG_SEC,"msg4: %s",err);
return False;
}
- if (!process_msg3_msg4(st,&m))
+ if (!process_msg3_msg4(st,m))
return False;
/* Terminate their DH public key with a '0' */
- m.pk[m.pklen]=0;
+ m->pk[m->pklen]=0;
/* Generate the shared key and set up the transform */
- if (!set_new_transform(st,m.pk)) return False;
+ if (!set_new_transform(st,m->pk)) return False;
return True;
}
/* Leaves transformed part of buffer untouched */
}
-static bool_t generate_msg5(struct site *st)
+static bool_t generate_msg5(struct site *st, const struct msg *prompt)
{
cstring_t transform_err;
buf_prepend_uint32(&st->buffer,session_id);
}
-static bool_t generate_msg6(struct site *st)
+static bool_t generate_msg6(struct site *st, const struct msg *prompt)
{
if (!is_transform_valid(st->new_transform))
return False;
switch (st->state) {
case SITE_RESOLVE:
if (transport_compute_setupinit_peers(st,addrs,naddrs,0)) {
- enter_new_state(st,SITE_SENTMSG1);
+ enter_new_state(st,SITE_SENTMSG1,0);
} else {
/* Can't figure out who to try to to talk to */
slog(st,LOG_SETUP_INIT,
slog(st,LOG_SETUP_INIT,"resolving peer address(es)");
return enter_state_resolve(st);
} else if (transport_compute_setupinit_peers(st,0,0,prod_hint)) {
- return enter_new_state(st,SITE_SENTMSG1);
+ return enter_new_state(st,SITE_SENTMSG1,0);
}
slog(st,LOG_SETUP_INIT,"key exchange failed: no address for peer");
return False;
return ensure_resolving(st);
}
-static bool_t enter_new_state(struct site *st, uint32_t next)
+static bool_t enter_new_state(struct site *st, uint32_t next,
+ const struct msg *prompt
+ /* may be 0 for SENTMSG1 */)
{
- bool_t (*gen)(struct site *st);
+ bool_t (*gen)(struct site *st, const struct msg *prompt);
int r;
slog(st,LOG_STATE,"entering state %s",state_name(next));
switch(next) {
case SITE_SENTMSG1:
state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE);
+ if (!kex_init(st)) return False;
gen=generate_msg1;
st->msg1_crossed_logged = False;
break;
case SITE_SENTMSG2:
state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE ||
st->state==SITE_SENTMSG1 || st->state==SITE_WAIT);
+ if (!kex_init(st)) return False;
gen=generate_msg2;
break;
case SITE_SENTMSG3:
if (hacky_par_start_failnow()) return False;
- r= gen(st) && send_msg(st);
+ r= gen(st,prompt) && send_msg(st);
hacky_par_end(&r,
st->setup_retries, st->setup_retry_interval,
}
static bool_t named_for_us(struct site *st, const struct buffer_if *buf_in,
- uint32_t type, struct msg *m)
+ uint32_t type, struct msg *m,
+ struct priomsg *whynot)
/* For packets which are identified by the local and remote names.
* If it has our name and our peer's name in it it's for us. */
{
struct buffer_if buf[1];
buffer_readonly_clone(buf,buf_in);
- return unpick_msg(st,type,buf,m)
- && name_matches(&m->remote,st->remotename)
- && name_matches(&m->local,st->localname);
+
+ if (!unpick_msg(st,type,buf,m)) {
+ priomsg_update_fixed(whynot, comm_notify_whynot_unpick, "malformed");
+ return False;
+ }
+#define NAME_MATCHES(lr) \
+ if (!name_matches(&m->lr, st->lr##name)) { \
+ if (priomsg_update_fixed(whynot, comm_notify_whynot_name_##lr, \
+ "unknown " #lr " name: ")) { \
+ truncmsg_add_packet_string(&whynot->m, m->lr.len, m->lr.name); \
+ } \
+ return False; \
+ }
+ NAME_MATCHES(remote);
+ NAME_MATCHES(local );
+#undef NAME_MATCHES
+
+ return True;
}
static bool_t we_have_priority(struct site *st, const struct msg *m) {
static bool_t setup_late_msg_ok(struct site *st,
const struct buffer_if *buf_in,
uint32_t msgtype,
- const struct comm_addr *source) {
+ const struct comm_addr *source,
+ struct msg *m /* returned */) {
/* For setup packets which seem from their type like they are
* late. Maybe they came via a different path. All we do is make
* a note of the sending address, iff they look like they are part
* of the current key setup attempt. */
- struct msg m;
- if (!named_for_us(st,buf_in,msgtype,&m))
+ if (!named_for_us(st,buf_in,msgtype,m,0))
/* named_for_us calls unpick_msg which gets the nonces */
return False;
- if (!consttime_memeq(m.nR,st->remoteN,NONCELEN) ||
- !consttime_memeq(m.nL,st->localN, NONCELEN))
+ if (!consttime_memeq(m->nR,st->remoteN,NONCELEN) ||
+ !consttime_memeq(m->nL,st->localN, NONCELEN))
/* spoof ? from stale run ? who knows */
return False;
transport_setup_msgok(st,source);
this current site instance (and should therefore not be processed
by other sites), even if the packet was otherwise ignored. */
static bool_t site_incoming(void *sst, struct buffer_if *buf,
- const struct comm_addr *source)
+ const struct comm_addr *source,
+ struct priomsg *whynot)
{
struct site *st=sst;
uint32_t dest=get_uint32(buf->start);
uint32_t msgtype=get_uint32(buf->start+8);
- struct msg named_msg;
+ struct msg msg;
+ /* initialised by named_for_us, or process_msgN for N!=1 */
if (msgtype==LABEL_MSG1) {
- if (!named_for_us(st,buf,msgtype,&named_msg))
+ if (!named_for_us(st,buf,msgtype,&msg,whynot))
return False;
/* It's a MSG1 addressed to us. Decide what to do about it. */
dump_packet(st,buf,source,True,True);
st->state==SITE_WAIT) {
/* We should definitely process it */
transport_compute_setupinit_peers(st,0,0,source);
- if (process_msg1(st,buf,source,&named_msg)) {
+ if (process_msg1(st,buf,source,&msg)) {
slog(st,LOG_SETUP_INIT,"key setup initiated by peer");
- bool_t entered=enter_new_state(st,SITE_SENTMSG2);
+ bool_t entered=enter_new_state(st,SITE_SENTMSG2,&msg);
if (entered && st->addresses && st->local_mobile)
/* We must do this as the very last thing, because
the resolver callback might reenter us. */
/* We've just sent a message 1! They may have crossed on
the wire. If we have priority then we ignore the
incoming one, otherwise we process it as usual. */
- if (we_have_priority(st,&named_msg)) {
+ if (we_have_priority(st,&msg)) {
BUF_FREE(buf);
if (!st->msg1_crossed_logged++)
slog(st,LOG_SETUP_INIT,"crossed msg1s; we are higher "
} else {
slog(st,LOG_SETUP_INIT,"crossed msg1s; we are lower "
"priority => use incoming msg1");
- if (process_msg1(st,buf,source,&named_msg)) {
+ if (process_msg1(st,buf,source,&msg)) {
BUF_FREE(&st->buffer); /* Free our old message 1 */
transport_setup_msgok(st,source);
- enter_new_state(st,SITE_SENTMSG2);
+ enter_new_state(st,SITE_SENTMSG2,&msg);
} else {
slog(st,LOG_ERROR,"failed to process an incoming "
"crossed msg1 (we have low priority)");
}
} else if (st->state==SITE_SENTMSG2 ||
st->state==SITE_SENTMSG4) {
- if (consttime_memeq(named_msg.nR,st->remoteN,NONCELEN)) {
+ if (consttime_memeq(msg.nR,st->remoteN,NONCELEN)) {
/* We are ahead in the protocol, but that msg1 had the
* peer's nonce so presumably it is from this key
* exchange run, via a slower route */
return True;
}
if (msgtype==LABEL_PROD) {
- if (!named_for_us(st,buf,msgtype,&named_msg))
+ if (!named_for_us(st,buf,msgtype,&msg,whynot))
return False;
dump_packet(st,buf,source,True,True);
if (st->state!=SITE_RUN) {
if (st->state!=SITE_SENTMSG1) {
if ((st->state==SITE_SENTMSG3 ||
st->state==SITE_SENTMSG5) &&
- setup_late_msg_ok(st,buf,msgtype,source))
+ setup_late_msg_ok(st,buf,msgtype,source,&msg))
break;
slog(st,LOG_UNEXPECTED,"unexpected MSG2");
- } else if (process_msg2(st,buf,source)) {
+ } else if (process_msg2(st,buf,source,&msg)) {
transport_setup_msgok(st,source);
- enter_new_state(st,SITE_SENTMSG3);
+ enter_new_state(st,SITE_SENTMSG3,&msg);
} else {
slog(st,LOG_SEC,"invalid MSG2");
}
/* Setup packet: expected only in state SENTMSG2 */
if (st->state!=SITE_SENTMSG2) {
if ((st->state==SITE_SENTMSG4) &&
- setup_late_msg_ok(st,buf,msgtype,source))
+ setup_late_msg_ok(st,buf,msgtype,source,&msg))
break;
slog(st,LOG_UNEXPECTED,"unexpected MSG3");
- } else if (process_msg3(st,buf,source,msgtype)) {
+ } else if (process_msg3(st,buf,source,msgtype,&msg)) {
transport_setup_msgok(st,source);
- enter_new_state(st,SITE_SENTMSG4);
+ enter_new_state(st,SITE_SENTMSG4,&msg);
} else {
slog(st,LOG_SEC,"invalid MSG3");
}
/* Setup packet: expected only in state SENTMSG3 */
if (st->state!=SITE_SENTMSG3) {
if ((st->state==SITE_SENTMSG5) &&
- setup_late_msg_ok(st,buf,msgtype,source))
+ setup_late_msg_ok(st,buf,msgtype,source,&msg))
break;
slog(st,LOG_UNEXPECTED,"unexpected MSG4");
- } else if (process_msg4(st,buf,source)) {
+ } else if (process_msg4(st,buf,source,&msg)) {
transport_setup_msgok(st,source);
- enter_new_state(st,SITE_SENTMSG5);
+ enter_new_state(st,SITE_SENTMSG5,&msg);
} else {
slog(st,LOG_SEC,"invalid MSG4");
}
if (st->state==SITE_SENTMSG4) {
if (process_msg5(st,buf,source,st->new_transform)) {
transport_setup_msgok(st,source);
- enter_new_state(st,SITE_RUN);
+ enter_new_state(st,SITE_RUN,&msg);
} else {
slog(st,LOG_SEC,"invalid MSG5");
}
return True;
}
+ priomsg_update_fixed(whynot, comm_notify_whynot_general,
+ "not MSG1 or PROD; unknown dest index");
return False;
}