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>
This commit is contained in:
Mark Stapp 2019-03-05 15:28:26 -05:00
parent b19abe1131
commit c5c44d4b41
2 changed files with 29 additions and 0 deletions

View File

@ -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. */

View File

@ -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 */