summaryrefslogtreecommitdiffstats
path: root/lib/privs.c
diff options
context:
space:
mode:
authorMark Stapp <mjs@voltanet.io>2019-04-02 11:01:27 +0200
committerMark Stapp <mjs@voltanet.io>2019-04-22 15:32:41 +0200
commit8875d0515ec970a6779f1c485314e70964487122 (patch)
tree1f5760d982c59e0c253b95317291ac8ed98b7cf5 /lib/privs.c
parentMerge pull request #4166 from donaldsharp/pim_s_g (diff)
downloadfrr-8875d0515ec970a6779f1c485314e70964487122.tar.xz
frr-8875d0515ec970a6779f1c485314e70964487122.zip
libs: control privs changes with refcount
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>
Diffstat (limited to 'lib/privs.c')
-rw-r--r--lib/privs.c88
1 files changed, 79 insertions, 9 deletions
diff --git a/lib/privs.c b/lib/privs.c
index 59f24afe4..a19707b1c 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,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;