summaryrefslogtreecommitdiff
path: root/src/irmd/reg/reg.c
diff options
context:
space:
mode:
authorDimitri Staessens <dimitri@ouroboros.rocks>2026-01-26 22:02:50 +0100
committerSander Vrijders <sander@ouroboros.rocks>2026-02-02 08:15:15 +0100
commitb1687570df3e080c961cdcc0d59b708cfbdf955e (patch)
treecaf93583ab36ab2b62b95fcfbea4b63e29857e0d /src/irmd/reg/reg.c
parent37e3dbdd8206e4f0f03fab13ff3f38aa932be065 (diff)
downloadouroboros-b1687570df3e080c961cdcc0d59b708cfbdf955e.tar.gz
ouroboros-b1687570df3e080c961cdcc0d59b708cfbdf955e.zip
lib: Add per-user packet pools
The IRMd will now check the user UID and GID for privileged access, avoiding unprivileged users being able to disrupt all IPC (e.g. by shm_open the single pool and corrupting its metadata). Non-privileged users are now limited to a PUP (per-user pool) for sending/receiving packets. It is still created by the IRMd, but owned by the user (uid) with 600 permissions. It does not add additional copies for local IPC between their own processes (i.e. over the local IPCP), but packets between processes owned by a different user or destined over the network (other IPCPs) will incur a copy when crossing the PUP / PUP or the PUP / GSPP boundary. Privileged users and users in the ouroboros group still have direct access to the GSPP (globally shared private pool) for packet transfer that will avoid additional copies when processing packets between processes owned by different users and to the network. This aligns the security model with UNIX trust domains defined by UID and GID by leveraging file permission on the pools in shared memory. ┌─────────────────────────────────────────────────────────────┐ │ Source Pool │ Dest Pool │ Operation │ Copies │ ├─────────────────────────────────────────────────────────────┤ │ GSPP │ GSPP │ Zero-copy │ 0 │ │ PUP.uid │ PUP.uid │ Zero-copy │ 0 │ │ PUP.uid1 │ PUP.uid2 │ memcpy() │ 1 │ │ PUP.uid │ GSPP │ memcpy() │ 1 │ │ GSPP │ PUP.uid │ memcpy() │ 1 │ └─────────────────────────────────────────────────────────────┘ This also renames the struct ai ("application instance") in dev.c to struct proc (process). Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks> Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
Diffstat (limited to 'src/irmd/reg/reg.c')
-rw-r--r--src/irmd/reg/reg.c105
1 files changed, 102 insertions, 3 deletions
diff --git a/src/irmd/reg/reg.c b/src/irmd/reg/reg.c
index fd8285be..e89b492b 100644
--- a/src/irmd/reg/reg.c
+++ b/src/irmd/reg/reg.c
@@ -35,6 +35,7 @@ The IPC Resource Manager - Registry
#include "flow.h"
#include "ipcp.h"
#include "name.h"
+#include "pool.h"
#include "proc.h"
#include "prog.h"
@@ -57,6 +58,9 @@ struct {
struct list_head names; /* registered names known */
size_t n_names; /* number of names */
+ struct list_head pools; /* per-user pools */
+ size_t n_pools; /* number of pools */
+
struct list_head procs; /* processes */
size_t n_procs; /* number of processes */
@@ -235,6 +239,20 @@ static struct list_head * __reg_after_name(const char * name)
return p;
}
+static struct reg_pool * __reg_get_pool(uid_t uid)
+{
+ struct list_head * p;
+
+ list_for_each(p, &reg.pools) {
+ struct reg_pool * entry;
+ entry = list_entry(p, struct reg_pool, next);
+ if (entry->uid == uid)
+ return entry;
+ }
+
+ return NULL;
+}
+
static struct reg_proc * __reg_get_proc(pid_t pid)
{
struct list_head * p;
@@ -540,6 +558,7 @@ int reg_init(void)
list_head_init(&reg.flows);
list_head_init(&reg.ipcps);
list_head_init(&reg.names);
+ list_head_init(&reg.pools);
list_head_init(&reg.procs);
list_head_init(&reg.progs);
list_head_init(&reg.spawned);
@@ -589,6 +608,15 @@ void reg_clear(void)
reg.n_procs--;
}
+ list_for_each_safe(p, h, &reg.pools) {
+ struct reg_pool * entry;
+ entry = list_entry(p, struct reg_pool, next);
+ list_del(&entry->next);
+ entry->refcount = 0; /* Force destroy during cleanup */
+ reg_pool_destroy(entry);
+ reg.n_pools--;
+ }
+
list_for_each_safe(p, h, &reg.flows) {
struct reg_flow * entry;
entry = list_entry(p, struct reg_flow, next);
@@ -621,6 +649,7 @@ void reg_fini(void)
assert(list_is_empty(&reg.spawned));
assert(list_is_empty(&reg.progs));
assert(list_is_empty(&reg.procs));
+ assert(list_is_empty(&reg.pools));
assert(list_is_empty(&reg.names));
assert(list_is_empty(&reg.ipcps));
assert(list_is_empty(&reg.flows));
@@ -628,6 +657,7 @@ void reg_fini(void)
assert(reg.n_spawned == 0);
assert(reg.n_progs == 0);
assert(reg.n_procs == 0);
+ assert(reg.n_pools == 0);
assert(reg.n_names == 0);
assert(reg.n_ipcps == 0);
assert(reg.n_flows == 0);
@@ -1090,6 +1120,35 @@ int reg_list_names(name_info_msg_t *** names)
return -ENOMEM;
}
+int reg_prepare_pool(uid_t uid,
+ gid_t gid)
+{
+ struct reg_pool * pool;
+
+ if (is_ouroboros_member_uid(uid))
+ return 0;
+
+ pthread_mutex_lock(&reg.mtx);
+
+ pool = __reg_get_pool(uid);
+ if (pool == NULL) {
+ pool = reg_pool_create(uid, gid);
+ if (pool == NULL) {
+ log_err("Failed to create pool for uid %d.", uid);
+ pthread_mutex_unlock(&reg.mtx);
+ return -1;
+ }
+ list_add(&pool->next, &reg.pools);
+ reg.n_pools++;
+ }
+
+ reg_pool_ref(pool);
+
+ pthread_mutex_unlock(&reg.mtx);
+
+ return 0;
+}
+
int reg_create_proc(const struct proc_info * info)
{
struct reg_proc * proc;
@@ -1100,13 +1159,13 @@ int reg_create_proc(const struct proc_info * info)
if (__reg_get_proc(info->pid) != NULL) {
log_err("Process %d already exists.", info->pid);
- goto fail_proc;
+ goto fail;
}
proc = reg_proc_create(info);
if (proc == NULL) {
log_err("Failed to create process %d.", info->pid);
- goto fail_proc;
+ goto fail;
}
__reg_proc_update_names(proc);
@@ -1121,7 +1180,7 @@ int reg_create_proc(const struct proc_info * info)
return 0;
- fail_proc:
+ fail:
pthread_mutex_unlock(&reg.mtx);
return -1;
}
@@ -1129,6 +1188,7 @@ int reg_create_proc(const struct proc_info * info)
int reg_destroy_proc(pid_t pid)
{
struct reg_proc * proc;
+ struct reg_pool * pool = NULL;
struct pid_entry * spawn;
struct reg_ipcp * ipcp;
@@ -1136,11 +1196,18 @@ int reg_destroy_proc(pid_t pid)
proc = __reg_get_proc(pid);
if (proc != NULL) {
+ if (!is_ouroboros_member_uid(proc->info.uid))
+ pool = __reg_get_pool(proc->info.uid);
list_del(&proc->next);
reg.n_procs--;
reg_proc_destroy(proc);
__reg_del_proc_from_names(pid);
__reg_cancel_flows_for_proc(pid);
+ if (pool != NULL && reg_pool_unref(pool) == 0) {
+ list_del(&pool->next);
+ reg.n_pools--;
+ reg_pool_destroy(pool);
+ }
}
spawn = __reg_get_spawned(pid);
@@ -1175,6 +1242,38 @@ bool reg_has_proc(pid_t pid)
return ret;
}
+bool reg_is_proc_privileged(pid_t pid)
+{
+ struct reg_proc * proc;
+ bool ret = false;
+
+ pthread_mutex_lock(&reg.mtx);
+
+ proc = __reg_get_proc(pid);
+ if (proc != NULL)
+ ret = reg_proc_is_privileged(proc);
+
+ pthread_mutex_unlock(&reg.mtx);
+
+ return ret;
+}
+
+uid_t reg_get_proc_uid(pid_t pid)
+{
+ struct reg_proc * proc;
+ uid_t ret = 0;
+
+ pthread_mutex_lock(&reg.mtx);
+
+ proc = __reg_get_proc(pid);
+ if (proc != NULL && !is_ouroboros_member_uid(proc->info.uid))
+ ret = proc->info.uid;
+
+ pthread_mutex_unlock(&reg.mtx);
+
+ return ret;
+}
+
void reg_kill_all_proc(int signal)
{
pthread_mutex_lock(&reg.mtx);