*
* secnet is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version d of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* secnet is distributed in the hope that it will be useful, but
/* configuration information */
string_t localname;
string_t remotename;
+ bool_t keepalive;
bool_t local_mobile, peer_mobile; /* Mobile client support */
int32_t transport_peers_max;
string_t tunname; /* localname<->remotename by default, used in logs */
uint32_t mtu_target;
struct netlink_if *netlink;
struct comm_if **comms;
+ struct comm_clientinfo **commclientinfos;
int ncomms;
struct resolver_if *resolver;
struct log_if *log;
after this time, initiate a new
key exchange */
- bool_t setup_priority; /* Do we have precedence if both sites emit
- message 1 simultaneously? */
+ bool_t our_name_later; /* our name > peer name */
uint32_t log_events;
/* runtime information */
uint32_t state;
uint64_t now; /* Most recently seen time */
bool_t allow_send_prod;
+ bool_t msg1_crossed_logged;
int resolving_count;
int resolving_n_results_all;
int resolving_n_results_stored;
}
#define DEFINE_CALL_TRANSFORM(fwdrev) \
-static int call_transform_##fwdrev(struct site *st, \
+static transform_apply_return \
+call_transform_##fwdrev(struct site *st, \
struct transform_inst_if *transform, \
struct buffer_if *buf, \
const char **errmsg) \
{ \
if (!is_transform_valid(transform)) { \
*errmsg="transform not set up"; \
- return 1; \
+ return transform_apply_err; \
} \
return transform->fwdrev(transform->st,buf,errmsg); \
}
char *sig;
};
-static void set_new_transform(struct site *st, char *pk)
+static _Bool set_new_transform(struct site *st, char *pk)
{
+ _Bool ok;
+
/* Make room for the shared key */
st->sharedsecretlen=st->chosen_transform->keylen?:st->dh->ceil_len;
assert(st->sharedsecretlen);
/* Set up the transform */
struct transform_if *generator=st->chosen_transform;
struct transform_inst_if *generated=generator->create(generator->st);
- generated->setkey(generated->st,st->sharedsecret,
- st->sharedsecretlen,st->setup_priority);
+ ok = generated->setkey(generated->st,st->sharedsecret,
+ st->sharedsecretlen,st->our_name_later);
+
dispose_transform(&st->new_transform);
+ if (!ok) return False;
st->new_transform=generated;
slog(st,LOG_SETUP_INIT,"key exchange negotiated transform"
" %d (capabilities ours=%#"PRIx32" theirs=%#"PRIx32")",
st->chosen_transform->capab_transformnum,
st->local_capabilities, st->remote_capabilities);
+ return True;
}
struct xinfoadd {
CHECK_AVAIL(msg,m->siglen);
m->sig=buf_unprepend(msg,m->siglen);
CHECK_EMPTY(msg);
+
+ /* In `process_msg3_msg4' below, we assume that we can write a nul
+ * terminator following the signature. Make sure there's enough space.
+ */
+ if (msg->start >= msg->base + msg->alloclen)
+ return False;
+
return True;
}
hst=st->hash->init();
st->hash->update(hst,m->hashstart,m->hashlen);
st->hash->final(hst,hash);
- /* Terminate signature with a '0' - cheating, but should be ok */
+ /* Terminate signature with a '0' - already checked that this will fit */
m->sig[m->siglen]=0;
if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m->sig)) {
slog(st,LOG_SEC,"msg3/msg4 signature failed check!");
st->random->generate(st->random->st,st->dh->len,st->dhsecret);
/* Generate the shared key and set up the transform */
- set_new_transform(st,m.pk);
+ if (!set_new_transform(st,m.pk)) return False;
return True;
}
m.pk[m.pklen]=0;
/* Generate the shared key and set up the transform */
- set_new_transform(st,m.pk);
+ if (!set_new_transform(st,m.pk)) return False;
return True;
}
/* Give the netlink code an opportunity to put its own stuff in the
message (configuration information, etc.) */
buf_prepend_uint32(&st->buffer,LABEL_MSG6);
- int problem = call_transform_forwards(st,transform,
- &st->buffer,&transform_err);
+ transform_apply_return problem =
+ call_transform_forwards(st,transform,
+ &st->buffer,&transform_err);
assert(!problem);
buf_prepend_uint32(&st->buffer,LABEL_MSG6);
buf_prepend_uint32(&st->buffer,st->index);
return True;
}
-static bool_t decrypt_msg0(struct site *st, struct buffer_if *msg0,
+static transform_apply_return
+decrypt_msg0(struct site *st, struct buffer_if *msg0,
const struct comm_addr *src)
{
cstring_t transform_err, auxkey_err, newkey_err="n/a";
struct msg0 m;
- uint32_t problem;
+ transform_apply_return problem;
if (!unpick_msg0(st,msg0,&m)) return False;
if (!st->auxiliary_is_new)
delete_one_key(st,&st->auxiliary_key,
"peer has used new key","auxiliary key",LOG_SEC);
- return True;
+ return 0;
}
- if (problem==2)
- goto skew;
+ if (transform_apply_return_badseq(problem))
+ goto badseq;
buffer_copy(msg0, &st->scratch);
problem = call_transform_reverse(st,st->auxiliary_key.transform,
msg0,&auxkey_err);
- if (problem==0) {
+ if (!problem) {
slog(st,LOG_DROP,"processing packet which uses auxiliary key");
if (st->auxiliary_is_new) {
/* We previously timed out in state SENTMSG5 but it turns
st->auxiliary_is_new=0;
st->renegotiate_key_time=st->auxiliary_renegotiate_key_time;
}
- return True;
+ return 0;
}
- if (problem==2)
- goto skew;
+ if (transform_apply_return_badseq(problem))
+ goto badseq;
if (st->state==SITE_SENTMSG5) {
buffer_copy(msg0, &st->scratch);
BUF_FREE(&st->buffer);
st->timeout=0;
activate_new_key(st);
- return True; /* do process the data in this packet */
+ return 0; /* do process the data in this packet */
}
- if (problem==2)
- goto skew;
+ if (transform_apply_return_badseq(problem))
+ goto badseq;
}
slog(st,LOG_SEC,"transform: %s (aux: %s, new: %s)",
transform_err,auxkey_err,newkey_err);
initiate_key_setup(st,"incoming message would not decrypt",0);
send_nak(src,m.dest,m.source,m.type,msg0,"message would not decrypt");
- return False;
+ assert(problem);
+ return problem;
- skew:
- slog(st,LOG_DROP,"transform: %s (merely skew)",transform_err);
- return False;
+ badseq:
+ slog(st,LOG_DROP,"transform: %s (bad seq.)",transform_err);
+ assert(problem);
+ return problem;
}
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,src))
+ transform_apply_return problem;
+
+ problem = decrypt_msg0(st,msg0,src);
+ if (problem==transform_apply_seqdupe) {
+ /* We recently received another copy of this packet, maybe due
+ * to polypath. That's not a problem; indeed, for the
+ * purposes of transport address management it is a success.
+ * But we don't want to process the packet. */
+ transport_data_msgok(st,src);
+ return False;
+ }
+ if (problem)
return False;
CHECK_AVAIL(msg0,4);
case LABEL_MSG7:
/* We must forget about the current session. */
delete_keys(st,"request from peer",LOG_SEC);
+ /* probably, the peer is shutting down, and this is going to fail,
+ * but we need to be trying to bring the link up again */
+ if (st->keepalive)
+ initiate_key_setup(st,"peer requested key teardown",0);
return True;
case LABEL_MSG9:
/* Deliver to netlink layer */
ok?"":" - fail");
}
+static bool_t comm_addr_sendmsg(struct site *st,
+ const struct comm_addr *dest,
+ struct buffer_if *buf)
+{
+ int i;
+ struct comm_clientinfo *commclientinfo = 0;
+
+ for (i=0; i < st->ncomms; i++) {
+ if (st->comms[i] == dest->comm) {
+ commclientinfo = st->commclientinfos[i];
+ break;
+ }
+ }
+ return dest->comm->sendmsg(dest->comm->st, buf, dest, commclientinfo);
+}
+
static uint32_t site_status(void *st)
{
return 0;
FILLZERO(st->remoteN);
dispose_transform(&st->new_transform);
memset(st->dhsecret,0,st->dh->len);
- memset(st->sharedsecret,0,st->sharedsecretlen);
+ if (st->sharedsecret) memset(st->sharedsecret,0,st->sharedsecretlen);
set_link_quality(st);
+
+ if (st->keepalive && !current_valid(st))
+ initiate_key_setup(st, "keepalive", 0);
}
static bool_t ensure_resolving(struct site *st)
case SITE_SENTMSG1:
state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE);
gen=generate_msg1;
+ st->msg1_crossed_logged = False;
break;
case SITE_SENTMSG2:
state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE ||
slog(st,LOG_SETUP_INIT,"prodding peer for key exchange");
st->allow_send_prod=0;
generate_prod(st,&st->scratch);
- bool_t ok = source->comm->sendmsg(source->comm->st, &st->scratch, source);
+ bool_t ok = comm_addr_sendmsg(st, source, &st->scratch);
dump_packet(st,&st->scratch,source,False,ok);
}
&& name_matches(&m->local,st->localname);
}
+static bool_t we_have_priority(struct site *st, const struct msg *m) {
+ if ((st->local_capabilities & m->remote_capabilities)
+ && CAPAB_PRIORITY_MOBILE) {
+ if (st->local_mobile) return True;
+ if (st-> peer_mobile) return False;
+ }
+ return st->our_name_later;
+}
+
/* This function is called by the communication device to deliver
packets from our peers.
It should return True if the packet is recognised as being for
/* 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 (st->setup_priority) {
+ if (we_have_priority(st,&named_msg)) {
BUF_FREE(buf);
- slog(st,LOG_DUMP,"crossed msg1s; we are higher "
- "priority => ignore incoming msg1");
+ if (!st->msg1_crossed_logged++)
+ slog(st,LOG_SETUP_INIT,"crossed msg1s; we are higher "
+ "priority => ignore incoming msg1");
return True;
} else {
- slog(st,LOG_DUMP,"crossed msg1s; we are lower "
+ slog(st,LOG_SETUP_INIT,"crossed msg1s; we are lower "
"priority => use incoming msg1");
if (process_msg1(st,buf,source,&named_msg)) {
BUF_FREE(&st->buffer); /* Free our old message 1 */
st->localname=dict_read_string(dict, "local-name", True, "site", loc);
st->remotename=dict_read_string(dict, "name", True, "site", loc);
+ st->keepalive=dict_read_bool(dict,"keepalive",False,"site",loc,False);
+
st->peer_mobile=dict_read_bool(dict,"mobile",False,"site",loc,False);
st->local_mobile=
dict_read_bool(dict,"local-mobile",False,"site",loc,False);
GET_CLOSURE_LIST("comm",comms,ncomms,CL_COMM);
+ NEW_ARY(st->commclientinfos, st->ncomms);
+ dict_t *comminfo = dict_read_dict(dict,"comm-info",False,"site",loc);
+ for (i=0; i<st->ncomms; i++) {
+ st->commclientinfos[i] =
+ !comminfo ? 0 :
+ st->comms[i]->clientinfo(st->comms[i],comminfo,loc);
+ }
+
st->resolver=find_cl_if(dict,"resolver",CL_RESOLVER,True,"site",loc);
st->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc);
st->random=find_cl_if(dict,"random",CL_RANDOMSRC,True,"site",loc);
/* The information we expect to see in incoming messages of type 1 */
/* fixme: lots of unchecked overflows here, but the results are only
corrupted packets rather than undefined behaviour */
- st->setup_priority=(strcmp(st->localname,st->remotename)>0);
+ st->our_name_later=(strcmp(st->localname,st->remotename)>0);
buffer_new(&st->buffer,SETUP_BUFFER_LEN);
st->local_capabilities |= capbit;
}
+ if (st->local_mobile || st->peer_mobile)
+ st->local_capabilities |= CAPAB_PRIORITY_MOBILE;
+
/* We need to register the remote networks with the netlink device */
uint32_t netlink_mtu; /* local virtual interface mtu */
st->netlink->reg(st->netlink->st, site_outgoing, st, &netlink_mtu);
*
* Caller must first call transport_peers_expire. */
- if (naddrs==1 && peers->npeers>=1 &&
- comm_addr_equal(&addrs[0], &peers->peers[0].addr)) {
- /* optimisation, also avoids debug for trivial updates */
- peers->peers[0].last = *tv_now;
- return;
+ if (naddrs==1) {
+ /* avoids debug for uninteresting updates */
+ int i;
+ for (i=0; i<peers->npeers; i++) {
+ if (comm_addr_equal(&addrs[0], &peers->peers[i].addr)) {
+ memmove(peers->peers+1, peers->peers,
+ sizeof(peers->peers[0]) * i);
+ peers->peers[0].addr = addrs[0];
+ peers->peers[0].last = *tv_now;
+ return;
+ }
+ }
}
int old_npeers=peers->npeers;
int nfailed=0;
for (slot=0; slot<peers->npeers; slot++) {
transport_peer *peer=&peers->peers[slot];
- bool_t ok =
- peer->addr.comm->sendmsg(peer->addr.comm->st, buf, &peer->addr);
+ bool_t ok = comm_addr_sendmsg(st, &peer->addr, buf);
if (candebug)
dump_packet(st, buf, &peer->addr, False, ok);
if (!ok) {