From ceacedba227e77156f92f7676b274c48a2817e8f Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 29 Sep 2005 14:39:32 +0000 Subject: 2005-09-29 Paul Jakma * configure.ac: Add the test for Solaris least-privileges. Set defines for whether capabilities are supported and whether of the linux or solaris variety. Add missing-prototypes, missing-declarations, char-subscripts and cast-qual warnings to default cflags, cause Hasso enjoys warnings, and we really should clean the remaining ones up. (ie isisd..). * (*/*main.c) Update the zebra_capabilities_t arrays in the various daemons to match the changes made in lib/privs.h. * zebra.h: Solaris capabilities requires priv.h to be included. * privs.{c,h}: Add support for Solaris Least-Privileges. privs.h: Reduce some of the abstract capabilities, which do not have rough equivalents on both systems. Rename the net related caps to _NET, as they should have been in first place. (zprivs_terminate) should take the zebra_privs_t as argument so that it can update change pointer. Add an additional privilege state, ZPRIVS_UNKNOWN. * privs.c: (various capability functions) Add Solaris privileges variants. (zprivs_state) Use privs.c specific generic types to represent various capability/privilege related types, so that each can be typedef'd as appropriate on each platform. (zprivs_null_state) static added, to hold the state the null method should report (should be raised by default, and LOWERED if zprivs_terminate has been called) (zprivs_state_null) Report back the zprivs_null_state. (cap_map) Make it able to map abstract capability to multiple system capabilities. (zcaps2sys) Map to abstract capabilities to multiple system privileges/capabilities. (zprivs_init) move capability related init to seperate function, zprivs_caps_init. (zprivs_terminate) ditto, moved to zprivs_caps_terminate. Set the change_state callback to the NULL state, so the user can continue to run and use the callbacks. --- ChangeLog | 11 + bgpd/bgp_main.c | 2 +- configure.ac | 21 +- isisd/isis_main.c | 2 +- lib/ChangeLog | 30 +++ lib/privs.c | 618 ++++++++++++++++++++++++++++++++++++++++------------ lib/privs.h | 13 +- lib/zebra.h | 4 + ospf6d/ospf6_main.c | 2 +- ospfd/ospf_main.c | 5 +- ripd/rip_main.c | 2 +- ripngd/ripng_main.c | 2 +- zebra/main.c | 4 +- 13 files changed, 557 insertions(+), 159 deletions(-) diff --git a/ChangeLog b/ChangeLog index c4f8950b..603f5c44 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2005-09-29 Paul Jakma + + * configure.ac: Add the test for Solaris least-privileges. Set + defines for whether capabilities are supported and whether of + the linux or solaris variety. + Add missing-prototypes, missing-declarations, char-subscripts + and cast-qual warnings to default cflags, cause Hasso enjoys warnings, + and we really should clean the remaining ones up. (ie isisd..). + * (*/*main.c) Update the zebra_capabilities_t arrays in the various + daemons to match the changes made in lib/privs.h. + 2005-09-19 Hasso Tepper * configure.ac: Test existance of strndup. diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 6de0a8a0..f9f0373d 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -102,7 +102,7 @@ char *vty_addr = NULL; zebra_capabilities_t _caps_p [] = { ZCAP_BIND, - ZCAP_RAW, + ZCAP_NET_RAW, }; struct zebra_privs_t bgpd_privs = diff --git a/configure.ac b/configure.ac index 7aa9844c..aca49e90 100755 --- a/configure.ac +++ b/configure.ac @@ -5,7 +5,7 @@ ## Copyright (c) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro ## Portions Copyright (c) 2003 Paul Jakma ## -## $Id: configure.ac,v 1.112 2005/09/19 09:53:21 hasso Exp $ +## $Id: configure.ac,v 1.113 2005/09/29 14:39:32 paul Exp $ AC_PREREQ(2.53) AC_INIT(Quagga, 0.99.1, [http://bugzilla.quagga.net]) @@ -91,6 +91,8 @@ if test "x$cflags_specified" = "x" ; then CFLAGS="-Os -fno-omit-frame-pointer -g -Wall" CFLAGS="${CFLAGS} -Wsign-compare -Wpointer-arith" CFLAGS="${CFLAGS} -Wbad-function-cast -Wwrite-strings" + CFLAGS="${CFLAGS} -Wmissing-prototypes -Wmissing-declarations" + CFLAGS="${CFLAGS} -Wchar-subscripts -Wcast-qual" fi # TODO: conditionally addd -Wpacked if handled fi @@ -1238,8 +1240,23 @@ if test "${enable_capabilities}" != "no"; then AC_CHECK_LIB(cap, cap_init, [AC_DEFINE(HAVE_LCAPS,1,Capabilities) LIBCAP="-lcap" - ] + quagga_ac_lcaps="yes"] ) + else + AC_CHECK_HEADERS(priv.h, + [AC_MSG_CHECKING(Solaris style privileges are available) + AC_TRY_COMPILE([#include ],[getpflags(PRIV_AWARE);], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SOLARIS_CAPABILITIES,1,getpflags) + quagga_ac_scaps="yes"], + AC_MSG_RESULT(no) + ) + ] + ) + fi + if test x"${quagga_ac_scaps}" = x"yes" \ + -o x"${quagga_ac_lcaps}" = x"yes"; then + AC_DEFINE(HAVE_CAPABILITIES,1,capabilities) fi fi AC_SUBST(LIBCAP) diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 2bb22cb6..bc90847b 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -52,7 +52,7 @@ /* isisd privileges */ zebra_capabilities_t _caps_p[] = { - ZCAP_RAW, + ZCAP_NET_RAW, ZCAP_BIND }; diff --git a/lib/ChangeLog b/lib/ChangeLog index e0605022..2a8256f8 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,33 @@ +2005-09-29 Paul Jakma + + * zebra.h: Solaris capabilities requires priv.h to be included. + * privs.{c,h}: Add support for Solaris Least-Privileges. + privs.h: Reduce some of the abstract capabilities, which do + not have rough equivalents on both systems. Rename the net + related caps to _NET, as they should have been in first + place. + (zprivs_terminate) should take the zebra_privs_t as argument so + that it can update change pointer. + Add an additional privilege state, ZPRIVS_UNKNOWN. + * privs.c: (various capability functions) Add + Solaris privileges variants. + (zprivs_state) Use privs.c specific generic types to + represent various capability/privilege related types, so that + each can be typedef'd as appropriate on each platform. + (zprivs_null_state) static added, to hold the state the null + method should report (should be raised by default, and + LOWERED if zprivs_terminate has been called) + (zprivs_state_null) Report back the zprivs_null_state. + (cap_map) Make it able to map abstract capability to multiple + system capabilities. + (zcaps2sys) Map to abstract capabilities to multiple system + privileges/capabilities. + (zprivs_init) move capability related init to seperate + function, zprivs_caps_init. + (zprivs_terminate) ditto, moved to zprivs_caps_terminate. + Set the change_state callback to the NULL state, so the + user can continue to run and use the callbacks. + 2005-09-29 Alain Ritoux * filer.c: show protocol name in filter_show() diff --git a/lib/privs.c b/lib/privs.c index 66681dab..9f36db06 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -2,6 +2,7 @@ * Zebra privileges. * * Copyright (C) 2003 Paul Jakma. + * Copyright (C) 2005 Sun Microsystems, Inc. * * This file is part of GNU Zebra. * @@ -20,22 +21,55 @@ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ - #include #include "log.h" #include "privs.h" #include "memory.h" +#ifdef HAVE_CAPABILITIES +/* sort out some generic internal types for: + * + * privilege values (cap_value_t, priv_t) -> pvalue_t + * privilege set (..., priv_set_t) -> pset_t + * privilege working storage (cap_t, ...) -> pstorage_t + * + * values we think of as numeric (they're ints really, but we dont know) + * sets are mostly opaque, to hold a set of privileges, related in some way. + * storage binds together a set of sets we're interested in. + * (in reality: cap_value_t and priv_t are ints) + */ +#ifdef HAVE_LCAPS +/* Linux doesn't have a 'set' type: a set of related privileges */ +struct _pset { + int num; + cap_value_t *caps; +}; +typedef cap_value_t pvalue_t; +typedef struct _pset pset_t; +typedef cap_t pstorage_t; + +#elif defined (HAVE_SOLARIS_CAPABILITIES) +typedef priv_t pvalue_t; +typedef priv_set_t pset_t; +typedef priv_set_t *pstorage_t; +#else /* neither LCAPS nor SOLARIS_CAPABILITIES */ +#error "HAVE_CAPABILITIES defined, but neither LCAPS nor Solaris Capabilties!" +#endif /* HAVE_LCAPS */ +#endif /* HAVE_CAPABILITIES */ + +/* the default NULL state we report is RAISED, but could be LOWERED if + * zprivs_terminate is called and the NULL handler is installed. + */ +static zebra_privs_current_t zprivs_null_state = ZPRIVS_RAISED; + /* internal privileges state */ static struct _zprivs_t { -#ifdef HAVE_LCAPS - cap_t caps; /* caps storage */ - cap_value_t *syscaps_p; /* system permitted caps */ - cap_value_t *syscaps_i; /* system inheritable caps */ - int sys_num_p; /* number of syscaps_p */ - int sys_num_i; /* number of syscaps_i */ -#endif /* HAVE_LCAPS */ +#ifdef HAVE_CAPABILITIES + pstorage_t caps; /* working storage */ + pset_t *syscaps_p; /* system-type requested permitted caps */ + pset_t *syscaps_i; /* system-type requested inheritable caps */ +#endif /* HAVE_CAPABILITIES */ uid_t zuid, /* uid to run as */ zsuid; /* saved uid */ gid_t zgid; /* gid to run as */ @@ -43,58 +77,103 @@ static struct _zprivs_t } zprivs_state; /* externally exported but not directly accessed functions */ -#ifdef HAVE_LCAPS +#ifdef HAVE_CAPABILITIES int zprivs_change_caps (zebra_privs_ops_t); zebra_privs_current_t zprivs_state_caps (void); -#endif /* HAVE_LCAPS */ +#endif /* HAVE_CAPABILITIES */ int zprivs_change_uid (zebra_privs_ops_t); zebra_privs_current_t zprivs_state_uid (void); int zprivs_change_null (zebra_privs_ops_t); zebra_privs_current_t zprivs_state_null (void); -void zprivs_terminate (void); -#ifdef HAVE_LCAPS -static int -cap_map [ZCAP_MAX] = +#ifdef HAVE_CAPABILITIES +/* internal capability API */ +static pset_t *zcaps2sys (zebra_capabilities_t *, int); +static void zprivs_caps_init (struct zebra_privs_t *); +static void zprivs_caps_terminate (void); + +/* Map of Quagga abstract capabilities to system capabilities */ +static struct +{ + int num; + pvalue_t *system_caps; +} cap_map [ZCAP_MAX] = { - [ZCAP_SETGID] = CAP_SETGID, - [ZCAP_SETUID] = CAP_SETUID, - [ZCAP_BIND] = CAP_NET_BIND_SERVICE, - [ZCAP_BROADCAST] = CAP_NET_BROADCAST, - [ZCAP_ADMIN] = CAP_NET_ADMIN, - [ZCAP_RAW] = CAP_NET_RAW, - [ZCAP_CHROOT] = CAP_SYS_CHROOT, - [ZCAP_NICE] = CAP_SYS_NICE, - [ZCAP_PTRACE] = CAP_SYS_PTRACE, - [ZCAP_DAC_OVERRIDE] = CAP_DAC_OVERRIDE, - [ZCAP_READ_SEARCH] = CAP_DAC_READ_SEARCH, - [ZCAP_SYS_ADMIN] = CAP_SYS_ADMIN, - [ZCAP_FOWNER] = CAP_FOWNER +#ifdef HAVE_LCAPS /* Quagga -> Linux capabilities mappings */ + [ZCAP_SETID] = { 2, (pvalue_t []) { CAP_SETGID, + CAP_SETUID }, }, + [ZCAP_BIND] = { 2, (pvalue_t []) { CAP_NET_BIND_SERVICE, + CAP_NET_BROADCAST }, }, + [ZCAP_NET_ADMIN] = { 1, (pvalue_t []) { CAP_NET_ADMIN }, }, + [ZCAP_NET_RAW] = { 1, (pvalue_t []) { CAP_NET_RAW }, }, + [ZCAP_CHROOT] = { 1, (pvalue_t []) { CAP_SYS_CHROOT, }, }, + [ZCAP_NICE] = { 1, (pvalue_t []) { CAP_SYS_NICE }, }, + [ZCAP_PTRACE] = { 1, (pvalue_t []) { CAP_SYS_PTRACE }, }, + [ZCAP_DAC_OVERRIDE] = { 1, (pvalue_t []) { CAP_DAC_OVERRIDE }, }, + [ZCAP_READ_SEARCH] = { 1, (pvalue_t []) { CAP_DAC_READ_SEARCH }, }, + [ZCAP_SYS_ADMIN] = { 1, (pvalue_t []) { CAP_SYS_ADMIN }, }, + [ZCAP_FOWNER] = { 1, (pvalue_t []) { CAP_FOWNER }, }, +#elif defined(HAVE_SOLARIS_CAPABILITIES) /* HAVE_LCAPS */ + /* Quagga -> Solaris privilege mappings */ + [ZCAP_SETID] = { 1, (pvalue_t []) { PRIV_PROC_SETID }, }, + [ZCAP_BIND] = { 1, (pvalue_t []) { PRIV_NET_PRIVADDR }, }, + [ZCAP_NET_ADMIN] = { 1, (pvalue_t []) { PRIV_SYS_NET_CONFIG }, }, + [ZCAP_NET_RAW] = { 2, (pvalue_t []) { PRIV_NET_RAWACCESS, + PRIV_NET_ICMPACCESS }, }, + [ZCAP_CHROOT] = { 1, (pvalue_t []) { PRIV_PROC_CHROOT }, }, + [ZCAP_NICE] = { 1, (pvalue_t []) { PRIV_PROC_PRIOCNTL }, }, + [ZCAP_PTRACE] = { 1, (pvalue_t []) { PRIV_PROC_SESSION }, }, + [ZCAP_DAC_OVERRIDE] = { 2, (pvalue_t []) { PRIV_FILE_DAC_EXECUTE, + PRIV_FILE_DAC_READ, + PRIV_FILE_DAC_SEARCH, + PRIV_FILE_DAC_WRITE, + PRIV_FILE_DAC_SEARCH }, }, + [ZCAP_READ_SEARCH] = { 2, (pvalue_t []) { PRIV_FILE_DAC_SEARCH, + PRIV_FILE_DAC_READ }, }, + [ZCAP_SYS_ADMIN] = { 1, (pvalue_t []) { PRIV_SYS_ADMIN }, }, + [ZCAP_FOWNER] = { 1, (pvalue_t []) { PRIV_FILE_OWNER }, }, +#endif /* HAVE_SOLARIS_CAPABILITIES */ }; - + +#ifdef HAVE_LCAPS +/* Linux forms of capabilities methods */ /* convert zebras privileges to system capabilities */ -static cap_value_t * +static pset_t * zcaps2sys (zebra_capabilities_t *zcaps, int num) { - cap_value_t *syscaps; - int i; + pset_t *syscaps; + int i, j = 0, count = 0; if (!num) return NULL; - - syscaps = (cap_value_t *) XCALLOC ( MTYPE_PRIVS, - (sizeof(cap_value_t) * num) ); - if (!syscaps) + + /* first count up how many system caps we have */ + for (i= 0; i < num; i++) + count += cap_map[zcaps[i]].num; + + if ( (syscaps = XCALLOC (MTYPE_PRIVS, (sizeof(pset_t) * num))) == NULL) { - zlog_err ("zcap2sys: could not XCALLOC!"); + fprintf (stderr, "%s: could not allocate syscaps!", __func__); return NULL; } - for (i=0; i < num; i++) + syscaps->caps = XCALLOC (MTYPE_PRIVS, (sizeof (pvalue_t) * count)); + + if (!syscaps->caps) { - syscaps[i] = cap_map[zcaps[i]]; + fprintf (stderr, "%s: could not XCALLOC caps!", __func__); + return NULL; } - + + /* copy the capabilities over */ + count = 0; + for (i=0; i < num; i++) + for (j = 0; j < cap_map[zcaps[i]].num; j++) + syscaps->caps[count++] = cap_map[zcaps[i]].system_caps[j]; + + /* iterations above should be exact same as previous count, obviously.. */ + syscaps->num = count; + return syscaps; } @@ -104,6 +183,11 @@ zprivs_change_caps (zebra_privs_ops_t op) { cap_flag_value_t cflag; + /* should be no possibility of being called without valid caps */ + assert (zprivs_state.syscaps_p && zprivs_state.caps); + if (! (zprivs_state.syscaps_p && zprivs_state.caps)) + exit (1); + if (op == ZPRIVS_RAISE) cflag = CAP_SET; else if (op == ZPRIVS_LOWER) @@ -112,7 +196,9 @@ zprivs_change_caps (zebra_privs_ops_t op) return -1; if ( !cap_set_flag (zprivs_state.caps, CAP_EFFECTIVE, - zprivs_state.sys_num_p, zprivs_state.syscaps_p, cflag)) + zprivs_state.syscaps_p->num, + zprivs_state.syscaps_p->caps, + cflag)) return cap_set_proc (zprivs_state.caps); return -1; } @@ -122,21 +208,348 @@ zprivs_state_caps (void) { int i; cap_flag_value_t val; + + /* should be no possibility of being called without valid caps */ + assert (zprivs_state.syscaps_p && zprivs_state.caps); + if (! (zprivs_state.syscaps_p && zprivs_state.caps)) + exit (1); - for (i=0; i < zprivs_state.sys_num_p; i++) + for (i=0; i < zprivs_state.syscaps_p->num; i++) { - if ( cap_get_flag (zprivs_state.caps, zprivs_state.syscaps_p[i], + if ( cap_get_flag (zprivs_state.caps, zprivs_state.syscaps_p->caps[i], CAP_EFFECTIVE, &val) ) - zlog_warn ("zprivs_state_caps: could not cap_get_flag, %s", - safe_strerror (errno) ); + { + zlog_warn ("zprivs_state_caps: could not cap_get_flag, %s", + safe_strerror (errno) ); + return ZPRIVS_UNKNOWN; + } if (val == CAP_SET) return ZPRIVS_RAISED; } return ZPRIVS_LOWERED; } -#endif /* HAVE_LCAPS */ +static void +zprivs_caps_init (struct zebra_privs_t *zprivs) +{ + zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p); + zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i); + + /* Tell kernel we want caps maintained across uid changes */ + if ( prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1 ) + { + fprintf (stderr, "privs_init: could not set PR_SET_KEEPCAPS, %s\n", + safe_strerror (errno) ); + exit(1); + } + + if ( !zprivs_state.syscaps_p ) + { + fprintf (stderr, "privs_init: capabilities enabled, " + "but no capabilities supplied\n"); + } + + if ( !(zprivs_state.caps = cap_init()) ) + { + fprintf (stderr, "privs_init: failed to cap_init, %s\n", + safe_strerror (errno)); + exit (1); + } + + /* we have caps, we have no need to ever change back the original user */ + if (zprivs_state.zuid) + { + if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) ) + { + fprintf (stderr, "zprivs_init (cap): could not setreuid, %s\n", + safe_strerror (errno)); + exit (1); + } + } + + if ( cap_clear (zprivs_state.caps) ) + { + fprintf (stderr, "privs_init: failed to cap_clear, %s\n", + safe_strerror (errno)); + exit (1); + } + + /* set permitted caps */ + cap_set_flag(zprivs_state.caps, CAP_PERMITTED, + zprivs_state.syscaps_p->num, + zprivs_state.syscaps_p->caps, + CAP_SET); + + /* set inheritable caps, if any */ + if (zprivs_state.syscaps_i && zprivs_state.syscaps_i->num) + { + cap_set_flag(zprivs_state.caps, CAP_INHERITABLE, + zprivs_state.syscaps_i->num, + zprivs_state.syscaps_i->caps, + CAP_SET); + } + + /* apply caps. CAP_EFFECTIVE is cleared. we'll raise the caps as + * and when, and only when, they are needed. + */ + if ( cap_set_proc (zprivs_state.caps) ) + { + fprintf (stderr, "privs_init: initial cap_set_proc failed\n"); + exit (1); + } + + /* set methods for the caller to use */ + zprivs->change = zprivs_change_caps; + zprivs->current_state = zprivs_state_caps; +} + +static void +zprivs_caps_terminate (void) +{ + /* clear all capabilities */ + if (zprivs_state.caps) + cap_clear (zprivs_state.caps); + /* and boom, capabilities are gone forever */ + if ( cap_set_proc (zprivs_state.caps) ) + { + fprintf (stderr, "privs_terminate: cap_set_proc failed, %s", + safe_strerror (errno) ); + exit (1); + } + + /* free up private state */ + if (zprivs_state.syscaps_p->num) + { + XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p->caps); + XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p); + } + + if (zprivs_state.syscaps_i && zprivs_state.syscaps_i->num) + { + XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i->caps); + XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i); + } + + cap_free (zprivs_state.caps); +} +#elif defined (HAVE_SOLARIS_CAPABILITIES) /* !HAVE_LCAPS */ + +/* Solaris specific capability/privilege methods + * + * Resources: + * - the 'privileges' man page + * - http://cvs.opensolaris.org + * - http://blogs.sun.com/roller/page/gbrunett?entry=privilege_enabling_set_id_programs1 + */ + +/* convert zebras privileges to system capabilities */ +static pset_t * +zcaps2sys (zebra_capabilities_t *zcaps, int num) +{ + pset_t *syscaps; + int i, j = 0, count = 0; + + if ((syscaps = priv_allocset()) == NULL) + { + fprintf (stderr, "%s: could not allocate syscaps!\n", __func__); + exit (1); + } + + priv_emptyset (syscaps); + + for (i=0; i < num; i++) + for (j = 0; j < cap_map[zcaps[i]].num; j++) + priv_addset (syscaps, cap_map[zcaps[i]].system_caps[j]); + + return syscaps; +} + +/* callback exported to users to RAISE and LOWER effective privileges + * from nothing to the given permitted set and back down + */ +int +zprivs_change_caps (zebra_privs_ops_t op) +{ + + /* should be no possibility of being called without valid caps */ + assert (zprivs_state.syscaps_p); + if (!zprivs_state.syscaps_p) + { + fprintf (stderr, "%s: Eek, missing caps!", __func__); + exit (1); + } + + /* to raise: copy original permitted into our working effective set + * to lower: just clear the working effective set + */ + if (op == ZPRIVS_RAISE) + priv_copyset (zprivs_state.syscaps_p, zprivs_state.caps); + else if (op == ZPRIVS_LOWER) + priv_emptyset (zprivs_state.caps); + else + return -1; + + if (setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps) != 0) + return -1; + + return 0; +} + +/* Retrieve current privilege state, is it RAISED or LOWERED? */ +zebra_privs_current_t +zprivs_state_caps (void) +{ + zebra_privs_current_t result; + pset_t *effective; + + if ( (effective = priv_allocset()) == NULL) + { + fprintf (stderr, "%s: failed to get priv_allocset!\n", __func__, + safe_strerror (errno)); + return ZPRIVS_UNKNOWN; + } + + if (getppriv (PRIV_EFFECTIVE, effective)) + { + fprintf (stderr, "%s: failed to get state!%s\n", __func__, + safe_strerror (errno)); + result = ZPRIVS_UNKNOWN; + } + else + { + if (priv_isemptyset (effective) == B_TRUE) + result = ZPRIVS_LOWERED; + else + result = ZPRIVS_RAISED; + } + + if (effective) + priv_freeset (effective); + + return result; +} + +static void +zprivs_caps_init (struct zebra_privs_t *zprivs) +{ + pset_t *basic; + pset_t *empty; + + /* the specified sets */ + zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p); + zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i); + + /* nonsensical to have gotten here but not have capabilities */ + if (!zprivs_state.syscaps_p) + { + fprintf (stderr, "%s: capabilities enabled, " + "but no valid capabilities supplied\n", + __func__); + } + + /* We retain the basic set in our permitted set, as Linux has no + * equivalent. The basic set on Linux hence is implicit, always + * there. + */ + if ((basic = priv_str_to_set("basic", ",", NULL)) == NULL) + { + fprintf (stderr, "%s: couldn't get basic set!\n", __func__); + exit (1); + } + + /* Add the basic set to the permitted set */ + priv_union (basic, zprivs_state.syscaps_p); + priv_freeset (basic); + + /* we need an empty set for 'effective', potentially for inheritable too */ + if ( (empty = priv_allocset()) == NULL) + { + fprintf (stderr, "%s: couldn't get empty set!\n", __func__); + exit (1); + } + priv_emptyset (empty); + + /* Hey kernel, we know about privileges! + * this isn't strictly required, use of setppriv should have same effect + */ + if (setpflags (PRIV_AWARE, 1)) + { + fprintf (stderr, "%s: error setting PRIV_AWARE!, %s\n", __func__, + safe_strerror (errno) ); + exit (1); + } + + /* need either valid or empty sets for both p and i.. */ + assert (zprivs_state.syscaps_i && zprivs_state.syscaps_p); + + /* set the permitted set */ + if (setppriv (PRIV_SET, PRIV_PERMITTED, zprivs_state.syscaps_p)) + { + fprintf (stderr, "%s: error setting permitted set!, %s\n", __func__, + safe_strerror (errno) ); + exit (1); + } + + /* set the inheritable set */ + if (setppriv (PRIV_SET, PRIV_INHERITABLE, zprivs_state.syscaps_i)) + { + fprintf (stderr, "%s: error setting inheritable set!, %s\n", __func__, + safe_strerror (errno) ); + exit (1); + } + + /* we have caps, we have no need to ever change back the original user */ + if (zprivs_state.zuid) + { + if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) ) + { + fprintf (stderr, "%s: could not setreuid, %s\n", + __func__, safe_strerror (errno)); + exit (1); + } + } + + /* now clear the effective set and we're ready to go */ + if (setppriv (PRIV_SET, PRIV_EFFECTIVE, empty)) + { + fprintf (stderr, "%s: error setting effective set!, %s\n", __func__, + safe_strerror (errno) ); + exit (1); + } + + /* we'll use this as our working-storage privset */ + zprivs_state.caps = empty; + + /* set methods for the caller to use */ + zprivs->change = zprivs_change_caps; + zprivs->current_state = zprivs_state_caps; +} + +static void +zprivs_caps_terminate (void) +{ + assert (zprivs_state.caps); + + /* clear all capabilities */ + priv_emptyset (zprivs_state.caps); + setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps); + setppriv (PRIV_SET, PRIV_PERMITTED, zprivs_state.caps); + setppriv (PRIV_SET, PRIV_INHERITABLE, zprivs_state.caps); + + /* free up private state */ + if (zprivs_state.syscaps_p) + priv_freeset (zprivs_state.syscaps_p); + if (zprivs_state.syscaps_i) + priv_freeset (zprivs_state.syscaps_i); + + priv_freeset (zprivs_state.caps); +} +#else /* !HAVE_LCAPS && ! HAVE_SOLARIS_CAPABILITIES */ +#error "Neither Solaris nor Linux capabilities, dazed and confused..." +#endif /* HAVE_LCAPS */ +#endif /* HAVE_CAPABILITIES */ + int zprivs_change_uid (zebra_privs_ops_t op) { @@ -164,10 +577,9 @@ zprivs_change_null (zebra_privs_ops_t op) zebra_privs_current_t zprivs_state_null (void) { - return ZPRIVS_RAISED; + return zprivs_null_state; } - void zprivs_init(struct zebra_privs_t *zprivs) { @@ -248,80 +660,14 @@ zprivs_init(struct zebra_privs_t *zprivs) } } -#ifdef HAVE_LCAPS - zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p); - zprivs_state.sys_num_p = zprivs->cap_num_p; - zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i); - zprivs_state.sys_num_i = zprivs->cap_num_i; - - /* Tell kernel we want caps maintained across uid changes */ - if ( prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1 ) - { - fprintf (stderr, "privs_init: could not set PR_SET_KEEPCAPS, %s\n", - safe_strerror (errno) ); - exit(1); - } - - if ( !zprivs_state.syscaps_p ) - { - fprintf (stderr, "privs_init: capabilities enabled, but no capabilities supplied\n"); - } - - if ( !(zprivs_state.caps = cap_init()) ) - { - fprintf (stderr, "privs_init: failed to cap_init, %s\n", - safe_strerror (errno)); - exit (1); - } - - /* we have caps, we have no need to ever change back the original user */ - if (zprivs_state.zuid) - { - if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) ) - { - fprintf (stderr, "zprivs_init (cap): could not setreuid, %s\n", - safe_strerror (errno)); - exit (1); - } - } - - if ( cap_clear (zprivs_state.caps) ) - { - fprintf (stderr, "privs_init: failed to cap_clear, %s\n", - safe_strerror (errno)); - exit (1); - } - - /* set permitted caps */ - cap_set_flag(zprivs_state.caps, CAP_PERMITTED, - zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET); - cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE, - zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET); - - /* set inheritable caps, if any */ - if (zprivs_state.sys_num_i) - { - cap_set_flag(zprivs_state.caps, CAP_INHERITABLE, - zprivs_state.sys_num_i, zprivs_state.syscaps_i, CAP_SET); - } - - /* apply caps. CAP_EFFECTIVE is cleared. we'll raise the caps as - * and when, and only when, they are needed. - */ - if ( cap_set_proc (zprivs_state.caps) ) - { - fprintf (stderr, "privs_init: initial cap_set_proc failed\n"); - exit (1); - } - - /* set methods for the caller to use */ - zprivs->change = zprivs_change_caps; - zprivs->current_state = zprivs_state_caps; - -#elif !defined(HAVE_LCAPS) +#ifdef HAVE_CAPABILITIES + zprivs_caps_init (zprivs); +#else /* !HAVE_CAPABILITIES */ /* we dont have caps. we'll need to maintain rid and saved uid * and change euid back to saved uid (who we presume has all neccessary * privileges) whenever we are asked to raise our privileges. + * + * This is not worth that much security wise, but all we can do. */ zprivs_state.zsuid = geteuid(); if ( zprivs_state.zuid ) @@ -336,43 +682,35 @@ zprivs_init(struct zebra_privs_t *zprivs) zprivs->change = zprivs_change_uid; zprivs->current_state = zprivs_state_uid; -#endif /* HAVE_LCAPS */ +#endif /* HAVE_CAPABILITIES */ } void -zprivs_terminate (void) +zprivs_terminate (struct zebra_privs_t *zprivs) { - -#ifdef HAVE_LCAPS - - if (zprivs_state.caps) - cap_clear (zprivs_state.caps); - - if ( cap_set_proc (zprivs_state.caps) ) + if (!zprivs) { - zlog_err ("privs_terminate: cap_set_proc failed, %s", - safe_strerror (errno) ); - exit (1); - } - - if (zprivs_state.sys_num_p) - XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p); - - if (zprivs_state.sys_num_i) - XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i); + fprintf (stderr, "%s: no privs struct given, terminating", __func__); + exit (0); + } - cap_free (zprivs_state.caps); -#else +#ifdef HAVE_CAPABILITIES + zprivs_caps_terminate(); +#else /* !HAVE_CAPABILITIES */ if (zprivs_state.zuid) { if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) ) { - zlog_err ("privs_terminate: could not setreuid, %s", + fprintf (stderr, "privs_terminate: could not setreuid, %s", safe_strerror (errno) ); exit (1); } } #endif /* HAVE_LCAPS */ + + zprivs->change = zprivs_change_null; + zprivs->current_state = zprivs_state_null; + zprivs_null_state = ZPRIVS_LOWERED; return; } diff --git a/lib/privs.h b/lib/privs.h index 70507565..46d614e0 100644 --- a/lib/privs.h +++ b/lib/privs.h @@ -27,13 +27,11 @@ /* list of zebra capabilities */ typedef enum { - ZCAP_SETGID, - ZCAP_SETUID, + ZCAP_SETID, ZCAP_BIND, - ZCAP_BROADCAST, - ZCAP_ADMIN, + ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN, - ZCAP_RAW, + ZCAP_NET_RAW, ZCAP_CHROOT, ZCAP_NICE, ZCAP_PTRACE, @@ -46,7 +44,8 @@ typedef enum typedef enum { ZPRIVS_LOWERED, - ZPRIVS_RAISED + ZPRIVS_RAISED, + ZPRIVS_UNKNOWN, } zebra_privs_current_t; typedef enum @@ -84,7 +83,7 @@ struct zprivs_ids_t /* initialise zebra privileges */ extern void zprivs_init (struct zebra_privs_t *zprivs); /* drop all and terminate privileges */ -extern void zprivs_terminate (void); +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 *); diff --git a/lib/zebra.h b/lib/zebra.h index b4ab3352..03af70d3 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -120,6 +120,10 @@ typedef int socklen_t; #include #endif /* HAVE_LCAPS */ +#ifdef HAVE_SOLARIS_CAPABILITIES +#include +#endif /* HAVE_SOLARIS_CAPABILITIES */ + /* network include group */ #include diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 073e9d68..c40b46ed 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -46,7 +46,7 @@ /* ospf6d privileges */ zebra_capabilities_t _caps_p [] = { - ZCAP_RAW, + ZCAP_NET_RAW, ZCAP_BIND }; diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index b0ad3ab7..483b6387 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -52,10 +52,9 @@ /* ospfd privileges */ zebra_capabilities_t _caps_p [] = { - ZCAP_RAW, + ZCAP_NET_RAW, ZCAP_BIND, - ZCAP_BROADCAST, - ZCAP_ADMIN, + ZCAP_NET_ADMIN, }; struct zebra_privs_t ospfd_privs = diff --git a/ripd/rip_main.c b/ripd/rip_main.c index ba60b36f..c9b45e8b 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -54,7 +54,7 @@ static struct option longopts[] = /* ripd privileges */ zebra_capabilities_t _caps_p [] = { - ZCAP_RAW, + ZCAP_NET_RAW, ZCAP_BIND }; diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index ea30c1bf..5885f47c 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -61,7 +61,7 @@ struct option longopts[] = /* ripngd privileges */ zebra_capabilities_t _caps_p [] = { - ZCAP_RAW, + ZCAP_NET_RAW, ZCAP_BIND }; diff --git a/zebra/main.c b/zebra/main.c index acfde80a..67108c4e 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -87,9 +87,9 @@ struct option longopts[] = zebra_capabilities_t _caps_p [] = { - ZCAP_ADMIN, + ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN, - ZCAP_RAW, + ZCAP_NET_RAW, }; /* zebra privileges to run with */ -- cgit v1.2.1