summaryrefslogtreecommitdiff
path: root/src/irmd/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/irmd/main.c')
-rw-r--r--src/irmd/main.c980
1 files changed, 774 insertions, 206 deletions
diff --git a/src/irmd/main.c b/src/irmd/main.c
index cc15078f..daaf4129 100644
--- a/src/irmd/main.c
+++ b/src/irmd/main.c
@@ -40,6 +40,7 @@
#include <ouroboros/lockfile.h>
#include <ouroboros/logs.h>
#include <ouroboros/pthread.h>
+#include <ouroboros/random.h>
#include <ouroboros/rib.h>
#include <ouroboros/shm_rdrbuff.h>
#include <ouroboros/sockets.h>
@@ -50,9 +51,11 @@
#include "irmd.h"
#include "ipcp.h"
+#include "oap.h"
#include "reg/reg.h"
#include "configfile.h"
+#include <dirent.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
@@ -71,15 +74,24 @@
#define SHM_SAN_HOLDOFF 1000 /* ms */
#define IPCP_HASH_LEN(p) hash_len((p)->dir_hash_algo)
#define BIND_TIMEOUT 10 /* ms */
+#define TIMESYNC_SLACK 100 /* ms */
+#define OAP_SEEN_TIMER 20 /* s */
#define DEALLOC_TIME 300 /* s */
-#define MSGBUFSZ 2048
enum irm_state {
IRMD_NULL = 0,
+ IRMD_INIT,
IRMD_RUNNING,
IRMD_SHUTDOWN
};
+struct oaph {
+ struct list_head next;
+
+ uint64_t stamp;
+ uint8_t id[OAP_ID_SIZE];
+};
+
struct cmd {
struct list_head next;
@@ -93,6 +105,12 @@ struct {
#ifdef HAVE_TOML
char * cfg_file; /* configuration file path */
#endif
+ struct {
+ struct auth_ctx * ctx; /* default authentication ctx */
+ struct list_head list; /* OAP headers seen before */
+ pthread_mutex_t mtx; /* mutex for OAP headers */
+ } auth;
+
struct lockfile * lf; /* single irmd per system */
struct shm_rdrbuff * rdrb; /* rdrbuff for packets */
@@ -173,8 +191,11 @@ static pid_t spawn_ipcp(struct ipcp_info * info)
case IPCP_BROADCAST:
exec_name = IPCP_BROADCAST_EXEC;
break;
- case IPCP_UDP:
- exec_name = IPCP_UDP_EXEC;
+ case IPCP_UDP4:
+ exec_name = IPCP_UDP4_EXEC;
+ break;
+ case IPCP_UDP6:
+ exec_name = IPCP_UDP6_EXEC;
break;
case IPCP_ETH_LLC:
exec_name = IPCP_ETH_LLC_EXEC;
@@ -217,7 +238,7 @@ static pid_t spawn_ipcp(struct ipcp_info * info)
}
info->pid = pid;
- info->state = IPCP_BOOT;
+ info->state = IPCP_INIT;
return 0;
}
@@ -312,7 +333,7 @@ int bootstrap_ipcp(pid_t pid,
goto fail;
}
- if (conf->type == IPCP_UDP)
+ if (conf->type == IPCP_UDP4 || conf->type == IPCP_UDP6)
conf->layer_info.dir_hash_algo = (enum pol_dir_hash) HASH_MD5;
if (ipcp_bootstrap(pid, conf, &layer)) {
@@ -320,7 +341,7 @@ int bootstrap_ipcp(pid_t pid,
goto fail;
}
- info.state = IPCP_BOOTSTRAPPED;
+ info.state = IPCP_BOOT;
if (reg_set_layer_for_ipcp(&info, &layer) < 0) {
log_err("Failed to set layer info for IPCP.");
@@ -352,6 +373,8 @@ int enroll_ipcp(pid_t pid,
goto fail;
}
+ info.state = IPCP_BOOT;
+
if (reg_set_layer_for_ipcp(&info, &layer) < 0) {
log_err("Failed to set layer info for IPCP.");
goto fail;
@@ -425,6 +448,71 @@ static int disconnect_ipcp(pid_t pid,
return 0;
}
+static void name_update_sec_paths(struct name_info * info)
+{
+ char * srv_dir = OUROBOROS_SRV_CRT_DIR;
+ char * cli_dir = OUROBOROS_CLI_CRT_DIR;
+
+ assert(info != NULL);
+
+ if (strlen(info->s.enc) == 0)
+ sprintf(info->s.enc, "%s/%s/enc.cfg", srv_dir, info->name);
+
+ if (strlen(info->s.crt) == 0)
+ sprintf(info->s.crt, "%s/%s/crt.pem", srv_dir, info->name);
+
+ if (strlen(info->s.key) == 0)
+ sprintf(info->s.key, "%s/%s/key.pem", srv_dir, info->name);
+
+ if (strlen(info->c.enc) == 0)
+ sprintf(info->c.enc, "%s/%s/enc.cfg", cli_dir, info->name);
+
+ if (strlen(info->c.crt) == 0)
+ sprintf(info->c.crt, "%s/%s/crt.pem", cli_dir, info->name);
+
+ if (strlen(info->c.key) == 0)
+ sprintf(info->c.key, "%s/%s/key.pem", cli_dir, info->name);
+}
+
+int name_create(struct name_info * info)
+{
+ int ret;
+
+ assert(info != NULL);
+
+ name_update_sec_paths(info);
+
+ ret = reg_create_name(info);
+ if (ret == -EEXIST) {
+ log_info("Name %s already exists.", info->name);
+ return 0;
+ }
+
+ if (ret < 0) {
+ log_err("Failed to create name %s.", info->name);
+ return -1;
+ }
+
+ log_info("Created new name: %s.", info->name);
+
+ return 0;
+}
+
+static int name_destroy(const char * name)
+{
+
+ assert(name != NULL);
+
+ if (reg_destroy_name(name) < 0) {
+ log_err("Failed to destroy name %s.", name);
+ return -1;
+ }
+
+ log_info("Destroyed name: %s.", name);
+
+ return 0;
+}
+
int bind_program(char ** exec,
const char * name,
uint8_t flags)
@@ -448,10 +536,8 @@ int bind_program(char ** exec,
if (!reg_has_name(name)) {
ni.pol_lb = LB_SPILL;
strcpy(ni.name, name);
- if (reg_create_name(&ni) < 0) {
- log_err("Failed to create name %s.", name);
+ if (name_create(&ni) < 0)
goto fail_name;
- }
}
if (reg_bind_prog(name, exec, flags) < 0) {
@@ -497,10 +583,8 @@ int bind_process(pid_t pid,
if (!reg_has_name(name)) {
ni.pol_lb = LB_SPILL;
strcpy(ni.name, name);
- if (reg_create_name(&ni) < 0) {
- log_err("Failed to create name %s.", name);
+ if (name_create(&ni) < 0)
goto fail;
- }
}
if (reg_bind_proc(name, pid) < 0) {
@@ -581,43 +665,6 @@ static int list_ipcps(ipcp_list_msg_t *** ipcps,
return -1;
}
-int name_create(const struct name_info * info)
-{
- int ret;
-
- assert(info != NULL);
-
- ret = reg_create_name(info);
- if (ret == -EEXIST) {
- log_info("Name %s already exists.", info->name);
- return 0;
- }
-
- if (ret < 0) {
- log_err("Failed to create name %s.", info->name);
- return -1;
- }
-
- log_info("Created new name: %s.", info->name);
-
- return 0;
-}
-
-static int name_destroy(const char * name)
-{
-
- assert(name != NULL);
-
- if (reg_destroy_name(name) < 0) {
- log_err("Failed to destroy name %s.", name);
- return -1;
- }
-
- log_info("Destroyed name: %s.", name);
-
- return 0;
-}
-
static int list_names(name_info_msg_t *** names,
size_t * n_names)
{
@@ -760,67 +807,279 @@ static void __cleanup_flow(void * flow)
reg_destroy_flow(((struct flow_info *) flow)->id);
}
+static bool file_exists(const char * path)
+{
+ struct stat s;
+
+ if (stat(path, &s) < 0 && errno == ENOENT) {
+ log_dbg("File %s does not exist.", path);
+ return false;
+ }
+
+ return true;
+}
+
+static int load_credentials(const char * name,
+ const struct name_sec_paths * paths,
+ void ** pkp,
+ void ** crt,
+ bool * crypt)
+{
+ assert(paths != NULL);
+ assert(pkp != NULL);
+ assert(crt != NULL);
+
+ *pkp = NULL;
+ *crt = NULL;
+
+ /* TODO: Allow configuration. For now, encrypt if path exists */
+ *crypt = file_exists(paths->enc);
+ if (*crypt)
+ log_info("Encryption enabled for %s.", name);
+
+ if (!file_exists(paths->crt) || !file_exists(paths->key)) {
+ log_info("No security info for %s.", name);
+ return 0;
+ }
+
+ if (crypt_load_crt_file(paths->crt, crt) < 0) {
+ log_err("Failed to load %s for %s.", paths->crt, name);
+ goto fail_crt;
+ }
+
+ if (crypt_load_privkey_file(paths->key, pkp) < 0) {
+ log_err("Failed to load %s for %s.", paths->key, name);
+ goto fail_key;
+ }
+
+ log_info("Loaded security keys for %s.", name);
+
+ return 0;
+
+ fail_key:
+ crypt_free_crt(*crt);
+ *crt = NULL;
+ fail_crt:
+ return -EAUTH;
+}
+
+static int load_srv_credentials(const char * name,
+ void ** pkp,
+ void ** crt,
+ bool * crypt)
+{
+ struct name_info info;
+
+ assert(name != NULL);
+ assert(pkp != NULL);
+ assert(crt != NULL);
+
+ if (reg_get_name_info(name, &info) < 0) {
+ log_err("Failed to get name info for %s.", name);
+ return -ENAME;
+ }
+
+ return load_credentials(name, &info.s, pkp, crt, crypt);
+}
+
+static int load_cli_credentials(const char * name,
+ void ** pkp,
+ void ** crt,
+ bool * crypt)
+{
+ struct name_info info;
+
+ assert(name != NULL);
+ assert(pkp != NULL);
+ assert(crt != NULL);
+
+ if (reg_get_name_info(name, &info) < 0) {
+ log_err("Failed to get name info for %s.", name);
+ return -ENAME;
+ }
+
+ return load_credentials(name, &info.c, pkp, crt, crypt);
+}
+
+#define ID_IS_EQUAL(id1, id2) (memcmp(id1, id2, OAP_ID_SIZE) == 0)
+static int irm_check_oap_hdr(const struct oap_hdr * oap_hdr,
+ time_t mpl)
+{
+ struct list_head * p;
+ struct list_head * h;
+ struct timespec now;
+ struct oaph * new;
+ uint64_t stamp;
+ uint64_t cur;
+ uint8_t * id;
+ ssize_t delta;
+
+ assert(oap_hdr != NULL);
+
+ stamp = oap_hdr->timestamp;
+ id = oap_hdr->id.data;
+
+ clock_gettime(CLOCK_REALTIME, &now);
+
+ cur = TS_TO_UINT64(now);
+
+ delta = (ssize_t)(cur - stamp) / MILLION;
+ if (delta > mpl)
+ log_warn("Transit time exceeds MPL by %zd ms.", delta);
+ if (delta < -TIMESYNC_SLACK)
+ log_warn("OAP header sent %zd ms from the future.", -delta);
+
+ new = malloc(sizeof(*new));
+ if (new == NULL) {
+ log_err("Failed to allocate memory for OAP element.");
+ return -ENOMEM;
+ }
+
+ pthread_mutex_lock(&irmd.auth.mtx);
+
+ list_for_each_safe(p, h, &irmd.auth.list) {
+ struct oaph * oaph = list_entry(p, struct oaph, next);
+ if (cur > oaph->stamp + OAP_SEEN_TIMER * BILLION) {
+ list_del(&oaph->next);
+ free(oaph);
+ continue;
+ }
+
+ if (oaph->stamp == stamp && ID_IS_EQUAL(oaph->id, id)) {
+ log_warn("OAP header already known: " HASH_FMT64 ".",
+ HASH_VAL64(id));
+ goto fail_replay;
+ }
+ }
+
+ memcpy(new->id, id, OAP_ID_SIZE);
+ new->stamp = stamp;
+
+ list_add_tail(&new->next, &irmd.auth.list);
+
+ pthread_mutex_unlock(&irmd.auth.mtx);
+
+ return 0;
+
+ fail_replay:
+ pthread_mutex_unlock(&irmd.auth.mtx);
+ free(new);
+ return -EAUTH;
+}
+
+static int irm_auth_peer(const char * name,
+ const struct oap_hdr * oap_hdr,
+ const struct oap_hdr * r_oap_hdr)
+{
+ void * crt;
+ void * pk;
+ buffer_t sign;
+ const char * n = name == NULL ? "<client>" : name;
+
+ if (memcmp(r_oap_hdr->id.data, oap_hdr->id.data, OAP_ID_SIZE) != 0) {
+ log_err("OAP ID mismatch in flow allocation.");
+ goto fail_check;
+ }
+
+ if (r_oap_hdr->crt.len == 0) {
+ log_info("No certificate provided by %s.", n);
+ return 0;
+ }
+
+ if (crypt_load_crt_der(r_oap_hdr->crt, &crt) < 0) {
+ log_err("Failed to load certificate from %s.", n);
+ goto fail_check;
+ }
+
+ log_dbg("Loaded peer certificate for %s.", n);
+
+ if (name != NULL) {
+ if (crypt_check_crt_name(crt, n) < 0) {
+ log_err("Certificate does not match %s.", n);
+ goto fail_crt;
+ }
+ log_dbg("Certificate matches name %s.", n);
+ }
+
+ if (crypt_get_pubkey_crt(crt, &pk) < 0) {
+ log_err("Failed to get pubkey from certificate for %s.", n);
+ goto fail_crt;
+ }
+
+ log_dbg("Got public key from certificate for %s.", n);
+
+ if (auth_verify_crt(irmd.auth.ctx, crt) < 0) {
+ log_err("Failed to verify peer %s with CA store.", n);
+ goto fail_crt;
+ }
+
+ log_info("Successfully verified peer certificate for %s.", n);
+
+ sign = r_oap_hdr->hdr;
+ sign.len -= (r_oap_hdr->sig.len + sizeof(uint16_t));
+
+ if (auth_verify_sig(pk, sign, r_oap_hdr->sig) < 0) {
+ log_err("Failed to verify signature for peer %s.", n);
+ goto fail_check_sig;
+ }
+
+ crypt_free_key(pk);
+ crypt_free_crt(crt);
+
+ log_info("Successfully authenticated %s.", n);
+
+ return 0;
+
+ fail_check_sig:
+ crypt_free_key(pk);
+ fail_crt:
+ crypt_free_crt(crt);
+ fail_check:
+ return -1;
+}
+
static int flow_accept(struct flow_info * flow,
+ buffer_t * symmkey,
buffer_t * data,
struct timespec * abstime)
{
- uint8_t buf[MSGBUFSZ];
- buffer_t lpk; /* local public key */
- buffer_t rpk; /* remote public key */
- void * pkp; /* my public/private key pair */
- ssize_t key_len;
- uint8_t * s;
- int err;
+ struct oap_hdr oap_hdr; /* incoming request */
+ struct oap_hdr r_oap_hdr; /* outgoing response */
+ uint8_t buf[MSGBUFSZ]; /* buffer for local ephkey */
+ buffer_t lpk = BUF_INIT; /* local ephemeral pubkey */
+ char name[NAME_SIZE + 1]; /* name for flow */
+ void * pkp = NULL; /* signing private key */
+ void * crt = NULL; /* signing certificate */
+ int err;
+ bool crypt;
/* piggyback of user data not yet implemented */
- assert(data != NULL && data->len == 0 && data->data == NULL);
+ assert(data != NULL && BUF_IS_EMPTY(data));
+ assert(symmkey != NULL && BUF_IS_EMPTY(symmkey));
if (!reg_has_proc(flow->n_pid)) {
log_err("Unknown process %d calling accept.", flow->n_pid);
err = -EINVAL;
- goto fail;
- }
-
- s = malloc(SYMMKEYSZ);
- if (s == NULL) {
- log_err("Failed to malloc symmkey.");
- err = -ENOMEM;
- goto fail;
- }
-
- key_len = crypt_dh_pkp_create(&pkp, buf);
- if (key_len < 0) {
- log_err("Failed to generate key pair.");
- err = -ECRYPT;
- goto fail_pkp;
+ goto fail_flow;
}
- lpk.data = buf;
- lpk.len = (size_t) key_len;
-
- log_dbg("Generated ephemeral keys for %d.", flow->n_pid);
-
if (reg_create_flow(flow) < 0) {
log_err("Failed to create flow.");
err = -EBADF;
goto fail_flow;
}
- if (reg_prepare_flow_accept(flow, &lpk) < 0) {
+ if (reg_prepare_flow_accept(flow) < 0) {
log_err("Failed to prepare accept.");
err = -EBADF;
goto fail_wait;
}
pthread_cleanup_push(__cleanup_flow, flow);
- pthread_cleanup_push(__cleanup_pkp, pkp);
- pthread_cleanup_push(free, s);
- err = reg_wait_flow_accepted(flow, &rpk, abstime);
+ err = reg_wait_flow_accepted(flow, &oap_hdr.hdr, abstime);
pthread_cleanup_pop(false);
- pthread_cleanup_pop(false);
- pthread_cleanup_pop(false);
if (err == -ETIMEDOUT) {
log_err("Flow accept timed out.");
@@ -835,45 +1094,135 @@ static int flow_accept(struct flow_info * flow,
assert(err == 0);
- if (flow->qs.cypher_s != 0) { /* crypto requested */
- if (crypt_dh_derive(pkp, rpk, s) < 0) {
+ if (reg_get_name_for_flow_id(name, flow->id) < 0) {
+ log_err("Failed to get name for flow %d.", flow->id);
+ err = -EIPCP;
+ goto fail_cred;
+ }
+
+ log_dbg("IPCP %d accepting flow %d for %s.",
+ flow->n_pid, flow->id, name);
+
+ if (load_srv_credentials(name, &pkp, &crt, &crypt) < 0) {
+ log_err("Failed to load security keys for %s.", name);
+ err = -EAUTH;
+ goto fail_cred;
+ }
+
+ if (oap_hdr_decode(oap_hdr.hdr, &oap_hdr) < 0) {
+ log_err("Failed to decode OAP header from %s.", name);
+ err = -EIPCP;
+ goto fail_oap_hdr;
+ }
+#ifdef DEBUG_PROTO_OAP
+ debug_oap_hdr_rcv(&oap_hdr);
+#endif
+ if (irm_check_oap_hdr(&oap_hdr, flow->mpl) < 0) {
+ log_err("OAP header failed replay check.");
+ goto fail_oap_hdr;
+ }
+
+ if (crypt && oap_hdr.eph.len == 0) {
+ log_warn("Encryption required but no key provided.");
+ err = -ECRYPT;
+ goto fail_oap_hdr;
+ }
+
+ if (oap_hdr.eph.len > 0) { /* crypto requested */
+ uint8_t * s; /* symmetric encryption key */
+ ssize_t key_len; /* length of local pubkey */
+ void * pkp = NULL; /* ephemeral private key pair */
+
+ s = malloc(SYMMKEYSZ);
+ if (s == NULL) {
+ log_err("Failed to malloc symmkey.");
+ err = -ENOMEM;
+ goto fail_keys;
+ }
+
+ key_len = crypt_dh_pkp_create(&pkp, buf);
+ if (key_len < 0) {
+ free(s);
+ log_err("Failed to generate key pair.");
+ err = -ECRYPT;
+ goto fail_keys;
+ }
+
+ lpk.data = buf;
+ lpk.len = (size_t) key_len;
+
+ log_dbg("Generated ephemeral keys for %d.", flow->n_pid);
+
+ if (crypt_dh_derive(pkp, oap_hdr.eph, s) < 0) {
log_err("Failed to derive secret for %d.", flow->id);
+ crypt_dh_pkp_destroy(pkp);
+ free(s);
err = -ECRYPT;
goto fail_derive;
}
- freebuf(rpk);
- data->data = s;
- data->len = SYMMKEYSZ;
- s= NULL;
- } else {
- clrbuf(lpk);
+
+ symmkey->data = s;
+ symmkey->len = SYMMKEYSZ;
+
+ crypt_dh_pkp_destroy(pkp);
}
- if (ipcp_flow_alloc_resp(flow, 0, lpk) < 0) {
+ if (oap_hdr_init(oap_hdr.id, pkp, crt, lpk, *data, &r_oap_hdr) < 0) {
+ log_err("Failed to create OAP header.");
+ err = -ENOMEM;
+ goto fail_r_oap_hdr;
+ }
+
+ if (irm_auth_peer(NULL, &r_oap_hdr, &oap_hdr) < 0) {
+ log_err("Failed to auth %s client, flow %d.", name, flow->id);
+ err = -EAUTH;
+ goto fail_r_oap_hdr;
+ }
+
+ crypt_free_crt(crt);
+ crypt_free_key(pkp);
+
+#ifdef DEBUG_PROTO_OAP
+ debug_oap_hdr_snd(&r_oap_hdr);
+#endif
+ if (ipcp_flow_alloc_resp(flow, 0, r_oap_hdr.hdr) < 0) {
log_err("Failed to respond to flow allocation.");
- err = -EIPCP;
- goto fail_alloc_resp;
+ goto fail_resp;
}
- crypt_dh_pkp_destroy(pkp);
- free(s);
+ log_info("Flow %d accepted by %d for %s.",
+ flow->id, flow->n_pid, name);
+
+ oap_hdr_fini(&oap_hdr);
+ oap_hdr_fini(&r_oap_hdr);
return 0;
+ fail_r_oap_hdr:
+ freebuf(*symmkey);
fail_derive:
- freebuf(rpk);
clrbuf(lpk);
+ fail_keys:
+ oap_hdr_fini(&oap_hdr);
+ fail_oap_hdr:
+ crypt_free_crt(crt);
+ crypt_free_key(pkp);
+ fail_cred:
+ assert(lpk.data == NULL && lpk.len == 0);
ipcp_flow_alloc_resp(flow, err, lpk);
- fail_alloc_resp:
- flow->state = FLOW_NULL;
fail_wait:
reg_destroy_flow(flow->id);
fail_flow:
- crypt_dh_pkp_destroy(pkp);
- fail_pkp:
- free(s);
- fail:
return err;
+
+ fail_resp:
+ flow->state = FLOW_NULL;
+ oap_hdr_fini(&r_oap_hdr);
+ freebuf(*symmkey);
+ clrbuf(lpk);
+ oap_hdr_fini(&oap_hdr);
+ reg_destroy_flow(flow->id);
+ return -EIPCP;
}
static int flow_join(struct flow_info * flow,
@@ -883,7 +1232,7 @@ static int flow_join(struct flow_info * flow,
struct ipcp_info ipcp;
struct layer_info layer;
buffer_t hash;
- buffer_t pbuf = {NULL, 0}; /* nothing to piggyback */
+ buffer_t pbuf = BUF_INIT; /* nothing to piggyback */
int err;
log_info("Allocating flow for %d to %s.", flow->n_pid, dst);
@@ -901,6 +1250,8 @@ static int flow_join(struct flow_info * flow,
goto fail_ipcp;
}
+ flow->n_1_pid = ipcp.pid;
+
hash.len = hash_len((enum hash_algo) layer.dir_hash_algo);
hash.data = malloc(hash.len);
if (hash.data == NULL) {
@@ -909,6 +1260,8 @@ static int flow_join(struct flow_info * flow,
goto fail_ipcp;
}
+ str_hash((enum hash_algo) layer.dir_hash_algo, hash.data, dst);
+
reg_prepare_flow_alloc(flow);
if (ipcp_flow_join(flow, hash)) {
@@ -936,6 +1289,7 @@ static int flow_join(struct flow_info * flow,
goto fail_alloc;
}
+ assert(pbuf.data == NULL && pbuf.len == 0);
assert(err == 0);
freebuf(hash);
@@ -1009,23 +1363,46 @@ static int get_ipcp_by_dst(const char * dst,
static int flow_alloc(struct flow_info * flow,
const char * dst,
+ buffer_t * symmkey,
buffer_t * data,
struct timespec * abstime)
{
- uint8_t buf[MSGBUFSZ];
- buffer_t lpk ={NULL, 0}; /* local public key */
- buffer_t rpk; /* remote public key */
- void * pkp = NULL; /* my public/private key pair */
- uint8_t * s = NULL;
- buffer_t hash;
- int err;
+ struct oap_hdr oap_hdr; /* outgoing request */
+ struct oap_hdr r_oap_hdr; /* incoming response */
+ uint8_t buf[MSGBUFSZ]; /* buffer for local ephkey */
+ buffer_t lpk = BUF_INIT; /* local ephemeral pubkey */
+ void * pkp = NULL; /* ephemeral private key pair */
+ uint8_t * s = NULL; /* symmetric key */
+ void * cpkp = NULL; /* signing private key */
+ void * ccrt = NULL; /* signing certificate */
+ buffer_t hash;
+ uint8_t idbuf[OAP_ID_SIZE];
+ buffer_t id;
+ int err;
+ bool crypt;
+
/* piggyback of user data not yet implemented */
- assert(data != NULL && data->len == 0 && data->data == NULL);
+ assert(data != NULL && BUF_IS_EMPTY(data));
+ assert(symmkey != NULL && BUF_IS_EMPTY(symmkey));
log_info("Allocating flow for %d to %s.", flow->n_pid, dst);
+ if (random_buffer(idbuf, OAP_ID_SIZE) < 0) {
+ log_err("Failed to generate ID.");
+ err = -EIRMD;
+ goto fail_id;
+ }
+
+ id.data = idbuf;
+ id.len = OAP_ID_SIZE;
- if (flow->qs.cypher_s > 0) {
+ if (load_cli_credentials(dst, &cpkp, &ccrt, &crypt) < 0) {
+ log_err("Failed to load security keys for %s.", dst);
+ err = -EAUTH;
+ goto fail_cred;
+ }
+
+ if (crypt > 0) {
ssize_t key_len;
s = malloc(SYMMKEYSZ);
@@ -1048,6 +1425,14 @@ static int flow_alloc(struct flow_info * flow,
log_dbg("Generated ephemeral keys for %d.", flow->n_pid);
}
+ if (oap_hdr_init(id, cpkp, ccrt, lpk, *data, &oap_hdr) < 0) {
+ log_err("Failed to create OAP header.");
+ err = -ENOMEM;
+ goto fail_oap_hdr;
+ }
+#ifdef DEBUG_PROTO_OAP
+ debug_oap_hdr_snd(&oap_hdr);
+#endif
if (reg_create_flow(flow) < 0) {
log_err("Failed to create flow.");
err = -EBADF;
@@ -1062,7 +1447,7 @@ static int flow_alloc(struct flow_info * flow,
reg_prepare_flow_alloc(flow);
- if (ipcp_flow_alloc(flow, hash, lpk)) {
+ if (ipcp_flow_alloc(flow, hash, oap_hdr.hdr)) {
log_err("Flow allocation %d failed.", flow->id);
err = -ENOTALLOC;
goto fail_alloc;
@@ -1073,7 +1458,7 @@ static int flow_alloc(struct flow_info * flow,
pthread_cleanup_push(free, hash.data);
pthread_cleanup_push(free, s);
- err = reg_wait_flow_allocated(flow, &rpk, abstime);
+ err = reg_wait_flow_allocated(flow, &r_oap_hdr.hdr, abstime);
pthread_cleanup_pop(false);
pthread_cleanup_pop(false);
@@ -1087,48 +1472,86 @@ static int flow_alloc(struct flow_info * flow,
if (err == -1) {
log_dbg("Flow allocation terminated.");
- err = -EPIPE;
+ err = -EIPCP;
goto fail_alloc;
}
- assert(err == 0);
+ log_dbg("Response received for flow %d to %s.", flow->id, dst);
+
+ if (err < 0) {
+ log_warn("Flow allocation rejected for %s: %d.", dst, err);
+ goto fail_alloc;
+ }
- if (flow->qs.cypher_s != 0) { /* crypto requested */
- if (crypt_dh_derive(pkp, rpk, s) < 0) {
+ if (oap_hdr_decode(r_oap_hdr.hdr, &r_oap_hdr) < 0) {
+ log_err("Failed to decode OAP header.");
+ err = -EIPCP;
+ goto fail_r_oap_hdr;
+ }
+#ifdef DEBUG_PROTO_OAP
+ debug_oap_hdr_rcv(&r_oap_hdr);
+#endif
+ if (irm_check_oap_hdr(&r_oap_hdr, flow->mpl) < 0) {
+ log_err("OAP header failed replay check.");
+ err = -EAUTH;
+ goto fail_r_oap_hdr;
+ }
+
+ if (irm_auth_peer(dst, &oap_hdr, &r_oap_hdr) < 0) {
+ log_err("Failed to authenticate %s (flow %d).", dst, flow->id);
+ err = -EAUTH;
+ goto fail_r_oap_hdr;
+ }
+
+ if (lpk.len > 0) { /* crypto requested */
+ if (crypt_dh_derive(pkp, r_oap_hdr.eph, s) < 0) {
log_err("Failed to derive secret for %d.", flow->id);
err = -ECRYPT;
- goto fail_derive;
+ goto fail_r_oap_hdr;
}
crypt_dh_pkp_destroy(pkp);
- freebuf(rpk);
- data->data = s;
- data->len = SYMMKEYSZ;
+
+ symmkey->data = s;
+ symmkey->len = SYMMKEYSZ;
s = NULL;
}
+ oap_hdr_fini(&r_oap_hdr);
+ oap_hdr_fini(&oap_hdr);
+
+ crypt_free_crt(ccrt);
+ crypt_free_key(cpkp);
+
+ /* TODO: piggyback user data if needed */
+
freebuf(hash);
free(s);
return 0;
- fail_derive:
- freebuf(rpk);
+ fail_r_oap_hdr:
flow->state = FLOW_DEALLOCATED;
+ oap_hdr_fini(&r_oap_hdr);
fail_alloc:
freebuf(hash);
fail_ipcp:
reg_destroy_flow(flow->id);
fail_flow:
- if (flow->qs.cypher_s > 0)
- crypt_dh_pkp_destroy(pkp);
+ oap_hdr_fini(&oap_hdr);
+ fail_oap_hdr:
+ crypt_dh_pkp_destroy(pkp);
fail_pkp:
free(s);
fail_malloc:
+ crypt_free_crt(ccrt);
+ crypt_free_key(cpkp);
+ fail_cred:
+ clrbuf(id);
+ fail_id:
return err;
}
-static int wait_for_accept(enum hash_algo algo,
- const uint8_t * hash)
+static int wait_for_accept(const char * name)
{
struct timespec timeo = TIMESPEC_INIT_MS(IRMD_REQ_ARR_TIMEOUT);
struct timespec abstime;
@@ -1138,25 +1561,23 @@ static int wait_for_accept(enum hash_algo algo,
clock_gettime(PTHREAD_COND_CLOCK, &abstime);
ts_add(&abstime, &timeo, &abstime);
- ret = reg_wait_flow_accepting(algo, hash, &abstime);
+ ret = reg_wait_flow_accepting(name, &abstime);
if (ret == -ETIMEDOUT) {
- if (reg_get_exec(algo, hash, &exec) < 0) {
- log_dbg("No program bound to " HASH_FMT32 ".",
- HASH_VAL32(hash));
+ if (reg_get_exec(name, &exec) < 0) {
+ log_dbg("No program bound for %s.", name);
goto fail;
}
- log_info("Autostarting %s.", exec[0]);
-
if (spawn_program(exec) < 0) {
- log_dbg("Failed to autostart " HASH_FMT32 ".",
- HASH_VAL32(hash));
+ log_err("Failed to start %s for %s.", exec[0], name);
goto fail_spawn;
}
+ log_info("Starting %s for %s.", exec[0], name);
+
ts_add(&abstime, &timeo, &abstime);
- ret = reg_wait_flow_accepting(algo, hash, &abstime);
+ ret = reg_wait_flow_accepting(name, &abstime);
if (ret == -ETIMEDOUT)
goto fail_spawn;
@@ -1179,10 +1600,11 @@ static int flow_req_arr(struct flow_info * flow,
struct layer_info layer;
enum hash_algo algo;
int ret;
+ char name[NAME_SIZE + 1];
info.pid = flow->n_1_pid;
- log_info("Flow req arrived from IPCP %d for " HASH_FMT32 ".",
+ log_dbg("Flow req arrived from IPCP %d for " HASH_FMT32 ".",
info.pid, HASH_VAL32(hash));
if (reg_get_ipcp(&info, &layer) < 0) {
@@ -1193,10 +1615,17 @@ static int flow_req_arr(struct flow_info * flow,
algo = (enum hash_algo) layer.dir_hash_algo;
- ret = wait_for_accept(algo, hash);
+ if (reg_get_name_for_hash(name, algo, hash) < 0) {
+ log_warn("No name for " HASH_FMT32 ".", HASH_VAL32(hash));
+ ret = -ENAME;
+ goto fail;
+ }
+
+ log_info("Flow request arrived for %s.", name);
+
+ ret = wait_for_accept(name);
if (ret < 0) {
- log_err("No activeprocess for " HASH_FMT32 ".",
- HASH_VAL32(hash));
+ log_err("No active process for %s.", name);
goto fail;
}
@@ -1218,9 +1647,9 @@ static int flow_alloc_reply(struct flow_info * flow,
int response,
buffer_t * data)
{
- flow->state = response ? FLOW_DEALLOCATED : FLOW_ALLOCATED;
+ flow->state = response != 0 ? FLOW_DEALLOCATED : FLOW_ALLOCATED;
- if (reg_respond_alloc(flow, data) < 0) {
+ if (reg_respond_alloc(flow, data, response) < 0) {
log_err("Failed to reply to flow %d.", flow->id);
flow->state = FLOW_DEALLOCATED;
return -EBADF;
@@ -1308,7 +1737,7 @@ static void * acceptloop(void * o)
return (void *) 0;
}
-static void free_msg(void * o)
+static void __cleanup_irm_msg(void * o)
{
irm_msg__free_unpacked((irm_msg_t *) o, NULL);
}
@@ -1327,6 +1756,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
int res;
irm_msg_t * ret_msg;
buffer_t data;
+ buffer_t symmkey = BUF_INIT;;
memset(&flow, 0, sizeof(flow));
@@ -1351,7 +1781,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
ret_msg->code = IRM_MSG_CODE__IRM_REPLY;
- pthread_cleanup_push(free_msg, ret_msg);
+ pthread_cleanup_push(__cleanup_irm_msg, ret_msg);
switch (msg->code) {
case IRM_MSG_CODE__IRM_CREATE_IPCP:
@@ -1380,7 +1810,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
res = disconnect_ipcp(msg->pid, msg->dst, msg->comp);
break;
case IRM_MSG_CODE__IRM_BIND_PROGRAM:
- /* Make exec NULL terminated instead of empty string terminated */
+ /* Terminate with NULL instead of "" */
free(msg->exec[msg->n_exec - 1]);
msg->exec[msg->n_exec - 1] = NULL;
res = bind_program(msg->exec, msg->name, msg->opts);
@@ -1406,8 +1836,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
res = list_ipcps(&ret_msg->ipcps, &ret_msg->n_ipcps);
break;
case IRM_MSG_CODE__IRM_CREATE_NAME:
- strcpy(name.name, msg->names[0]->name);
- name.pol_lb = msg->names[0]->pol_lb;
+ name = name_info_msg_to_s(msg->name_info);
res = name_create(&name);
break;
case IRM_MSG_CODE__IRM_DESTROY_NAME:
@@ -1423,17 +1852,21 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
res = name_unreg(msg->name, msg->pid);
break;
case IRM_MSG_CODE__IRM_FLOW_ACCEPT:
+ tpm_wait_work(irmd.tpm);
data.len = msg->pk.len;
data.data = msg->pk.data;
msg->has_pk = false;
assert(data.len > 0 ? data.data != NULL : data.data == NULL);
flow = flow_info_msg_to_s(msg->flow_info);
- res = flow_accept(&flow, &data, abstime);
+ res = flow_accept(&flow, &symmkey, &data, abstime);
if (res == 0) {
ret_msg->flow_info = flow_info_s_to_msg(&flow);
- ret_msg->has_symmkey = data.len != 0;
- ret_msg->symmkey.data = data.data;
- ret_msg->symmkey.len = data.len;
+ ret_msg->has_symmkey = symmkey.len != 0;
+ ret_msg->symmkey.data = symmkey.data;
+ ret_msg->symmkey.len = symmkey.len;
+ ret_msg->has_pk = data.len != 0;
+ ret_msg->pk.data = data.data;
+ ret_msg->pk.len = data.len;
}
break;
case IRM_MSG_CODE__IRM_FLOW_ALLOC:
@@ -1443,12 +1876,15 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
assert(data.len > 0 ? data.data != NULL : data.data == NULL);
flow = flow_info_msg_to_s(msg->flow_info);
abstime = abstime == NULL ? &max : abstime;
- res = flow_alloc(&flow, msg->dst, &data, abstime);
+ res = flow_alloc(&flow, msg->dst, &symmkey, &data, abstime);
if (res == 0) {
ret_msg->flow_info = flow_info_s_to_msg(&flow);
- ret_msg->has_symmkey = data.len != 0;
- ret_msg->symmkey.data = data.data;
- ret_msg->symmkey.len = data.len;
+ ret_msg->has_symmkey = symmkey.len != 0;
+ ret_msg->symmkey.data = symmkey.data;
+ ret_msg->symmkey.len = symmkey.len;
+ ret_msg->has_pk = data.len != 0;
+ ret_msg->pk.data = data.data;
+ ret_msg->pk.len = data.len;
}
break;
case IRM_MSG_CODE__IRM_FLOW_JOIN:
@@ -1457,7 +1893,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
abstime = abstime == NULL ? &max : abstime;
res = flow_join(&flow, msg->dst, abstime);
if (res == 0)
- ret_msg->flow_info = flow_info_s_to_msg(&flow);
+ ret_msg->flow_info = flow_info_s_to_msg(&flow);
break;
case IRM_MSG_CODE__IRM_FLOW_DEALLOC:
flow = flow_info_msg_to_s(msg->flow_info);
@@ -1540,10 +1976,10 @@ static void * mainloop(void * o)
continue;
}
- tpm_dec(irmd.tpm);
+ tpm_begin_work(irmd.tpm);
pthread_cleanup_push(__cleanup_close_ptr, &sfd);
- pthread_cleanup_push(free_msg, msg);
+ pthread_cleanup_push(__cleanup_irm_msg, msg);
ret_msg = do_command_msg(msg);
@@ -1556,12 +1992,12 @@ static void * mainloop(void * o)
}
if (ret_msg->result == -EPIPE) {
- log_dbg("Terminated command: application closed socket.");
+ log_dbg("Terminated command: remote closed socket.");
goto fail;
}
if (ret_msg->result == -EIRMD) {
- log_dbg("Terminated command: IRMd not in running state.");
+ log_dbg("Terminated command: IRMd not running.");
goto fail;
}
@@ -1596,54 +2032,20 @@ static void * mainloop(void * o)
pthread_cleanup_pop(true);
pthread_cleanup_pop(true);
- tpm_inc(irmd.tpm);
+ tpm_end_work(irmd.tpm);
continue;
fail:
irm_msg__free_unpacked(ret_msg, NULL);
fail_msg:
close(sfd);
- tpm_inc(irmd.tpm);
+ tpm_end_work(irmd.tpm);
continue;
}
return (void *) 0;
}
-static void irm_fini(void)
-{
-#ifdef HAVE_FUSE
- struct timespec wait = TIMESPEC_INIT_MS(1);
- int retries = 5;
-#endif
- if (irmd_get_state() != IRMD_NULL)
- log_warn("Unsafe destroy.");
-
- tpm_destroy(irmd.tpm);
-
- close(irmd.sockfd);
-
- if (unlink(IRM_SOCK_PATH))
- log_dbg("Failed to unlink %s.", IRM_SOCK_PATH);
-
- if (irmd.rdrb != NULL)
- shm_rdrbuff_destroy(irmd.rdrb);
-
- if (irmd.lf != NULL)
- lockfile_destroy(irmd.lf);
-
- pthread_mutex_destroy(&irmd.cmd_lock);
- pthread_cond_destroy(&irmd.cmd_cond);
- pthread_rwlock_destroy(&irmd.state_lock);
-
-#ifdef HAVE_FUSE
- while (rmdir(FUSE_PREFIX) < 0 && retries-- > 0)
- nanosleep(&wait, NULL);
- if (retries < 0)
- log_err("Failed to remove " FUSE_PREFIX);
-#endif
-}
-
#ifdef HAVE_FUSE
static void destroy_mount(char * mnt)
{
@@ -1719,6 +2121,79 @@ void * irm_sanitize(void * o)
return (void *) 0;
}
+static int irm_load_store(char * dpath)
+{
+ struct stat st;
+ struct dirent * dent;
+ DIR * dir;
+ void * crt;
+
+ if (stat(dpath, &st) == -1) {
+ log_dbg("Store directory %s not found.", dpath);
+ return 0;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ log_err("%s is not a directory.", dpath);
+ goto fail_dir;
+ }
+
+ /* loop through files in directory and load certificates */
+ dir = opendir(dpath);
+ if (dir == NULL) {
+ log_err("Failed to open %s.", dpath);
+ goto fail_dir;
+ }
+
+ while ((dent = readdir(dir)) != NULL) {
+ char path[NAME_PATH_SIZE + 1];
+
+ if (strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0)
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s", dpath,
+ dent->d_name);
+
+ if (stat(path, &st) == -1) {
+ log_dbg("Failed to stat %s.", path);
+ continue;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ log_dbg("%s is not a regular file.", path);
+ goto fail_file;
+ }
+
+ if (crypt_load_crt_file(path, &crt) < 0) {
+ log_err("Failed to load certificate from %s.", path);
+ goto fail_file;
+ }
+
+ if (auth_add_crt_to_store(irmd.auth.ctx, crt) < 0) {
+ log_err("Failed to add certificate from %s to store.",
+ path);
+ goto fail_crt_add;
+ }
+
+ log_dbg("Loaded certificate: %s.", path);
+
+ crypt_free_crt(crt);
+ }
+
+ closedir(dir);
+
+ log_info("Loaded certificates from %s.", dpath);
+
+ return 0;
+
+ fail_crt_add:
+ crypt_free_crt(crt);
+ fail_file:
+ closedir(dir);
+ fail_dir:
+ return -1;
+}
static int irm_init(void)
{
@@ -1819,6 +2294,30 @@ static int irm_init(void)
log_err("Failed to greate thread pool.");
goto fail_tpm_create;
}
+
+ if (pthread_mutex_init(&irmd.auth.mtx, NULL) < 0) {
+ log_err("Failed to initialize auth mutex.");
+ goto fail_auth_mtx;
+ }
+
+ irmd.auth.ctx = auth_create_ctx();
+ if (irmd.auth.ctx == NULL) {
+ log_err("Failed to create auth store context.");
+ goto fail_auth_ctx;
+ }
+
+ list_head_init(&irmd.auth.list);
+
+ if (irm_load_store(OUROBOROS_CA_CRT_DIR) < 0) {
+ log_err("Failed to load CA certificates.");
+ goto fail_auth_ctx;
+ }
+
+ if (irm_load_store(OUROBOROS_CHAIN_DIR) < 0) {
+ log_err("Failed to load intermediate certificates.");
+ goto fail_auth_ctx;
+ }
+
#ifdef HAVE_FUSE
mask = umask(0);
@@ -1844,6 +2343,8 @@ static int irm_init(void)
gcry_control(GCRYCTL_INITIALIZATION_FINISHED);
#endif
+ irmd_set_state(IRMD_INIT);
+
return 0;
#ifdef HAVE_LIBGCRYPT
@@ -1851,8 +2352,12 @@ static int irm_init(void)
#ifdef HAVE_FUSE
rmdir(FUSE_PREFIX);
#endif
- tpm_destroy(irmd.tpm);
+ auth_destroy_ctx(irmd.auth.ctx);
#endif
+ fail_auth_ctx:
+ pthread_mutex_destroy(&irmd.auth.mtx);
+ fail_auth_mtx:
+ tpm_destroy(irmd.tpm);
fail_tpm_create:
shm_rdrbuff_destroy(irmd.rdrb);
fail_rdrbuff:
@@ -1872,6 +2377,69 @@ static int irm_init(void)
return -1;
}
+static void irm_fini(void)
+{
+ struct list_head * p;
+ struct list_head * h;
+#ifdef HAVE_FUSE
+ struct timespec wait = TIMESPEC_INIT_MS(1);
+ int retries = 5;
+#endif
+ if (irmd_get_state() != IRMD_INIT)
+ log_warn("Unsafe destroy.");
+
+ pthread_mutex_lock(&irmd.auth.mtx);
+
+ list_for_each_safe(p, h, &irmd.auth.list) {
+ struct oaph * oaph = list_entry(p, struct oaph, next);
+ list_del(&oaph->next);
+ free(oaph);
+ }
+
+ pthread_mutex_unlock(&irmd.auth.mtx);
+ pthread_mutex_destroy(&irmd.auth.mtx);
+
+ auth_destroy_ctx(irmd.auth.ctx);
+
+ tpm_destroy(irmd.tpm);
+
+ close(irmd.sockfd);
+
+ if (unlink(IRM_SOCK_PATH))
+ log_dbg("Failed to unlink %s.", IRM_SOCK_PATH);
+
+ if (irmd.rdrb != NULL)
+ shm_rdrbuff_destroy(irmd.rdrb);
+
+ if (irmd.lf != NULL)
+ lockfile_destroy(irmd.lf);
+
+ pthread_mutex_lock(&irmd.cmd_lock);
+
+ list_for_each_safe(p, h, &irmd.cmds) {
+ struct cmd * cmd = list_entry(p, struct cmd, next);
+ list_del(&cmd->next);
+ close(cmd->fd);
+ free(cmd);
+ }
+
+ pthread_mutex_unlock(&irmd.cmd_lock);
+
+ pthread_mutex_destroy(&irmd.cmd_lock);
+ pthread_cond_destroy(&irmd.cmd_cond);
+ pthread_rwlock_destroy(&irmd.state_lock);
+
+#ifdef HAVE_FUSE
+ while (rmdir(FUSE_PREFIX) < 0 && retries-- > 0)
+ nanosleep(&wait, NULL);
+ if (retries < 0)
+ log_err("Failed to remove " FUSE_PREFIX);
+#endif
+ assert(list_is_empty(&irmd.cmds));
+
+ irmd.state = IRMD_NULL;
+}
+
static void usage(void)
{
printf("Usage: irmd \n"
@@ -1885,11 +2453,11 @@ static void usage(void)
static int irm_start(void)
{
+ irmd_set_state(IRMD_RUNNING);
+
if (tpm_start(irmd.tpm))
goto fail_tpm_start;
- irmd_set_state(IRMD_RUNNING);
-
if (pthread_create(&irmd.irm_sanitize, NULL, irm_sanitize, NULL))
goto fail_irm_sanitize;
@@ -1904,9 +2472,9 @@ static int irm_start(void)
pthread_cancel(irmd.irm_sanitize);
pthread_join(irmd.irm_sanitize, NULL);
fail_irm_sanitize:
- irmd_set_state(IRMD_NULL);
tpm_stop(irmd.tpm);
fail_tpm_start:
+ irmd_set_state(IRMD_INIT);
return -1;
}
@@ -1947,7 +2515,7 @@ static void irm_stop(void)
tpm_stop(irmd.tpm);
- irmd_set_state(IRMD_NULL);
+ irmd_set_state(IRMD_INIT);
}
static void irm_argparse(int argc,
@@ -1998,8 +2566,8 @@ static void * kill_dash_nine(void * o)
slept += intv;
}
- log_dbg("I am become Death, destroyer of hung processes.");
-
+ log_dbg("I guess I’ll have to shut you down for good this time,");
+ log_dbg("already tried a SIGQUIT, so now it’s KILL DASH 9.");
#ifdef IRMD_KILL_ALL_PROCESSES
reg_kill_all_proc(SIGKILL);
nanosleep(&ts, NULL);
@@ -2058,7 +2626,7 @@ int main(int argc,
if (geteuid() != 0) {
printf("IPC Resource Manager must be run as root.\n");
- exit(EXIT_FAILURE);
+ goto fail_irm_init;
}
if (irm_init() < 0)