diff options
| author | Quentin Young <qlyoung@users.noreply.github.com> | 2019-04-25 14:40:05 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-04-25 14:40:05 -0400 |
| commit | 11db6e2ffcbee0606187a8fc48b537668d3321ed (patch) | |
| tree | ac5f6cc9f518fb4096b05829f66da3eaa994d360 | |
| parent | 7c682a5387c42005bc40a72c820ae6ac94384022 (diff) | |
| parent | e73b1383c28117f7bccbdbd988bb7cc9e7923877 (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.c | 119 | ||||
| -rw-r--r-- | lib/privs.h | 19 |
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 { |
