Module: xenomai-3
Branch: next
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

Reply via email to