summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Young <qlyoung@users.noreply.github.com>2019-04-25 14:40:05 -0400
committerGitHub <noreply@github.com>2019-04-25 14:40:05 -0400
commit11db6e2ffcbee0606187a8fc48b537668d3321ed (patch)
treeac5f6cc9f518fb4096b05829f66da3eaa994d360
parent7c682a5387c42005bc40a72c820ae6ac94384022 (diff)
parente73b1383c28117f7bccbdbd988bb7cc9e7923877 (diff)
Merge pull request #4195 from mjstapp/fix_privs_even_more_7_0
[7.0] lib: control privs changes with refcount
-rw-r--r--lib/privs.c119
-rw-r--r--lib/privs.h19
2 files changed, 110 insertions, 28 deletions
diff --git a/lib/privs.c b/lib/privs.c
index 3ce8e0d57a..a19707b1c9 100644
--- a/lib/privs.c
+++ b/lib/privs.c
@@ -25,10 +25,25 @@
#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;
diff --git a/lib/privs.h b/lib/privs.h
index c7f5dc249c..1e2be45dc5 100644
--- a/lib/privs.h
+++ b/lib/privs.h
@@ -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 {