Module: xenomai-3 Branch: master Commit: cf21e806295981a9d0e342f683bfef419b6e3c68 URL: http://git.xenomai.org/?p=xenomai-3.git;a=commit;h=cf21e806295981a9d0e342f683bfef419b6e3c68
Author: Philippe Gerum <r...@xenomai.org> Date: Fri Sep 11 18:47:24 2015 +0200 copperplate: enable group-based access to sessions Passing --session foo/<group> will allow the members of the designated UNIX group to attach the shared heap and use the Copperplate services within a particular session. <group> may be a valid GID or group name from /etc/group. With Cobalt, such group would typically match the xenomai.allowed_group parameter passed to the kernel. To set up a shared session involving non-privileged users, all of them must be members of a dedicated UNIX group, and the following operations should be carried out: 1. if using Cobalt, pass xenomai.gid=<group-id> on the kernel command line accordingly 2. set udev rules to chown+chmod /dev/rtdm/* files with proper group permissions (e.g. for user group "xenomai" => SUBSYSTEM=="rtdm", MODE="0660", GROUP="xenomai") 3. create the registry root manually with proper permissions, 1777 is recommended if non-privileged processes will belong to the session 4. if --shared-registry is required from a non-privileged session initiator (i.e. the first process establishing the session), set user_allow_other in /etc/fuse.conf 5. to start a session with group-based access control, suffix the session name with the allowed group name or id separated with a slash ('/') when starting the session initiator, i.e. --session name/<group-id|group-name>. For instance, with {user} a member of the "xenomai" group: /* The initiator of session 'foo' is [root] */ [root] ./program --session foo/xenomai /* Bind a non-privileged process from {user} to session 'foo' */ {user} ./program --session foo Or, with {user1} and {user2} both members of the "xenomai" group: /* The initiator of session 'foo' is {user1} */ {user1} ./program --session foo/xenomai /* Bind a process from {user2} to session 'foo' */ {user2} ./program --session foo --- include/copperplate/heapobj.h | 8 ------ include/copperplate/threadobj.h | 3 +- include/copperplate/tunables.h | 11 ++++++++ lib/copperplate/cluster.c | 5 +++- lib/copperplate/heapobj-pshared.c | 55 ++++++++++++++++++++++++++++--------- lib/copperplate/init.c | 47 +++++++++++++++++++++++++++++-- lib/copperplate/internal.c | 10 +++++++ lib/copperplate/internal.h | 2 ++ lib/copperplate/registry.c | 5 ++-- lib/copperplate/threadobj.c | 50 +++++++++++++++++++++------------ 10 files changed, 150 insertions(+), 46 deletions(-) diff --git a/include/copperplate/heapobj.h b/include/copperplate/heapobj.h index c61cf3e..4cf947e 100644 --- a/include/copperplate/heapobj.h +++ b/include/copperplate/heapobj.h @@ -192,11 +192,6 @@ struct sysgroup_memspec { struct holder next; }; -struct agent_memspec { - /** Agent pid in owner process. */ - pid_t pid; -}; - static inline void *mainheap_ptr(memoff_t off) { return off ? (void *)__memptr(__main_heap, off) : NULL; @@ -326,9 +321,6 @@ char *xnstrdup(const char *ptr); struct sysgroup_memspec { }; -struct agent_memspec { -}; - /* * Whether an object is laid in some shared heap. Never if pshared * mode is disabled. diff --git a/include/copperplate/threadobj.h b/include/copperplate/threadobj.h index 1d01709..f27c111 100644 --- a/include/copperplate/threadobj.h +++ b/include/copperplate/threadobj.h @@ -212,7 +212,6 @@ struct threadobj { struct traceobj *tracer; sem_t *cancel_sem; struct sysgroup_memspec memspec; - struct agent_memspec agent; struct backtrace_data btd; }; @@ -392,7 +391,7 @@ static inline int threadobj_local_p(struct threadobj *thobj) void threadobj_init_key(void); -int threadobj_pkg_init(void); +int threadobj_pkg_init(int anon_session); #ifdef __cplusplus } diff --git a/include/copperplate/tunables.h b/include/copperplate/tunables.h index 8428716..640b8b4 100644 --- a/include/copperplate/tunables.h +++ b/include/copperplate/tunables.h @@ -27,6 +27,7 @@ struct copperplate_setup_data { int no_registry; int shared_registry; size_t mem_pool; + gid_t session_gid; }; #ifdef __cplusplus @@ -85,6 +86,16 @@ static inline read_config_tunable(mem_pool_size, size_t) return __copperplate_setup_data.mem_pool; } +static inline define_config_tunable(session_gid, gid_t, gid) +{ + __copperplate_setup_data.session_gid = gid; +} + +static inline read_config_tunable(session_gid, gid_t) +{ + return __copperplate_setup_data.session_gid; +} + #ifdef __cplusplus } #endif diff --git a/lib/copperplate/cluster.c b/lib/copperplate/cluster.c index eac0afe..f0552f0 100644 --- a/lib/copperplate/cluster.c +++ b/lib/copperplate/cluster.c @@ -174,8 +174,11 @@ static int cluster_probe(struct hashobj *hobj) * we can send the latter a signal, the node is deemed active. * Over Cobalt, the main thread is always shadowed, therefore * we may use Cobalt's kill() service to probe for it. + * Receiving EPERM does mean that we found an active node, + * just that we don't have the credentials to actually send it + * a signal. */ - return __RT(kill(cobj->cnode, 0)) == 0; + return copperplate_probe_tid(cobj->cnode) == 0; } int cluster_addobj(struct cluster *c, const char *name, diff --git a/lib/copperplate/heapobj-pshared.c b/lib/copperplate/heapobj-pshared.c index c1631e5..38b4bca 100644 --- a/lib/copperplate/heapobj-pshared.c +++ b/lib/copperplate/heapobj-pshared.c @@ -620,6 +620,7 @@ out: static int create_main_heap(pid_t *cnode_r) { const char *session = __copperplate_setup_data.session_label; + gid_t gid =__copperplate_setup_data.session_gid; size_t size = __copperplate_setup_data.mem_pool; struct heapobj *hobj = &main_pool; struct session_heap *m_heap; @@ -666,21 +667,26 @@ static int create_main_heap(pid_t *cnode_r) return __bt(-errno); ret = flock(fd, LOCK_EX); - if (ret) + if (__bterrno(ret)) goto errno_fail; ret = fstat(fd, &sbuf); - if (ret) + if (__bterrno(ret)) goto errno_fail; if (sbuf.st_size == 0) goto init; m_heap = __STD(mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)); - if (m_heap == MAP_FAILED) - goto errno_fail; + if (m_heap == MAP_FAILED) { + ret = __bt(-errno); + goto close_fail; + } - if (m_heap->cpid && kill(m_heap->cpid, 0) == 0) { + if (m_heap->cpid == 0) + goto reset; + + if (copperplate_probe_tid(m_heap->cpid) == 0) { if (m_heap->maplen == len) { /* CAUTION: __moff() depends on __main_heap. */ __main_heap = m_heap; @@ -693,25 +699,47 @@ static int create_main_heap(pid_t *cnode_r) __STD(close(fd)); return __bt(-EEXIST); } - +reset: munmap(m_heap, len); + /* + * Reset shared memory ownership to revoke permissions from a + * former session with more permissive access rules, such as + * group-controlled access. + */ + fchown(fd, geteuid(), getegid()); init: ret = ftruncate(fd, 0); /* Clear all previous contents if any. */ - if (ret) + if (__bterrno(ret)) goto unlink_fail; ret = ftruncate(fd, len); - if (ret) + if (__bterrno(ret)) goto unlink_fail; + /* + * If we need to share the heap between members of a group, + * give the group RW access to the shared memory file backing + * the heap. + */ + if (gid != USHRT_MAX) { + ret = fchown(fd, geteuid(), gid); + if (__bterrno(ret) < 0) + goto unlink_fail; + ret = fchmod(fd, 0660); + if (__bterrno(ret) < 0) + goto unlink_fail; + } + m_heap = __STD(mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)); - if (m_heap == MAP_FAILED) + if (m_heap == MAP_FAILED) { + ret = __bt(-errno); goto unlink_fail; + } m_heap->maplen = len; /* CAUTION: init_main_heap() depends on hobj->pool_ref. */ hobj->pool_ref = __moff(&m_heap->heap); - ret = init_main_heap(m_heap, size); + ret = __bt(init_main_heap(m_heap, size)); if (ret) { errno = -ret; goto unmap_fail; @@ -731,7 +759,7 @@ done: unmap_fail: munmap(m_heap, len); unlink_fail: - ret = __bt(-errno); + ret = -errno; shm_unlink(hobj->fsname); goto close_fail; errno_fail: @@ -781,7 +809,7 @@ static int bind_main_heap(const char *session) cpid = m_heap->cpid; __STD(close(fd)); - if (cpid == 0 || kill(cpid, 0)) { + if (cpid == 0 || copperplate_probe_tid(cpid)) { munmap(m_heap, len); return -ENOENT; } @@ -905,7 +933,8 @@ void heapobj_destroy(struct heapobj *hobj) } cpid = main_heap.cpid; - if (cpid != get_thread_pid() && kill(cpid, 0) == 0) { + if (cpid != 0 && cpid != get_thread_pid() && + copperplate_probe_tid(cpid) == 0) { munmap(&main_heap, main_heap.maplen); return; } diff --git a/lib/copperplate/init.c b/lib/copperplate/init.c index d24673d..a9b4efd 100644 --- a/lib/copperplate/init.c +++ b/lib/copperplate/init.c @@ -25,6 +25,7 @@ #include <pwd.h> #include <errno.h> #include <getopt.h> +#include <grp.h> #include "copperplate/threadobj.h" #include "copperplate/heapobj.h" #include "copperplate/clockobj.h" @@ -39,6 +40,7 @@ struct copperplate_setup_data __copperplate_setup_data = { .registry_root = DEFAULT_REGISTRY_ROOT, .session_label = NULL, .session_root = NULL, + .session_gid = USHRT_MAX, }; #ifdef CONFIG_XENO_COBALT @@ -145,6 +147,42 @@ static int get_session_root(int *regflags_r) return 0; } +static int get_session_label(const char *optarg) +{ + char *session, *grpname, *p; + struct group *grp; + gid_t gid; + + session = strdup(optarg); + grpname = strrchr(session, '/'); + if (grpname == NULL) + goto no_group; + + *grpname++ = '\0'; + + if (isdigit(*grpname)) { + gid = (gid_t)strtol(grpname, &p, 10); + if (*p) + return -EINVAL; + errno = 0; + grp = getgrgid(gid); + } else { + errno = 0; + grp = getgrnam(grpname); + } + + if (grp == NULL) { + warning("invalid group %s", grpname); + return errno ? -errno : -EINVAL; + } + + __copperplate_setup_data.session_gid = grp->gr_gid; +no_group: + __copperplate_setup_data.session_label = session; + + return 0; +} + static int copperplate_init(void) { int ret, regflags = 0; @@ -177,7 +215,7 @@ static int copperplate_init(void) return ret; } - ret = threadobj_pkg_init(); + ret = threadobj_pkg_init((regflags & REGISTRY_ANON) != 0); if (ret) { warning("failed to initialize multi-threading package"); return ret; @@ -195,6 +233,7 @@ static int copperplate_init(void) static int copperplate_parse_option(int optnum, const char *optarg) { size_t memsz; + int ret; switch (optnum) { case mempool_opt: @@ -214,7 +253,9 @@ static int copperplate_parse_option(int optnum, const char *optarg) __copperplate_setup_data.mem_pool = memsz; break; case session_opt: - __copperplate_setup_data.session_label = strdup(optarg); + ret = get_session_label(optarg); + if (ret) + return ret; break; case regroot_opt: __copperplate_setup_data.registry_root = strdup(optarg); @@ -236,7 +277,7 @@ static void copperplate_help(void) fprintf(stderr, "--no-registry suppress object registration\n"); fprintf(stderr, "--shared-registry enable public access to registry\n"); fprintf(stderr, "--registry-root=<path> root path of registry\n"); - fprintf(stderr, "--session=<label> label of shared multi-processing session\n"); + fprintf(stderr, "--session=<label>[/<group>] enable shared session\n"); } static struct setup_descriptor copperplate_interface = { diff --git a/lib/copperplate/internal.c b/lib/copperplate/internal.c index b9751e4..eeb40e1 100644 --- a/lib/copperplate/internal.c +++ b/lib/copperplate/internal.c @@ -91,6 +91,11 @@ int copperplate_kill_tid(pid_t tid, int sig) return __RT(kill(tid, sig)) ? -errno : 0; } +int copperplate_probe_tid(pid_t tid) +{ + return copperplate_kill_tid(tid, 0); +} + void copperplate_set_current_name(const char *name) { __RT(pthread_setname_np(pthread_self(), name)); @@ -103,6 +108,11 @@ int copperplate_kill_tid(pid_t tid, int sig) return syscall(__NR_tkill, tid, sig) ? -errno : 0; } +int copperplate_probe_tid(pid_t tid) +{ + return copperplate_kill_tid(tid, 0) && errno != EPERM ? -errno : 0; +} + void copperplate_set_current_name(const char *name) { prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); diff --git a/lib/copperplate/internal.h b/lib/copperplate/internal.h index 6369694..23047b4 100644 --- a/lib/copperplate/internal.h +++ b/lib/copperplate/internal.h @@ -85,6 +85,8 @@ int copperplate_get_current_name(char *name, size_t maxlen); int copperplate_kill_tid(pid_t tid, int sig); +int copperplate_probe_tid(pid_t tid); + int copperplate_create_thread(struct corethread_attributes *cta, pthread_t *ptid); diff --git a/lib/copperplate/registry.c b/lib/copperplate/registry.c index 52c0f64..1df7d6e 100644 --- a/lib/copperplate/registry.c +++ b/lib/copperplate/registry.c @@ -821,9 +821,10 @@ int registry_pkg_init(const char *arg0, int flags) char *mountpt; int ret; - ret = connect_regd(__copperplate_setup_data.session_root, &mountpt, flags); + ret = connect_regd(__copperplate_setup_data.session_root, + &mountpt, flags); if (ret) - return ret; + return __bt(ret); return __bt(__registry_pkg_init(arg0, mountpt, flags)); } diff --git a/lib/copperplate/threadobj.c b/lib/copperplate/threadobj.c index 961d8c4..2f25992 100644 --- a/lib/copperplate/threadobj.c +++ b/lib/copperplate/threadobj.c @@ -168,6 +168,13 @@ static inline int send_agent(struct threadobj *thobj, struct remote_request *rq) { union sigval val = { .sival_ptr = rq }; + + /* + * We are not supposed to issue remote requests when nobody + * else may share our session. + */ + assert(agent_pid != 0); + /* * XXX: No backtracing, may legitimately fail if the remote * process goes away (hopefully cleanly). However, the request @@ -176,7 +183,7 @@ static inline int send_agent(struct threadobj *thobj, * creating user threads are unlikely to ungracefully leave * the session they belong to intentionally. */ - return __RT(sigqueue(thobj->agent.pid, SIGAGENT, val)); + return __RT(sigqueue(agent_pid, SIGAGENT, val)); } static void start_agent(void) @@ -197,7 +204,7 @@ static void start_agent(void) sigaddset(&set, SIGAGENT); pthread_sigmask(SIG_BLOCK, &set, NULL); - cta.policy = SCHED_CORE; + cta.policy = threadobj_agent_prio ? SCHED_CORE : SCHED_OTHER; cta.param_ex.sched_priority = threadobj_agent_prio; cta.prologue = agent_prologue; cta.run = agent_loop; @@ -210,11 +217,6 @@ static void start_agent(void) panic("failed to start agent thread, %s", symerror(ret)); } -static void threadobj_set_agent(struct threadobj *thobj) -{ - thobj->agent.pid = agent_pid; -} - #else /* !CONFIG_XENO_PSHARED */ static inline void start_agent(void) @@ -222,11 +224,6 @@ static inline void start_agent(void) /* No agent in private (process-local) session. */ } -static inline void threadobj_set_agent(struct threadobj *thobj) -{ - /* nop */ -} - #endif /* !CONFIG_XENO_PSHARED */ #ifdef CONFIG_XENO_COBALT @@ -235,8 +232,15 @@ static inline void threadobj_set_agent(struct threadobj *thobj) static inline void pkg_init_corespec(void) { + /* + * We must have CAP_SYS_NICE since we reached this code either + * as root or as a member of the allowed group, as a result of + * binding the current process to the Cobalt core earlier in + * libcobalt's setup code. + */ threadobj_irq_prio = sched_get_priority_max_ex(SCHED_CORE); threadobj_high_prio = sched_get_priority_max_ex(SCHED_FIFO); + threadobj_agent_prio = threadobj_high_prio; } static inline int threadobj_init_corespec(struct threadobj *thobj) @@ -556,6 +560,18 @@ static inline void pkg_init_corespec(void) threadobj_irq_prio = sched_get_priority_max(SCHED_FIFO); threadobj_lock_prio = threadobj_irq_prio - 1; threadobj_high_prio = threadobj_irq_prio - 2; + threadobj_agent_prio = threadobj_high_prio; + /* + * We allow a non-privileged process to start a low priority + * agent thread only, on the assumption that it lacks + * CAP_SYS_NICE, but this is pretty much the maximum extent of + * our abilities for such processes. Other internal threads + * requiring SCHED_CORE/FIFO scheduling such as the timer + * manager won't start properly, therefore the corresponding + * services won't be available. + */ + if (geteuid()) + threadobj_agent_prio = 0; memset(&sa, 0, sizeof(sa)); sa.sa_handler = unblock_sighandler; @@ -1322,7 +1338,6 @@ int threadobj_prologue(struct threadobj *thobj, const char *name) thobj->ptid = pthread_self(); thobj->pid = get_thread_pid(); thobj->errno_pointer = &errno; - threadobj_set_agent(thobj); backtrace_init_context(&thobj->btd, name); ret = threadobj_setup_corespec(thobj); if (ret) { @@ -1569,7 +1584,7 @@ int threadobj_sleep(const struct timespec *ts) */ if (ts->tv_sec == 0 && ts->tv_nsec == 0) { sigemptyset(&set); - ret = __RT(sigwaitinfo(&set, NULL)) ? errno : 0; + ret = __RT(sigwaitinfo(&set, NULL)) ? -errno : 0; } else ret = __RT(clock_nanosleep(CLOCK_COPPERPLATE, TIMER_ABSTIME, ts, NULL)); @@ -1751,12 +1766,13 @@ static inline int main_overlay(void) return 0; } -int threadobj_pkg_init(void) +int threadobj_pkg_init(int anon_session) { sigaddset(&sigperiod_set, SIGPERIOD); pkg_init_corespec(); - threadobj_agent_prio = threadobj_high_prio; - start_agent(); + + if (!anon_session) + start_agent(); return main_overlay(); } _______________________________________________ Xenomai-git mailing list Xenomai-git@xenomai.org http://xenomai.org/mailman/listinfo/xenomai-git