A is the originating gateway machine name
B is the destination gateway machine name
-A+ and B+ are the names with optional additional data, currently ignored
+A+ and B+ are the names with optional additional data, see below
PK_A is the public RSA key of A
PK_B is the public RSA key of B
PK_A^-1 is the private RSA key of A
Note that 'i' may be re-used from one session to the next, whereas 'n'
is always fresh.
-The protocol version selection stuff is not yet implemented.
+The optional additional data after the sender's name consists of some
+initial subset of the following list of items:
+ * A 32-bit integer with a set of capability flags, representing the
+ abilities of the sender.
+ * More data which is yet to be defined and which must be ignored
+ by receivers.
+The optional additional data after the receiver's name is not
+currently used. If any is seen, it must be ignored.
+
+Capability flag bits must be in one the following two categories:
+
+1. Early capability flags must be advertised in MSG1 or MSG2, as
+ applicable. If MSG3 or MSG4 advertise any "early" capability bits,
+ MSG1 or MSG3 (as applicable) must have advertised them too. Sadly,
+ advertising an early capability flag will produce MSG1s which are
+ not understood by versions of secnet which predate the capability
+ mechanism.
+
+2. Late capability flags are advertised in MSG2 or MSG3, as
+ applicable. They may also appear in MSG1, but this is not
+ guaranteed. MSG4 must advertise the same set as MSG2.
+
+No capability flags are currently defined. Unknown capability flags
+should be treated as late ones.
+
Messages:
struct hash_if *hash;
uint32_t index; /* Index of this site */
+ uint32_t local_capabilities;
int32_t setup_retries; /* How many times to send setup packets */
int32_t setup_retry_interval; /* Initial timeout for setup packets */
int32_t wait_timeout; /* How long to wait if setup unsuccessful */
packet; we keep trying to continue the exchange, and have to
timeout before we can listen for another setup packet); perhaps
we should keep a list of 'bad' sources for setup packets. */
+ uint32_t remote_capabilities;
uint32_t setup_session_id;
transport_peers setup_peers;
uint8_t localN[NONCELEN]; /* Nonces for key exchange */
struct parsedname {
int32_t len;
uint8_t *name;
- int32_t extrainfo_len;
- uint8_t *extrainfo;
+ struct buffer_if extrainfo;
};
struct msg {
uint32_t source;
struct parsedname remote;
struct parsedname local;
+ uint32_t remote_capabilities;
uint8_t *nR;
uint8_t *nL;
int32_t pklen;
char *sig;
};
+struct xinfoadd {
+ int32_t lenpos, afternul;
+};
+static void append_string_xinfo_start(struct buffer_if *buf,
+ struct xinfoadd *xia,
+ const char *str)
+ /* Helps construct one of the names with additional info as found
+ * in MSG1..4. Call this function first, then append all the
+ * desired extra info (not including the nul byte) to the buffer,
+ * then call append_string_xinfo_done. */
+{
+ xia->lenpos = buf->size;
+ buf_append_string(buf,str);
+ buf_append_uint8(buf,0);
+ xia->afternul = buf->size;
+}
+static void append_string_xinfo_done(struct buffer_if *buf,
+ struct xinfoadd *xia)
+{
+ /* we just need to adjust the string length */
+ if (buf->size == xia->afternul) {
+ /* no extra info, strip the nul too */
+ buf_unappend_uint8(buf);
+ } else {
+ put_uint16(buf->start+xia->lenpos, buf->size-(xia->lenpos+2));
+ }
+}
+
/* 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)
(type==LABEL_MSG1?0:st->setup_session_id));
buf_append_uint32(&st->buffer,st->index);
buf_append_uint32(&st->buffer,type);
- buf_append_string(&st->buffer,st->localname);
+
+ struct xinfoadd xia;
+ append_string_xinfo_start(&st->buffer,&xia,st->localname);
+ if ((st->local_capabilities & CAPAB_EARLY) || (type != LABEL_MSG1)) {
+ buf_append_uint32(&st->buffer,st->local_capabilities);
+ }
+ append_string_xinfo_done(&st->buffer,&xia);
+
buf_append_string(&st->buffer,st->remotename);
memcpy(buf_append(&st->buffer,NONCELEN),st->localN,NONCELEN);
if (type==LABEL_MSG1) return True;
nm->name=buf_unprepend(msg,nm->len);
uint8_t *nul=memchr(nm->name,0,nm->len);
if (!nul) {
- nm->extrainfo_len=0;
- nm->extrainfo=0;
+ buffer_readonly_view(&nm->extrainfo,0,0);
} else {
- nm->extrainfo=nul+1;
- nm->extrainfo_len=msg->start-nm->extrainfo;
+ buffer_readonly_view(&nm->extrainfo, nul+1, msg->start-(nul+1));
nm->len=nul-nm->name;
}
return True;
m->source=buf_unprepend_uint32(msg);
CHECK_TYPE(msg,type);
if (!unpick_name(msg,&m->remote)) return False;
+ m->remote_capabilities=0;
+ if (m->remote.extrainfo.size) {
+ CHECK_AVAIL(&m->remote.extrainfo,4);
+ m->remote_capabilities=buf_unprepend_uint32(&m->remote.extrainfo);
+ }
if (!unpick_name(msg,&m->local)) return False;
CHECK_AVAIL(msg,NONCELEN);
m->nR=buf_unprepend(msg,NONCELEN);
*error="wrong remotely-generated nonce";
return False;
}
+ /* MSG3 has complicated rules about capabilities, which are
+ * handled in process_msg3. */
if (type==LABEL_MSG3) return True;
+ if (m->remote_capabilities!=st->remote_capabilities) {
+ *error="remote capabilities changed";
+ return False;
+ }
if (type==LABEL_MSG4) return True;
*error="unknown message type";
return False;
transport_record_peer(st,&st->setup_peers,src,"msg1");
st->setup_session_id=m->source;
+ st->remote_capabilities=m->remote_capabilities;
memcpy(st->remoteN,m->nR,NONCELEN);
return True;
}
return False;
}
st->setup_session_id=m.source;
+ st->remote_capabilities=m.remote_capabilities;
memcpy(st->remoteN,m.nR,NONCELEN);
return True;
}
slog(st,LOG_SEC,"msg3: %s",err);
return False;
}
+ uint32_t capab_adv_late = m.remote_capabilities
+ & ~st->remote_capabilities & CAPAB_EARLY;
+ 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);
+ return False;
+ }
+ st->remote_capabilities|=m.remote_capabilities;
/* Check signature and store g^x mod m */
hash=safe_malloc(st->hash->len, "process_msg3");
assert(index_sequence < 0xffffffffUL);
st->index = ++index_sequence;
+ st->local_capabilities = 0;
st->netlink=find_cl_if(dict,"link",CL_NETLINK,True,"site",loc);
list_t *comms_cfg=dict_lookup(dict,"comm");
register_for_poll(st, site_beforepoll, site_afterpoll, 0, "site");
st->timeout=0;
+ st->remote_capabilities=0;
st->current.key_timeout=0;
st->auxiliary_key.key_timeout=0;
transport_peers_clear(st,&st->peers);