]> git.puffer.fish Git - mirror/frr.git/commitdiff
libs: control privs changes with refcount 4057/head
authorMark Stapp <mjs@voltanet.io>
Tue, 2 Apr 2019 09:01:27 +0000 (05:01 -0400)
committerMark Stapp <mjs@voltanet.io>
Mon, 22 Apr 2019 13:32:41 +0000 (09:32 -0400)
Use a refcount to control privs changes. Support process-wide
privs apis, as well as per-pthread apis.

Signed-off-by: Mark Stapp <mjs@voltanet.io>
lib/privs.c
lib/privs.h

index 59f24afe4a5aecc4a84f47b18a778401ed8d4dd1..a19707b1c928c1c7866ee5d5a3c08775cad52f57 100644 (file)
 #include "privs.h"
 #include "memory.h"
 #include "lib_errors.h"
+#include "lib/queue.h"
 
+DEFINE_MTYPE_STATIC(LIB, PRIVS, "Privilege information")
+
+/*
+ * Different capabilities/privileges apis have different characteristics: some
+ * are process-wide, and some are per-thread.
+ */
 #ifdef HAVE_CAPABILITIES
+#ifdef HAVE_LCAPS
+static const bool privs_per_process;  /* = false */
+#elif defined(HAVE_SOLARIS_CAPABILITIES)
+static const bool privs_per_process = true;
+#endif
+#else
+static const bool privs_per_process = true;
+#endif /* HAVE_CAPABILITIES */
 
-DEFINE_MTYPE_STATIC(LIB, PRIVS, "Privilege information")
+#ifdef HAVE_CAPABILITIES
 
 /* sort out some generic internal types for:
  *
@@ -698,25 +713,66 @@ static int getgrouplist(const char *user, gid_t group, gid_t *groups,
 }
 #endif /* HAVE_GETGROUPLIST */
 
+/*
+ * Helper function that locates a refcounting object to use: a process-wide
+ * object or a per-pthread object.
+ */
+static struct zebra_privs_refs_t *get_privs_refs(struct zebra_privs_t *privs)
+{
+       struct zebra_privs_refs_t *temp, *refs = NULL;
+       pthread_t tid;
+
+       if (privs_per_process)
+               refs = &(privs->process_refs);
+       else {
+               /* Locate - or create - the object for the current pthread. */
+               tid = pthread_self();
+
+               STAILQ_FOREACH(temp, &(privs->thread_refs), entry) {
+                       if (pthread_equal(temp->tid, tid)) {
+                               refs = temp;
+                               break;
+                       }
+               }
+
+               /* Need to create a new refcounting object. */
+               if (refs == NULL) {
+                       refs = XCALLOC(MTYPE_PRIVS,
+                                      sizeof(struct zebra_privs_refs_t));
+                       refs->tid = tid;
+                       STAILQ_INSERT_TAIL(&(privs->thread_refs), refs, entry);
+               }
+       }
+
+       return refs;
+}
+
 struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs,
                                    const char *funcname)
 {
        int save_errno = errno;
+       struct zebra_privs_refs_t *refs;
 
        if (!privs)
                return NULL;
 
-       /* If we're already elevated, just return */
+       /*
+        * Serialize 'raise' operations; particularly important for
+        * OSes where privs are process-wide.
+        */
        pthread_mutex_lock(&(privs->mutex));
        {
-               if (++(privs->refcount) == 1) {
+               /* Locate ref-counting object to use */
+               refs = get_privs_refs(privs);
+
+               if (++(refs->refcount) == 1) {
                        errno = 0;
                        if (privs->change(ZPRIVS_RAISE)) {
                                zlog_err("%s: Failed to raise privileges (%s)",
                                         funcname, safe_strerror(errno));
                        }
                        errno = save_errno;
-                       privs->raised_in_funcname = funcname;
+                       refs->raised_in_funcname = funcname;
                }
        }
        pthread_mutex_unlock(&(privs->mutex));
@@ -727,22 +783,27 @@ struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs,
 void _zprivs_lower(struct zebra_privs_t **privs)
 {
        int save_errno = errno;
+       struct zebra_privs_refs_t *refs;
 
        if (!*privs)
                return;
 
-       /* Don't lower privs if there's another caller */
+       /* Serialize 'lower privs' operation - particularly important
+        * when OS privs are process-wide.
+        */
        pthread_mutex_lock(&(*privs)->mutex);
        {
-               if (--((*privs)->refcount) == 0) {
+               refs = get_privs_refs(*privs);
+
+               if (--(refs->refcount) == 0) {
                        errno = 0;
                        if ((*privs)->change(ZPRIVS_LOWER)) {
                                zlog_err("%s: Failed to lower privileges (%s)",
-                                        (*privs)->raised_in_funcname,
+                                        refs->raised_in_funcname,
                                         safe_strerror(errno));
                        }
                        errno = save_errno;
-                       (*privs)->raised_in_funcname = NULL;
+                       refs->raised_in_funcname = NULL;
                }
        }
        pthread_mutex_unlock(&(*privs)->mutex);
@@ -761,7 +822,9 @@ void zprivs_preinit(struct zebra_privs_t *zprivs)
        }
 
        pthread_mutex_init(&(zprivs->mutex), NULL);
-       zprivs->refcount = 0;
+       zprivs->process_refs.refcount = 0;
+       zprivs->process_refs.raised_in_funcname = NULL;
+       STAILQ_INIT(&zprivs->thread_refs);
 
        if (zprivs->vty_group) {
                /* in a "NULL" setup, this is allowed to fail too, but still
@@ -919,6 +982,8 @@ void zprivs_init(struct zebra_privs_t *zprivs)
 
 void zprivs_terminate(struct zebra_privs_t *zprivs)
 {
+       struct zebra_privs_refs_t *refs;
+
        if (!zprivs) {
                fprintf(stderr, "%s: no privs struct given, terminating",
                        __func__);
@@ -941,6 +1006,11 @@ void zprivs_terminate(struct zebra_privs_t *zprivs)
        }
 #endif /* HAVE_LCAPS */
 
+       while ((refs = STAILQ_FIRST(&(zprivs->thread_refs))) != NULL) {
+               STAILQ_REMOVE_HEAD(&(zprivs->thread_refs), entry);
+               XFREE(MTYPE_PRIVS, refs);
+       }
+
        zprivs->change = zprivs_change_null;
        zprivs->current_state = zprivs_state_null;
        zprivs_null_state = ZPRIVS_LOWERED;
index 01ddba46223e207a7eecca88a867dddce71e96af..2b0b44b3f2f7ca485a3a7e52303592fdc67fac2a 100644 (file)
@@ -24,6 +24,7 @@
 #define _ZEBRA_PRIVS_H
 
 #include <pthread.h>
+#include "lib/queue.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -56,6 +57,13 @@ typedef enum {
        ZPRIVS_LOWER,
 } zebra_privs_ops_t;
 
+struct zebra_privs_refs_t {
+       STAILQ_ENTRY(zebra_privs_refs_t) entry;
+       pthread_t tid;
+       uint32_t refcount;
+       const char *raised_in_funcname;
+};
+
 struct zebra_privs_t {
        zebra_capabilities_t *caps_p; /* caps required for operation */
        zebra_capabilities_t *caps_i; /* caps to allow inheritance of */
@@ -63,11 +71,15 @@ struct zebra_privs_t {
        int cap_num_i;
 
        /* Mutex and counter used to avoid race conditions in multi-threaded
-        * processes. The privs elevation is process-wide, so we need to
-        * avoid changing the privilege status across threads.
+        * processes. If privs status is process-wide, we need to
+        * control changes to the privilege status among threads.
+        * If privs changes are per-thread, we need to be able to
+        * manage that too.
         */
        pthread_mutex_t mutex;
-       uint32_t refcount;
+       struct zebra_privs_refs_t process_refs;
+
+       STAILQ_HEAD(thread_refs_q, zebra_privs_refs_t) thread_refs;
 
        const char *user; /* user and group to run as */
        const char *group;
@@ -76,7 +88,6 @@ struct zebra_privs_t {
        int (*change)(zebra_privs_ops_t); /* change privileges, 0 on success */
        zebra_privs_current_t (*current_state)(
                void); /* current privilege state */
-       const char *raised_in_funcname;
 };
 
 struct zprivs_ids_t {