From: David Lamparter Date: Fri, 10 Aug 2018 15:35:29 +0000 (+0200) Subject: lib: add frr_elevate_privs() wrapper X-Git-Tag: frr-6.1-dev~61^2~4 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=6017c3a2e71304381af5cfa5020b4a1358ee098b;p=matthieu%2Ffrr.git lib: add frr_elevate_privs() wrapper Used as: frr_elevate_privs(&my_privs) { ... code ... } and handles privilege raise/lower automatically in conjunction with the C expression block. This makes it impossible to accidentally exit a function with privileges raised (and then running a whole bunch of other code with privs.) Signed-off-by: David Lamparter --- diff --git a/lib/privs.c b/lib/privs.c index 7c99742d34..34905ca480 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -696,6 +696,41 @@ static int getgrouplist(const char *user, gid_t group, gid_t *groups, } #endif /* HAVE_GETGROUPLIST */ +struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs, + const char *funcname) +{ + int save_errno = errno; + + if (!privs) + return NULL; + + 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; + + if (!*privs) + return; + + 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; +} + void zprivs_preinit(struct zebra_privs_t *zprivs) { struct passwd *pwentry = NULL; diff --git a/lib/privs.h b/lib/privs.h index 7fe59328b2..b061370b75 100644 --- a/lib/privs.h +++ b/lib/privs.h @@ -62,6 +62,7 @@ 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 { @@ -81,4 +82,42 @@ extern void zprivs_terminate(struct zebra_privs_t *); /* query for runtime uid's and gid's, eg vty needs this */ extern void zprivs_get_ids(struct zprivs_ids_t *); +/* + * Wrapper around zprivs, to be used as: + * frr_elevate_privs(&privs) { + * ... code ... + * if (error) + * break; -- break can be used to get out of the block + * ... code ... + * } + * + * The argument to frr_elevate_privs() can be NULL to leave privileges as-is + * (mostly useful for conditional privilege-raising, i.e.:) + * frr_elevate_privs(cond ? &privs : NULL) {} + * + * NB: The code block is always executed, regardless of whether privileges + * could be raised or not, or whether NULL was given or not. This is fully + * intentional; the user may have configured some RBAC or similar that we + * are not aware of, but that allows our code to proceed without privileges. + * + * The point of this wrapper is to prevent accidental bugs where privileges + * are elevated but then not dropped. This can happen when, for example, a + * "return", "goto" or "break" in the middle of the elevated-privilege code + * skips past the privilege dropping call. + * + * The macro below uses variable cleanup to drop privileges as soon as the + * code block is left in any way (and thus the _privs variable goes out of + * scope.) _once is just a trick to run the loop exactly once. + */ +extern struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs, + const char *funcname); +extern void _zprivs_lower(struct zebra_privs_t **privs); + +#define frr_elevate_privs(privs) \ + for (struct zebra_privs_t *_once = NULL, \ + *_privs __attribute__( \ + (unused, cleanup(_zprivs_lower))) = \ + _zprivs_raise(privs, __func__); \ + _once == NULL; _once = (void *)1) + #endif /* _ZEBRA_PRIVS_H */