struct xinfoadd xia;
append_string_xinfo_start(&st->buffer,&xia,st->localname);
- if ((st->local_capabilities & st->early_capabilities) ||
- (type != LABEL_MSG1)) {
- buf_append_uint32(&st->buffer,st->local_capabilities);
- }
+ buf_append_uint32(&st->buffer,st->local_capabilities);
if (type_is_msg34(type)) {
buf_append_uint16(&st->buffer,st->mtu_target);
}
return False;
privkey_found:
+ slog(st,LOG_SIGKEYS,"using private key #%d " SIGKEYID_PR_FMT,
+ ki, SIGKEYID_PR_VAL(prompt->pubkeys_accepted[ki]));
buf_append_uint8(&st->buffer,ki);
}
static void peerkeys_check_for_update(struct site *st)
{
- /* peerkeys files
- *
- * <F> live file, loaded on startup, updated by secnet
- * (only). * in-memory peerkeys_current is kept
- * synced with this file
- *
- * <F>~update update file from config manager, checked before
- * every key exchange. config manager must rename
- * this file into place; it will be renamed and
- * then removed by secnet.
- *
- * <F>~proc update file being processed by secnet.
- * only secnet may write or remove.
- *
- * <F>~incoming update file from peer, being received by secnet
- * may be incomplete, unverified, or even malicious
- * only secnet may write or remove.
- *
- * <F>~tmp update file from config manager, only mss may
- * write or rename
- *
- * secnet discards updates that are not more recent than (by
- * serial) the live file. But it may not process updates
- * immediately.
- *
- * The implied keyset to be used is MAX(live, proc, update).
- *
- * secnet does:
- * check live vs proc, either mv proc live or rm proc
- * if proc doesn't exist, mv update proc
- *
- * make-secnet-sites does:
- * write: rename something onto update
- * read: read update,proc,live in that order and take max
- *
- * We support only one concurrent secnet, one concurrent
- * writing make-secnet-sites, and any number of readers.
- * We want to maintain a live file at all times as that
- * is what secnet actually reads at startup and uses.
- *
- * Proof that this is sound:
- * Let us regard update,proc,live as i=0,1,2
- * Files contain public key sets and are manipulated as
- * a whole, and we may regard key sets with the same
- * serial as equivalent.
- * We talk below about reading as if it were atomic.
- * Actually the atomic operation is open(2); the
- * reading gets whatever that name refers to. So
- * we can model this as an atomic read.
- * secnet eventually moves all data into the live file
- * or deletes it, so there should be no indefinitely
- * stale data; informally this means we can disregard
- * the possibility of very old serials and regard
- * serials as fully ordered. (We don't bother with
- * a formal proof of this property.)
- * Consequently we will only think about the serial
- * and not the contents. We treat absent files as
- * minimal (we will write -1 for convenience although
- * we don't mean a numerical value). We write S(i).
- *
- * Invariant 1 for secnet's transformations is as follows:
- * Each file S(i) is only reduced (to S'(i)) if for some j S'(j)
- * >= S(i), with S'(j) either being >= S(i) beforehand, or
- * updated atomically together with S(i).
- *
- * Proof of invariant 1 for the secnet operations:
- * (a) check live vs proc, proc>live, mv:
- * j=2, i=1; S'(i)=-1, so S(i) is being reduced. S'(j) is
- * equal to S(i), and the rename is atomic [1], so S'(j) and
- * S'(i) are updated simultaneously. S(j) is being
- * increased. (There are no hazards from concurrent writers;
- * only we ourselves (secnet) write to live or proc.)
- * (b) check live vs proc, proc<=live, rm:
- * j=2, i=1; S'(i)=-1, so S(i) is being reduced. But
- * S(j) is >= $(i) throughout. (Again, no concurrent
- * writer hazards.)
- * (c) mv update proc (when proc does not exist):
- * j=1, i=0; S(i) is being reduced to -1. But simultaneously
- * S(j) is being increased to the old S(i). Our precondition
- * (proc not existing) is not subject to a concurrent writer
- * hazards because only we write to proc; our action is
- * atomic and takes whatever update is available (if any).
- *
- * Proof of soundness for the mss reading operation:
- * Let M be MAX(\forall S) at the point where mss reads update.
- * Invariant 2: when mss reads S(k), MAX(K, S(k)..S(2)) >= M,
- * where K is the max S it has seen so far. Clearly this is
- * true for k=0 (with K==-1). secnet's operations never break
- * this invariant because if any S() is reduced, another one
- * counted must be increased. mss's step operation
- * updates K with S(k), so MAX(K', S(k+1)..)=MAX(K, S(k)..),
- * and updates k to k+1, preserving the invariant.
- * At the end we have k=3 and K=>M. Since secnet never
- * invents serials, K=M in the absence of an mss update
- * with a bigger S.
- *
- * Consideration of the mss update operation:
- * Successive serials from sites file updates etc. are supposed
- * to be increasing. When this is true, M is increased. A
- * concurrent reading mss which makes its first read after the
- * update will get the new data (by the proofs above). This
- * seems to be the required property.
- *
- * QED.
- *
- * [1] From "Base Specifications issue 7",
- * 2.9.7 Thread Interactions with Regular File Operations
- * All of the following functions shall be atomic with respect to
- * each other in the effects specified in POSIX.1-2017 when they
- * operate on regular files or symbolic links:
- * ... rename ... open ...
- */
if (!st->peerkeys_path) return;
pathprefix_template_setsuffix(&st->peerkeys_tmpl,"~proc");
int r=rename(inputp,oursp);
if (r) {
- slog(st,LOG_ERROR,"failed to claim key update file %s as %s: %s\n",
+ slog(st,LOG_ERROR,"failed to claim key update file %s as %s: %s",
inputp,oursp,strerror(errno));
return;
}
if (!pubkey->check(pubkey->st,
m->hashstart,m->hashlen,
&m->sig)) {
- slog(st,LOG_SEC,"msg3/msg4 signature failed check!");
+ slog(st,LOG_SEC,"msg3/msg4 signature failed check!"
+ " (key #%d " SIGKEYID_PR_FMT ")",
+ ki, SIGKEYID_PR_VAL(&st->peerkeys_kex->keys[ki].id));
return False;
}
+ slog(st,LOG_SIGKEYS,"verified peer signature with key #%d "
+ SIGKEYID_PR_FMT, ki,
+ SIGKEYID_PR_VAL(&st->peerkeys_kex->keys[ki].id));
st->remote_adv_mtu=m->remote_mtu;
crypto operations, but that's a task for another day. */
}
-static void setup_sethash(struct site *st, dict_t *dict,
- struct hash_if **hash, struct cloc loc,
- sig_sethash_fn *sethash, void *sigkey_st) {
- if (!*hash) *hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc);
- sethash(sigkey_st,*hash);
-}
-#define SETUP_SETHASH(k) do{ \
- if ((k)->sethash) \
- setup_sethash(st,dict, &hash,loc, (k)->sethash,(k)->st); \
-}while(0)
-
static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
list_t *args)
{
cfgfatal(loc,"site","parameter must be a dictionary\n");
dict=item->data.dict;
+ st->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc);
+ st->log_events=string_list_to_word(dict_lookup(dict,"log-events"),
+ log_event_table,"site");
+
st->localname=dict_read_string(dict, "local-name", True, "site", loc);
st->remotename=dict_read_string(dict, "name", True, "site", loc);
+ st->tunname=safe_malloc(strlen(st->localname)+strlen(st->remotename)+5,
+ "site_apply");
+ sprintf(st->tunname,"%s<->%s",st->localname,st->remotename);
+
+ /* Now slog is working */
+
st->keepalive=dict_read_bool(dict,"keepalive",False,"site",loc,False);
st->peer_mobile=dict_read_bool(dict,"mobile",False,"site",loc,False);
}
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);
- struct hash_if *hash=0;
-
st->privkeys=find_cl_if(dict,"key-cache",CL_PRIVCACHE,False,"site",loc);
if (!st->privkeys) {
st->privkey_fixed=
find_cl_if(dict,"local-key",CL_SIGPRIVKEY,True,"site",loc);
- SETUP_SETHASH(st->privkey_fixed);
}
struct sigpubkey_if *fixed_pubkey
}
} else {
assert(fixed_pubkey);
- SETUP_SETHASH(fixed_pubkey);
NEW(st->peerkeys_current);
st->peerkeys_current->refcount=1;
st->peerkeys_current->nkeys=1;
"renegotiate-time must be less than key-lifetime\n");
}
- st->log_events=string_list_to_word(dict_lookup(dict,"log-events"),
- log_event_table,"site");
-
st->resolving_count=0;
st->allow_send_prod=0;
- st->tunname=safe_malloc(strlen(st->localname)+strlen(st->remotename)+5,
- "site_apply");
- sprintf(st->tunname,"%s<->%s",st->localname,st->remotename);
-
/* 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 */
static void transport_peers_expire(struct site *st, transport_peers *peers) {
/* peers must be sorted first */
+ if (st->local_mobile) return;
+
int previous_peers=peers->npeers;
struct timeval oldest;
oldest.tv_sec = tv_now->tv_sec - st->mobile_peer_expiry;