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.c1075
1 files changed, 418 insertions, 657 deletions
diff --git a/src/irmd/main.c b/src/irmd/main.c
index daaf4129..e610a015 100644
--- a/src/irmd/main.c
+++ b/src/irmd/main.c
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2024
+ * Ouroboros - Copyright (C) 2016 - 2026
*
* The IPC Resource Manager
*
@@ -22,8 +22,10 @@
#if defined(__linux__) || defined(__CYGWIN__)
#define _DEFAULT_SOURCE
+#define _GNU_SOURCE
#else
-#define _POSIX_C_SOURCE 200809L
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
#endif
#include "config.h"
@@ -39,10 +41,11 @@
#include <ouroboros/list.h>
#include <ouroboros/lockfile.h>
#include <ouroboros/logs.h>
+#include <ouroboros/protobuf.h>
#include <ouroboros/pthread.h>
#include <ouroboros/random.h>
#include <ouroboros/rib.h>
-#include <ouroboros/shm_rdrbuff.h>
+#include <ouroboros/ssm_pool.h>
#include <ouroboros/sockets.h>
#include <ouroboros/time.h>
#include <ouroboros/tpm.h>
@@ -56,15 +59,21 @@
#include "configfile.h"
#include <dirent.h>
-#include <sys/socket.h>
-#include <sys/un.h>
+#include <grp.h>
+#include <pwd.h>
#include <signal.h>
+#include <spawn.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
-#include <spawn.h>
+#include <sys/un.h>
+#ifdef __APPLE__
+#include <sys/types.h>
+#include <unistd.h>
+#endif
#ifdef HAVE_LIBGCRYPT
#include <gcrypt.h>
@@ -77,6 +86,7 @@
#define TIMESYNC_SLACK 100 /* ms */
#define OAP_SEEN_TIMER 20 /* s */
#define DEALLOC_TIME 300 /* s */
+#define DIRECT_MPL 1 /* s */
enum irm_state {
IRMD_NULL = 0,
@@ -85,13 +95,6 @@ enum irm_state {
IRMD_SHUTDOWN
};
-struct oaph {
- struct list_head next;
-
- uint64_t stamp;
- uint8_t id[OAP_ID_SIZE];
-};
-
struct cmd {
struct list_head next;
@@ -105,14 +108,8 @@ 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 */
+ struct ssm_pool * gspp; /* pool for packets */
int sockfd; /* UNIX socket */
@@ -456,7 +453,7 @@ static void name_update_sec_paths(struct name_info * info)
assert(info != NULL);
if (strlen(info->s.enc) == 0)
- sprintf(info->s.enc, "%s/%s/enc.cfg", srv_dir, info->name);
+ sprintf(info->s.enc, "%s/%s/enc.conf", srv_dir, info->name);
if (strlen(info->s.crt) == 0)
sprintf(info->s.crt, "%s/%s/crt.pem", srv_dir, info->name);
@@ -465,7 +462,7 @@ static void name_update_sec_paths(struct name_info * info)
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);
+ sprintf(info->c.enc, "%s/%s/enc.conf", cli_dir, info->name);
if (strlen(info->c.crt) == 0)
sprintf(info->c.crt, "%s/%s/crt.pem", cli_dir, info->name);
@@ -613,18 +610,21 @@ static int unbind_program(const char * prog,
if (name == NULL) {
if (reg_destroy_prog(prog) < 0) {
log_err("Failed to unbind %s.", prog);
- return -1;
+ goto fail;
}
log_info("Program %s unbound.", prog);
} else {
if (reg_unbind_prog(name, prog) < 0) {
log_err("Failed to unbind %s from %s", prog, name);
- return -1;
+ goto fail;
}
log_info("Name %s unbound for %s.", name, prog);
}
return 0;
+
+ fail:
+ return -1;
}
static int unbind_process(pid_t pid,
@@ -633,18 +633,21 @@ static int unbind_process(pid_t pid,
if (name == NULL) {
if (reg_destroy_proc(pid) < 0) {
log_err("Failed to unbind %d.", pid);
- return -1;
+ goto fail;
}
log_info("Process %d unbound.", pid);
} else {
if (reg_unbind_proc(name, pid) < 0) {
log_err("Failed to unbind %d from %s", pid, name);
- return -1;
+ goto fail;
}
log_info("Name %s unbound for process %d.", name, pid);
}
return 0;
+
+ fail:
+ return -1;
}
static int list_ipcps(ipcp_list_msg_t *** ipcps,
@@ -716,8 +719,7 @@ int name_reg(const char * name,
if (ipcp_reg(pid, hash)) {
log_err("Could not register " HASH_FMT32 " with IPCP %d.",
HASH_VAL32(hash.data), pid);
- freebuf(hash);
- return -1;
+ goto fail_hash;
}
log_info("Registered %s with IPCP %d as " HASH_FMT32 ".",
@@ -726,6 +728,10 @@ int name_reg(const char * name,
freebuf(hash);
return 0;
+
+ fail_hash:
+ freebuf(hash);
+ return -1;
}
static int name_unreg(const char * name,
@@ -760,8 +766,7 @@ static int name_unreg(const char * name,
if (ipcp_unreg(pid, hash)) {
log_err("Could not unregister %s with IPCP %d.", name, pid);
- freebuf(hash);
- return -1;
+ goto fail_hash;
}
log_info("Unregistered %s from %d.", name, pid);
@@ -769,20 +774,53 @@ static int name_unreg(const char * name,
freebuf(hash);
return 0;
+
+ fail_hash:
+ freebuf(hash);
+ return -1;
+}
+
+static int get_peer_ids(int fd,
+ uid_t * uid,
+ gid_t * gid)
+{
+#if defined(__linux__)
+ struct ucred ucred;
+ socklen_t len;
+
+ len = sizeof(ucred);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0)
+ goto fail;
+
+ *uid = ucred.uid;
+ *gid = ucred.gid;
+#else
+ if (getpeereid(fd, uid, gid) < 0)
+ goto fail;
+#endif
+ return 0;
+ fail:
+ return -1;
}
static int proc_announce(const struct proc_info * info)
{
+ if (reg_prepare_pool(info->uid, info->gid) < 0) {
+ log_err("Failed to prepare pool for uid %d.", info->uid);
+ goto fail;
+ }
+
if (reg_create_proc(info) < 0) {
log_err("Failed to add process %d.", info->pid);
- goto fail_proc;
+ goto fail;
}
log_info("Process added: %d (%s).", info->pid, info->prog);
return 0;
- fail_proc:
+ fail:
return -1;
}
@@ -796,266 +834,26 @@ static int proc_exit(pid_t pid)
return 0;
}
-static void __cleanup_pkp(void * pkp)
-{
- if (pkp != NULL)
- crypt_dh_pkp_destroy(pkp);
-}
-
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)
+ struct timespec * abstime,
+ struct crypt_sk * sk)
{
- 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;
+ buffer_t req_hdr;
+ buffer_t resp_hdr;
+ char name[NAME_SIZE + 1];
+ struct name_info info;
+ int err;
- /* piggyback of user data not yet implemented */
assert(data != NULL && BUF_IS_EMPTY(data));
- assert(symmkey != NULL && BUF_IS_EMPTY(symmkey));
+
+ clrbuf(req_hdr);
+ clrbuf(resp_hdr);
if (!reg_has_proc(flow->n_pid)) {
log_err("Unknown process %d calling accept.", flow->n_pid);
@@ -1077,7 +875,7 @@ static int flow_accept(struct flow_info * flow,
pthread_cleanup_push(__cleanup_flow, flow);
- err = reg_wait_flow_accepted(flow, &oap_hdr.hdr, abstime);
+ err = reg_wait_flow_accepted(flow, &req_hdr, abstime);
pthread_cleanup_pop(false);
@@ -1097,119 +895,49 @@ static int flow_accept(struct flow_info * flow,
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;
+ goto fail_oap;
+ }
+
+ if (reg_get_name_info(name, &info) < 0) {
+ log_err("Failed to get name info for %s.", name);
+ err = -ENAME;
+ goto fail_oap;
}
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;
- }
+ flow->uid = reg_get_proc_uid(flow->n_pid);
- if (crypt && oap_hdr.eph.len == 0) {
- log_warn("Encryption required but no key provided.");
- err = -ECRYPT;
- goto fail_oap_hdr;
+ err = oap_srv_process(&info, req_hdr, &resp_hdr, data, sk);
+ if (err < 0) {
+ log_err("OAP processing failed for %s.", name);
+ goto fail_oap;
}
- 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;
+ if (reg_flow_is_direct(flow->id)) {
+ if (reg_respond_flow_direct(flow->id, &resp_hdr) < 0) {
+ log_err("Failed to respond to direct flow.");
+ goto fail_resp;
}
-
- 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;
- }
-
- symmkey->data = s;
- symmkey->len = SYMMKEYSZ;
-
- crypt_dh_pkp_destroy(pkp);
- }
-
- 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_info("Flow %d accepted (direct) by %d for %s.",
+ flow->id, flow->n_pid, name);
+ } else if (ipcp_flow_alloc_resp(flow, 0, resp_hdr) < 0) {
log_err("Failed to respond to flow allocation.");
goto fail_resp;
+ } else {
+ log_info("Flow %d accepted by %d for %s (uid %d).",
+ flow->id, flow->n_pid, name, flow->uid);
}
- 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);
+ freebuf(req_hdr);
+ freebuf(resp_hdr);
return 0;
- fail_r_oap_hdr:
- freebuf(*symmkey);
- fail_derive:
- 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_oap:
+ if (!reg_flow_is_direct(flow->id))
+ ipcp_flow_alloc_resp(flow, err, resp_hdr);
fail_wait:
reg_destroy_flow(flow->id);
fail_flow:
@@ -1217,10 +945,8 @@ static int flow_accept(struct flow_info * flow,
fail_resp:
flow->state = FLOW_NULL;
- oap_hdr_fini(&r_oap_hdr);
- freebuf(*symmkey);
- clrbuf(lpk);
- oap_hdr_fini(&oap_hdr);
+ freebuf(req_hdr);
+ freebuf(resp_hdr);
reg_destroy_flow(flow->id);
return -EIPCP;
}
@@ -1235,14 +961,17 @@ static int flow_join(struct flow_info * flow,
buffer_t pbuf = BUF_INIT; /* nothing to piggyback */
int err;
- log_info("Allocating flow for %d to %s.", flow->n_pid, dst);
-
if (reg_create_flow(flow) < 0) {
log_err("Failed to create flow.");
err = -EBADF;
goto fail_flow;
}
+ flow->uid = reg_get_proc_uid(flow->n_pid);
+
+ log_info("Allocating flow for %d to %s (uid %d).",
+ flow->n_pid, dst, flow->uid);
+
strcpy(layer.name, dst);
if (reg_get_ipcp_by_layer(&ipcp, &layer) < 0) {
log_err("Failed to get IPCP for layer %s.", dst);
@@ -1308,7 +1037,7 @@ static int get_ipcp_by_dst(const char * dst,
pid_t * pid,
buffer_t * hash)
{
- ipcp_list_msg_t ** ipcps;
+ ipcp_list_msg_t ** ipcps = NULL;
int n;
int i;
int err = -EIPCP;
@@ -1361,196 +1090,6 @@ static int get_ipcp_by_dst(const char * dst,
return err;
}
-static int flow_alloc(struct flow_info * flow,
- const char * dst,
- buffer_t * symmkey,
- buffer_t * data,
- struct timespec * abstime)
-{
- 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 && 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 (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);
- if (s == NULL) {
- log_err("Failed to malloc symmetric key");
- err = -ENOMEM;
- goto fail_malloc;
- }
-
- key_len = crypt_dh_pkp_create(&pkp, buf);
- if (key_len < 0) {
- log_err("Failed to generate key pair.");
- err = -ECRYPT;
- goto fail_pkp;
- }
-
- lpk.data = buf;
- lpk.len = (size_t) key_len;
-
- 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;
- goto fail_flow;
- }
-
- if (get_ipcp_by_dst(dst, &flow->n_1_pid, &hash) < 0) {
- log_err("Failed to find IPCP for %s.", dst);
- err = -EIPCP;
- goto fail_ipcp;
- }
-
- reg_prepare_flow_alloc(flow);
-
- if (ipcp_flow_alloc(flow, hash, oap_hdr.hdr)) {
- log_err("Flow allocation %d failed.", flow->id);
- err = -ENOTALLOC;
- goto fail_alloc;
- }
-
- pthread_cleanup_push(__cleanup_flow, flow);
- pthread_cleanup_push(__cleanup_pkp, pkp);
- pthread_cleanup_push(free, hash.data);
- pthread_cleanup_push(free, s);
-
- err = reg_wait_flow_allocated(flow, &r_oap_hdr.hdr, abstime);
-
- pthread_cleanup_pop(false);
- pthread_cleanup_pop(false);
- pthread_cleanup_pop(false);
- pthread_cleanup_pop(false);
-
- if (err == -ETIMEDOUT) {
- log_err("Flow allocation timed out.");
- goto fail_alloc;
- }
-
- if (err == -1) {
- log_dbg("Flow allocation terminated.");
- err = -EIPCP;
- goto fail_alloc;
- }
-
- 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 (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_r_oap_hdr;
- }
- crypt_dh_pkp_destroy(pkp);
-
- 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_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:
- 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(const char * name)
{
struct timespec timeo = TIMESPEC_INIT_MS(IRMD_REQ_ARR_TIMEOUT);
@@ -1643,6 +1182,194 @@ static int flow_req_arr(struct flow_info * flow,
return ret;
}
+#ifndef DISABLE_DIRECT_IPC
+static int flow_alloc_direct(const char * dst,
+ struct flow_info * flow,
+ buffer_t * data,
+ struct timespec * abstime,
+ struct crypt_sk * sk,
+ struct name_info * info)
+{
+ struct flow_info acc; /* server side flow */
+ buffer_t req_hdr = BUF_INIT;
+ buffer_t resp_hdr = BUF_INIT;
+ void * ctx;
+ int err;
+
+ acc.id = wait_for_accept(dst);
+ if (acc.id < 0) {
+ log_dbg("No accepting process for %s.", dst);
+ return -EAGAIN;
+ }
+
+ if (oap_cli_prepare(&ctx, info, &req_hdr, *data) < 0) {
+ log_err("Failed to prepare OAP for %s.", dst);
+ return -EBADF;
+ }
+
+ acc.n_1_pid = flow->n_pid;
+ acc.mpl = DIRECT_MPL;
+ acc.qs = flow->qs;
+ acc.state = FLOW_ALLOCATED;
+
+ err = reg_prepare_flow_direct(&acc, &req_hdr, flow->uid);
+ if (err == -EPERM) {
+ log_dbg("UID mismatch, falling back.");
+ oap_ctx_free(ctx);
+ freebuf(req_hdr);
+ return -EPERM;
+ }
+
+ if (err < 0) {
+ log_err("Failed to prepare direct flow.");
+ oap_ctx_free(ctx);
+ freebuf(req_hdr);
+ return -EBADF;
+ }
+
+ err = reg_wait_flow_direct(acc.id, &resp_hdr, abstime);
+ if (err < 0) {
+ log_err("Timeout waiting for OAP response.");
+ oap_ctx_free(ctx);
+ return -ETIMEDOUT;
+ }
+
+ err = oap_cli_complete(ctx, info, resp_hdr, data, sk);
+ if (err < 0) {
+ log_err("OAP completion failed for %s.", dst);
+ freebuf(resp_hdr);
+ return err;
+ }
+
+ flow->id = acc.id;
+ flow->n_1_pid = acc.n_pid;
+ flow->mpl = DIRECT_MPL;
+ flow->state = FLOW_ALLOCATED;
+
+ log_info("Flow %d allocated (direct) for %d to %s.",
+ flow->id, flow->n_pid, dst);
+
+ freebuf(resp_hdr);
+
+ return 0;
+}
+#endif /* DISABLE_DIRECT_IPC */
+
+static int flow_alloc(const char * dst,
+ struct flow_info * flow,
+ buffer_t * data,
+ struct timespec * abstime,
+ struct crypt_sk * sk)
+{
+ buffer_t req_hdr = BUF_INIT;
+ buffer_t resp_hdr = BUF_INIT;
+ buffer_t hash = BUF_INIT;
+ struct name_info info;
+ void * ctx;
+ int err;
+
+ /* piggyback of user data not yet implemented */
+ assert(data != NULL && BUF_IS_EMPTY(data));
+
+ /* Look up name_info for dst */
+ if (reg_get_name_info(dst, &info) < 0) {
+ log_err("Failed to get name info for %s.", dst);
+ err = -ENAME;
+ goto fail_flow;
+ }
+
+ flow->uid = reg_get_proc_uid(flow->n_pid);
+
+ log_info("Allocating flow for %d to %s (uid %d).",
+ flow->n_pid, dst, flow->uid);
+
+#ifndef DISABLE_DIRECT_IPC
+ err = flow_alloc_direct(dst, flow, data, abstime, sk, &info);
+ if (err == 0)
+ return 0;
+
+ if (err != -EPERM && err != -EAGAIN)
+ goto fail_flow;
+#endif
+ if (reg_create_flow(flow) < 0) {
+ log_err("Failed to create flow.");
+ err = -EBADF;
+ goto fail_flow;
+ }
+
+ if (get_ipcp_by_dst(dst, &flow->n_1_pid, &hash) < 0) {
+ log_err("Failed to find IPCP for %s.", dst);
+ err = -EIPCP;
+ goto fail_ipcp;
+ }
+
+ if (reg_prepare_flow_alloc(flow) < 0) {
+ log_err("Failed to prepare flow allocation.");
+ err = -EBADF;
+ goto fail_prepare;
+ }
+
+ if (oap_cli_prepare(&ctx, &info, &req_hdr, *data) < 0) {
+ log_err("Failed to prepare OAP request for %s.", dst);
+ err = -EBADF;
+ goto fail_prepare;
+ }
+
+ if (ipcp_flow_alloc(flow, hash, req_hdr)) {
+ log_err("Flow allocation %d failed.", flow->id);
+ err = -EIPCP;
+ goto fail_alloc;
+ }
+
+ pthread_cleanup_push(__cleanup_flow, flow);
+ pthread_cleanup_push(free, hash.data);
+
+ err = reg_wait_flow_allocated(flow, &resp_hdr, abstime);
+
+ pthread_cleanup_pop(false);
+ pthread_cleanup_pop(false);
+
+ if (err == -ETIMEDOUT) {
+ log_err("Flow allocation timed out.");
+ goto fail_wait;
+ }
+
+ log_dbg("Response for flow %d to %s.", flow->id, dst);
+
+ if (err < 0) {
+ log_warn("Allocation rejected: %s (%d).", dst, err);
+ goto fail_peer;
+ }
+
+ err = oap_cli_complete(ctx, &info, resp_hdr, data, sk);
+ if (err < 0) {
+ log_err("OAP completion failed for %s.", dst);
+ goto fail_complete;
+ }
+
+ freebuf(req_hdr);
+ freebuf(resp_hdr);
+ freebuf(hash);
+
+ return 0;
+
+ fail_complete:
+ ctx = NULL; /* freee'd on complete */
+ fail_peer:
+ flow->state = FLOW_DEALLOCATED;
+ fail_wait:
+ freebuf(resp_hdr);
+ fail_alloc:
+ freebuf(req_hdr);
+ oap_ctx_free(ctx);
+ fail_prepare:
+ freebuf(hash);
+ fail_ipcp:
+ reg_destroy_flow(flow->id);
+ fail_flow:
+ return err;
+}
+
static int flow_alloc_reply(struct flow_info * flow,
int response,
buffer_t * data)
@@ -1666,6 +1393,12 @@ static int flow_dealloc(struct flow_info * flow,
reg_dealloc_flow(flow);
+ if (reg_flow_is_direct(flow->id)) {
+ if (flow->state == FLOW_DEALLOCATED)
+ reg_destroy_flow(flow->id);
+ return 0;
+ }
+
if (ipcp_flow_dealloc(flow->n_1_pid, flow->id, ts->tv_sec) < 0) {
log_err("Failed to request dealloc from %d.", flow->n_1_pid);
return -EIPCP;
@@ -1742,21 +1475,24 @@ static void __cleanup_irm_msg(void * o)
irm_msg__free_unpacked((irm_msg_t *) o, NULL);
}
-static irm_msg_t * do_command_msg(irm_msg_t * msg)
+static irm_msg_t * do_command_msg(irm_msg_t * msg,
+ int fd)
{
- struct ipcp_config conf;
- struct ipcp_info ipcp;
- struct flow_info flow;
- struct proc_info proc;
- struct name_info name;
- struct timespec * abstime;
- struct timespec max = TIMESPEC_INIT_MS(FLOW_ALLOC_TIMEOUT);
- struct timespec now;
- struct timespec ts = TIMESPEC_INIT_S(0); /* static analysis */
- int res;
- irm_msg_t * ret_msg;
- buffer_t data;
- buffer_t symmkey = BUF_INIT;;
+ struct ipcp_config conf;
+ struct ipcp_info ipcp;
+ struct flow_info flow;
+ struct proc_info proc;
+ struct name_info name;
+ struct crypt_sk sk;
+ uint8_t kbuf[SYMMKEYSZ]; /* stack buffer for OAP */
+ uint8_t * hbuf = NULL; /* heap copy for response */
+ struct timespec * abstime;
+ struct timespec max = TIMESPEC_INIT_MS(FLOW_ALLOC_TIMEOUT);
+ struct timespec now;
+ struct timespec ts = TIMESPEC_INIT_S(0); /* static analysis */
+ int res;
+ irm_msg_t * ret_msg;
+ buffer_t data;
memset(&flow, 0, sizeof(flow));
@@ -1821,7 +1557,11 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
case IRM_MSG_CODE__IRM_PROC_ANNOUNCE:
proc.pid = msg->pid;
strcpy(proc.prog, msg->prog);
- res = proc_announce(&proc);
+ res = get_peer_ids(fd, &proc.uid, &proc.gid);
+ if (res < 0)
+ log_err("Failed to get UID/GID for pid %d.", msg->pid);
+ else
+ res = proc_announce(&proc);
break;
case IRM_MSG_CODE__IRM_PROC_EXIT:
res = proc_exit(msg->pid);
@@ -1858,15 +1598,27 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
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, &symmkey, &data, abstime);
+ sk.key = kbuf;
+ res = flow_accept(&flow, &data, abstime, &sk);
if (res == 0) {
- ret_msg->flow_info = flow_info_s_to_msg(&flow);
- 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;
+ ret_msg->flow_info = flow_info_s_to_msg(&flow);
+ ret_msg->has_pk = data.len != 0;
+ ret_msg->pk.data = data.data;
+ ret_msg->pk.len = data.len;
+ ret_msg->has_cipher_nid = true;
+ ret_msg->cipher_nid = sk.nid;
+ if (sk.nid != NID_undef) {
+ hbuf = malloc(SYMMKEYSZ);
+ if (hbuf == NULL) {
+ log_err("Failed to malloc key buf");
+ return NULL;
+ }
+
+ memcpy(hbuf, kbuf, SYMMKEYSZ);
+ ret_msg->sym_key.data = hbuf;
+ ret_msg->sym_key.len = SYMMKEYSZ;
+ ret_msg->has_sym_key = true;
+ }
}
break;
case IRM_MSG_CODE__IRM_FLOW_ALLOC:
@@ -1876,15 +1628,26 @@ 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, &symmkey, &data, abstime);
+ sk.key = kbuf;
+ res = flow_alloc(msg->dst, &flow, &data, abstime, &sk);
if (res == 0) {
- ret_msg->flow_info = flow_info_s_to_msg(&flow);
- 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;
+ ret_msg->flow_info = flow_info_s_to_msg(&flow);
+ ret_msg->has_pk = data.len != 0;
+ ret_msg->pk.data = data.data;
+ ret_msg->pk.len = data.len;
+ ret_msg->has_cipher_nid = true;
+ ret_msg->cipher_nid = sk.nid;
+ if (sk.nid != NID_undef) {
+ hbuf = malloc(SYMMKEYSZ);
+ if (hbuf == NULL) {
+ log_err("Failed to malloc key buf");
+ return NULL;
+ }
+ memcpy(hbuf, kbuf, SYMMKEYSZ);
+ ret_msg->sym_key.data = hbuf;
+ ret_msg->sym_key.len = SYMMKEYSZ;
+ ret_msg->has_sym_key = true;
+ }
}
break;
case IRM_MSG_CODE__IRM_FLOW_JOIN:
@@ -1938,14 +1701,16 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg)
else
ret_msg->result = res;
+ crypt_secure_clear(kbuf, SYMMKEYSZ);
+
return ret_msg;
}
static void * mainloop(void * o)
{
- int sfd;
- irm_msg_t * msg;
- buffer_t buffer;
+ int sfd;
+ irm_msg_t * msg;
+ buffer_t buffer;
(void) o;
@@ -1981,7 +1746,7 @@ static void * mainloop(void * o)
pthread_cleanup_push(__cleanup_close_ptr, &sfd);
pthread_cleanup_push(__cleanup_irm_msg, msg);
- ret_msg = do_command_msg(msg);
+ ret_msg = do_command_msg(msg, sfd);
pthread_cleanup_pop(true);
pthread_cleanup_pop(false);
@@ -2074,7 +1839,7 @@ static void destroy_mount(char * mnt)
static int ouroboros_reset(void)
{
- shm_rdrbuff_purge();
+ ssm_pool_gspp_purge();
lockfile_destroy(irmd.lf);
return 0;
@@ -2095,10 +1860,8 @@ static void cleanup_pid(pid_t pid)
}
destroy_mount(mnt);
-
-#else
- (void) pid;
#endif
+ ssm_pool_reclaim_orphans(irmd.gspp, pid);
}
void * irm_sanitize(void * o)
@@ -2170,7 +1933,7 @@ static int irm_load_store(char * dpath)
goto fail_file;
}
- if (auth_add_crt_to_store(irmd.auth.ctx, crt) < 0) {
+ if (oap_auth_add_ca_crt(crt) < 0) {
log_err("Failed to add certificate from %s to store.",
path);
goto fail_crt_add;
@@ -2198,6 +1961,8 @@ static int irm_load_store(char * dpath)
static int irm_init(void)
{
struct stat st;
+ struct group * grp;
+ gid_t gid;
pthread_condattr_t cattr;
#ifdef HAVE_FUSE
mode_t mask;
@@ -2283,11 +2048,23 @@ static int irm_init(void)
goto fail_sock_path;
}
- if ((irmd.rdrb = shm_rdrbuff_create()) == NULL) {
- log_err("Failed to create rdrbuff.");
- goto fail_rdrbuff;
+ grp = getgrnam("ouroboros");
+ if (grp == NULL) {
+ log_warn("ouroboros group not found, using gid %d.", getgid());
+ gid = getgid();
+ } else {
+ gid = grp->gr_gid;
+ }
+
+ irmd.gspp = ssm_pool_create(getuid(), gid);
+ if (irmd.gspp == NULL) {
+ log_err("Failed to create GSPP.");
+ goto fail_pool;
}
+ if (ssm_pool_mlock(irmd.gspp) < 0)
+ log_warn("Failed to mlock pool.");
+
irmd.tpm = tpm_create(IRMD_MIN_THREADS, IRMD_ADD_THREADS,
mainloop, NULL);
if (irmd.tpm == NULL) {
@@ -2295,27 +2072,19 @@ static int irm_init(void)
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;
+ if (oap_auth_init() < 0) {
+ log_err("Failed to initialize OAP module.");
+ goto fail_oap;
}
- 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;
+ goto fail_load_store;
}
if (irm_load_store(OUROBOROS_CHAIN_DIR) < 0) {
log_err("Failed to load intermediate certificates.");
- goto fail_auth_ctx;
+ goto fail_load_store;
}
#ifdef HAVE_FUSE
@@ -2352,15 +2121,14 @@ static int irm_init(void)
#ifdef HAVE_FUSE
rmdir(FUSE_PREFIX);
#endif
- auth_destroy_ctx(irmd.auth.ctx);
#endif
- fail_auth_ctx:
- pthread_mutex_destroy(&irmd.auth.mtx);
- fail_auth_mtx:
+ fail_load_store:
+ oap_auth_fini();
+ fail_oap:
tpm_destroy(irmd.tpm);
fail_tpm_create:
- shm_rdrbuff_destroy(irmd.rdrb);
- fail_rdrbuff:
+ ssm_pool_destroy(irmd.gspp);
+ fail_pool:
close(irmd.sockfd);
fail_sock_path:
unlink(IRM_SOCK_PATH);
@@ -2388,18 +2156,7 @@ static void irm_fini(void)
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);
+ oap_auth_fini();
tpm_destroy(irmd.tpm);
@@ -2408,8 +2165,7 @@ static void irm_fini(void)
if (unlink(IRM_SOCK_PATH))
log_dbg("Failed to unlink %s.", IRM_SOCK_PATH);
- if (irmd.rdrb != NULL)
- shm_rdrbuff_destroy(irmd.rdrb);
+ ssm_pool_destroy(irmd.gspp);
if (irmd.lf != NULL)
lockfile_destroy(irmd.lf);
@@ -2532,10 +2288,8 @@ static void irm_argparse(int argc,
argc--;
argv++;
} else if (strcmp(*argv, "--version") == 0) {
- printf("Ouroboros version %d.%d.%d\n",
- OUROBOROS_VERSION_MAJOR,
- OUROBOROS_VERSION_MINOR,
- OUROBOROS_VERSION_PATCH);
+ printf("Ouroboros version %s\n",
+ OUROBOROS_VERSION_STRING);
exit(EXIT_SUCCESS);
#ifdef HAVE_TOML
} else if (strcmp (*argv, "--config") == 0) {
@@ -2637,6 +2391,11 @@ int main(int argc,
goto fail_reg;
}
+ if (crypt_secure_malloc_init(IRMD_SECMEM_MAX) < 0) {
+ log_err("Failed to initialize secure memory allocation.");
+ goto fail_reg;
+ }
+
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
if (irm_start() < 0)
@@ -2656,6 +2415,8 @@ int main(int argc,
pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
+ crypt_secure_malloc_fini();
+
reg_clear();
reg_fini();