]> git.proxmox.com Git - mirror_frr.git/commitdiff
lib: control privs changes with refcount
authorMark Stapp <mjs@voltanet.io>
Wed, 24 Apr 2019 19:20:02 +0000 (15:20 -0400)
committerMark Stapp <mjs@voltanet.io>
Wed, 24 Apr 2019 19:20:02 +0000 (15:20 -0400)
Use a refcount to control privs changes. Support process-wide
privs apis, as well as per-pthread apis. Double-commit of
PR 4057.

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

index 577e8ba6c4c79d6c0dc195f5d69bb741ffb19e04..54c30107cf906aef54e29624aa89e9f6ac0f4e18 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,54 +713,101 @@ 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) {
-               pthread_mutex_unlock(&(privs->mutex));
-               return privs;
+       {
+               /* 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;
+                       refs->raised_in_funcname = funcname;
+               }
        }
        pthread_mutex_unlock(&(privs->mutex));
 
-       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;
        return 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) {
-               pthread_mutex_unlock(&(*privs)->mutex);
-               return;
+       {
+               refs = get_privs_refs(*privs);
+
+               if (--(refs->refcount) == 0) {
+                       errno = 0;
+                       if ((*privs)->change(ZPRIVS_LOWER)) {
+                               zlog_err("%s: Failed to lower privileges (%s)",
+                                        refs->raised_in_funcname,
+                                        safe_strerror(errno));
+                       }
+                       errno = save_errno;
+                       refs->raised_in_funcname = NULL;
+               }
        }
        pthread_mutex_unlock(&(*privs)->mutex);
 
-       errno = 0;
-       if ((*privs)->change(ZPRIVS_LOWER)) {
-               zlog_err("%s: Failed to lower privileges (%s)",
-                        (*privs)->raised_in_funcname, safe_strerror(errno));
-       }
-       errno = save_errno;
-       (*privs)->raised_in_funcname = NULL;
        *privs = NULL;
 }
 
@@ -760,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
@@ -918,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__);
@@ -940,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 c7f5dc249c7b34d9df7532fd9b1a23bc71cf4c8d..1e2be45dc58fe40479e288a7084a6333b5ef3891 100644 (file)
@@ -24,6 +24,7 @@
 #define _ZEBRA_PRIVS_H
 
 #include <pthread.h>
+#include "lib/queue.h"
 
 /* list of zebra capabilities */
 typedef enum {
@@ -52,6 +53,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 */
@@ -59,11 +67,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;
@@ -72,7 +84,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 {