From c5c44d4b41717484f23e11882e85983585b59e95 Mon Sep 17 00:00:00 2001
From: Mark Stapp <mjs@voltanet.io>
Date: Tue, 5 Mar 2019 15:28:26 -0500
Subject: libs: make privilege escalation thread-safe

Privs escalation is process-wide, and a multi-threaded process
can deadlock. This adds a mutex and a counter to the privs
object, preventing multiple threads from making the privs
escalation system call.

Signed-off-by: Mark Stapp <mjs@voltanet.io>
---
 lib/privs.c | 19 +++++++++++++++++++
 lib/privs.h | 10 ++++++++++
 2 files changed, 29 insertions(+)

(limited to 'lib')

diff --git a/lib/privs.c b/lib/privs.c
index 293280007..3ce8e0d57 100644
--- a/lib/privs.c
+++ b/lib/privs.c
@@ -706,6 +706,14 @@ struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs,
 	if (!privs)
 		return NULL;
 
+	/* If we're already elevated, just return */
+	pthread_mutex_lock(&(privs->mutex));
+	if (++privs->refcount > 1) {
+		pthread_mutex_unlock(&(privs->mutex));
+		return privs;
+	}
+	pthread_mutex_unlock(&(privs->mutex));
+
 	errno = 0;
 	if (privs->change(ZPRIVS_RAISE)) {
 		zlog_err("%s: Failed to raise privileges (%s)",
@@ -723,6 +731,14 @@ void _zprivs_lower(struct zebra_privs_t **privs)
 	if (!*privs)
 		return;
 
+	/* Don't lower privs if there's another caller */
+	pthread_mutex_lock(&(*privs)->mutex);
+	if (--((*privs)->refcount) > 0) {
+		pthread_mutex_unlock(&(*privs)->mutex);
+		return;
+	}
+	pthread_mutex_unlock(&(*privs)->mutex);
+
 	errno = 0;
 	if ((*privs)->change(ZPRIVS_LOWER)) {
 		zlog_err("%s: Failed to lower privileges (%s)",
@@ -743,6 +759,9 @@ void zprivs_preinit(struct zebra_privs_t *zprivs)
 		exit(1);
 	}
 
+	pthread_mutex_init(&(zprivs->mutex), NULL);
+	zprivs->refcount = 0;
+
 	if (zprivs->vty_group) {
 		/* in a "NULL" setup, this is allowed to fail too, but still
 		 * try. */
diff --git a/lib/privs.h b/lib/privs.h
index 1fee423a9..01ddba462 100644
--- a/lib/privs.h
+++ b/lib/privs.h
@@ -23,6 +23,8 @@
 #ifndef _ZEBRA_PRIVS_H
 #define _ZEBRA_PRIVS_H
 
+#include <pthread.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -59,6 +61,14 @@ struct zebra_privs_t {
 	zebra_capabilities_t *caps_i; /* caps to allow inheritance of */
 	int cap_num_p;		      /* number of caps in arrays */
 	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.
+	 */
+	pthread_mutex_t mutex;
+	uint32_t refcount;
+
 	const char *user; /* user and group to run as */
 	const char *group;
 	const char *vty_group; /* group to chown vty socket to */
-- 
cgit v1.2.3