uint8_t localN[NONCELEN]; /* Nonces for key exchange */
uint8_t remoteN[NONCELEN];
struct buffer_if buffer; /* Current outgoing key exchange packet */
+ struct buffer_if scratch;
int32_t retries; /* Number of retries remaining */
uint64_t timeout; /* Timeout for current state */
uint8_t *dhsecret;
static bool_t enter_state_resolve(struct site *st);
static bool_t enter_new_state(struct site *st,uint32_t next);
static void enter_state_wait(struct site *st);
+static void activate_new_key(struct site *st);
#define CHECK_AVAIL(b,l) do { if ((b)->size<(l)) return False; } while(0)
#define CHECK_EMPTY(b) do { if ((b)->size!=0) return False; } while(0)
}
static bool_t process_msg5(struct site *st, struct buffer_if *msg5,
- const struct comm_addr *src)
+ const struct comm_addr *src,
+ struct transform_inst_if *transform)
{
struct msg0 m;
cstring_t transform_err;
if (!unpick_msg0(st,msg5,&m)) return False;
- if (st->new_transform->reverse(st->new_transform->st,
- msg5,&transform_err)) {
+ if (transform->reverse(transform->st,msg5,&transform_err)) {
/* There's a problem */
slog(st,LOG_SEC,"process_msg5: transform: %s",transform_err);
return False;
return True;
}
-static bool_t generate_msg6(struct site *st)
+static void create_msg6(struct site *st, struct transform_inst_if *transform,
+ uint32_t session_id)
{
cstring_t transform_err;
/* Give the netlink code an opportunity to put its own stuff in the
message (configuration information, etc.) */
buf_prepend_uint32(&st->buffer,LABEL_MSG6);
- st->new_transform->forwards(st->new_transform->st,&st->buffer,
- &transform_err);
+ transform->forwards(transform->st,&st->buffer,&transform_err);
buf_prepend_uint32(&st->buffer,LABEL_MSG6);
buf_prepend_uint32(&st->buffer,st->index);
- buf_prepend_uint32(&st->buffer,st->setup_session_id);
+ buf_prepend_uint32(&st->buffer,session_id);
+}
+static bool_t generate_msg6(struct site *st)
+{
+ create_msg6(st,st->new_transform,st->setup_session_id);
st->retries=1; /* Peer will retransmit MSG5 if this packet gets lost */
return True;
}
return True;
}
-static bool_t process_msg0(struct site *st, struct buffer_if *msg0,
- const struct comm_addr *src)
+static bool_t decrypt_msg0(struct site *st, struct buffer_if *msg0)
{
+ cstring_t transform_err, newkey_err="n/a";
struct msg0 m;
- cstring_t transform_err;
- uint32_t type;
-
- if (!st->current_valid) {
- slog(st,LOG_DROP,"incoming message but no current key -> dropping");
- return initiate_key_setup(st,"incoming message but no current key");
- }
+ uint32_t problem;
if (!unpick_msg0(st,msg0,&m)) return False;
- if (st->current_transform->reverse(st->current_transform->st,
- msg0,&transform_err)) {
- /* There's a problem */
- slog(st,LOG_SEC,"transform: %s",transform_err);
- return initiate_key_setup(st,"incoming message would not decrypt");
+ /* Keep a copy so we can try decrypting it with multiple keys */
+ buffer_copy(&st->scratch, msg0);
+
+ problem = st->current_transform->reverse(st->current_transform->st,
+ msg0,&transform_err);
+ if (!problem) return True;
+
+ if (problem==2) {
+ slog(st,LOG_DROP,"transform: %s (merely skew)",transform_err);
+ return False;
+ }
+
+ if (st->state==SITE_SENTMSG5) {
+ buffer_copy(msg0, &st->scratch);
+ if (!st->new_transform->reverse(st->new_transform->st,
+ msg0,&newkey_err)) {
+ /* It looks like we didn't get the peer's MSG6 */
+ /* This is like a cut-down enter_new_state(SITE_RUN) */
+ slog(st,LOG_STATE,"will enter state RUN (MSG0 with new key)");
+ BUF_FREE(&st->buffer);
+ st->timeout=0;
+ activate_new_key(st);
+ return True; /* do process the data in this packet */
+ }
}
+
+ slog(st,LOG_SEC,"transform: %s (new: %s)",transform_err,newkey_err);
+ initiate_key_setup(st,"incoming message would not decrypt");
+ return False;
+}
+
+static bool_t process_msg0(struct site *st, struct buffer_if *msg0,
+ const struct comm_addr *src)
+{
+ uint32_t type;
+
+ if (!decrypt_msg0(st,msg0))
+ return False;
+
CHECK_AVAIL(msg0,4);
type=buf_unprepend_uint32(msg0);
switch(type) {
/* Setup packet: expected only in state SENTMSG4 */
/* (may turn up in state RUN if our return MSG6 was lost
and the new key has already been activated. In that
- case we should treat it as an ordinary PING packet. We
- can't pass it to process_msg5() because the
- new_transform will now be unkeyed. XXX) */
- if (st->state!=SITE_SENTMSG4) {
- slog(st,LOG_UNEXPECTED,"unexpected MSG5");
- } else if (process_msg5(st,buf,source)) {
- transport_setup_msgok(st,source);
- enter_new_state(st,SITE_RUN);
+ case we discard it. The peer will realise that we
+ are using the new key when they see our data packets.
+ Until then the peer's data packets to us get discarded. */
+ 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);
+ } else {
+ slog(st,LOG_SEC,"invalid MSG5");
+ }
+ } else if (st->state==SITE_RUN) {
+ if (process_msg5(st,buf,source,st->current_transform)) {
+ slog(st,LOG_DROP,"got MSG5, retransmitting MSG6");
+ transport_setup_msgok(st,source);
+ create_msg6(st,st->current_transform,st->remote_session_id);
+ transport_xmit(st,&st->peers,&st->buffer,True);
+ BUF_FREE(&st->buffer);
+ } else {
+ slog(st,LOG_SEC,"invalid MSG5 (in state RUN)");
+ }
} else {
- slog(st,LOG_SEC,"invalid MSG5");
+ slog(st,LOG_UNEXPECTED,"unexpected MSG5");
}
break;
case LABEL_MSG6:
buffer_new(&st->buffer,SETUP_BUFFER_LEN);
+ buffer_new(&st->scratch,0);
+ BUF_ALLOC(&st->scratch,"site:scratch");
+
/* We are interested in poll(), but only for timeouts. We don't have
any fds of our own. */
register_for_poll(st, site_beforepoll, site_afterpoll, 0, "site");