* Zebra privileges.
*
* Copyright (C) 2003 Paul Jakma.
- * Copyright (C) 2005 Sun Microsystems, Inc.
+ * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
*
* This file is part of GNU Zebra.
*
#include "memory.h"
#ifdef HAVE_CAPABILITIES
+
+DEFINE_MTYPE_STATIC(LIB, PRIVS, "Privilege information")
+
/* sort out some generic internal types for:
*
* privilege values (cap_value_t, priv_t) -> pvalue_t
typedef cap_value_t pvalue_t;
typedef struct _pset pset_t;
typedef cap_t pstorage_t;
-\f
+
#elif defined (HAVE_SOLARIS_CAPABILITIES)
typedef priv_t pvalue_t;
typedef priv_set_t pset_t;
#error "HAVE_CAPABILITIES defined, but neither LCAPS nor Solaris Capabilties!"
#endif /* HAVE_LCAPS */
#endif /* HAVE_CAPABILITIES */
-\f
+
/* the default NULL state we report is RAISED, but could be LOWERED if
* zprivs_terminate is called and the NULL handler is installed.
*/
#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_BIND] = { 2, (pvalue_t []) { CAP_NET_BIND_SERVICE }, },
[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, }, },
/* Quagga -> Solaris privilege mappings */
[ZCAP_SETID] = { 1, (pvalue_t []) { PRIV_PROC_SETID }, },
[ZCAP_BIND] = { 1, (pvalue_t []) { PRIV_NET_PRIVADDR }, },
+ /* IP_CONFIG is a subset of NET_CONFIG and is allowed in zones */
+#ifdef PRIV_SYS_IP_CONFIG
+ [ZCAP_NET_ADMIN] = { 1, (pvalue_t []) { PRIV_SYS_IP_CONFIG }, },
+#else
[ZCAP_NET_ADMIN] = { 1, (pvalue_t []) { PRIV_SYS_NET_CONFIG }, },
+#endif
[ZCAP_NET_RAW] = { 2, (pvalue_t []) { PRIV_NET_RAWACCESS,
PRIV_NET_ICMPACCESS }, },
[ZCAP_CHROOT] = { 1, (pvalue_t []) { PRIV_PROC_CHROOT }, },
[ZCAP_FOWNER] = { 1, (pvalue_t []) { PRIV_FILE_OWNER }, },
#endif /* HAVE_SOLARIS_CAPABILITIES */
};
-\f
+
#ifdef HAVE_LCAPS
/* Linux forms of capabilities methods */
/* convert zebras privileges to system capabilities */
exit(1);
}
- if ( !zprivs_state.syscaps_p )
- {
- fprintf (stderr, "privs_init: capabilities enabled, "
- "but no capabilities supplied\n");
- }
-
/* we have caps, we have no need to ever change back the original user */
- if (zprivs_state.zuid)
+ /* only change uid if we don't have the correct one */
+ if ((zprivs_state.zuid) && (zprivs_state.zsuid != zprivs_state.zuid))
{
if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
{
exit (1);
}
}
+
+ if ( !zprivs_state.syscaps_p )
+ return;
if ( !(zprivs_state.caps = cap_init()) )
{
*/
if ( cap_set_proc (zprivs_state.caps) )
{
- fprintf (stderr, "privs_init: initial cap_set_proc failed\n");
+ cap_t current_caps;
+ char *current_caps_text = NULL;
+ char *wanted_caps_text = NULL;
+
+ fprintf(stderr, "privs_init: initial cap_set_proc failed: %s\n",
+ safe_strerror(errno));
+
+ current_caps = cap_get_proc();
+ if (current_caps)
+ {
+ current_caps_text = cap_to_text(current_caps, NULL);
+ cap_free(current_caps);
+ }
+
+ wanted_caps_text = cap_to_text(zprivs_state.caps, NULL);
+ fprintf(stderr, "Wanted caps: %s\n", wanted_caps_text ? wanted_caps_text : "???");
+ fprintf(stderr, "Have caps: %s\n", current_caps_text ? current_caps_text : "???");
+ if (current_caps_text)
+ cap_free(current_caps_text);
+ if (wanted_caps_text)
+ cap_free(wanted_caps_text);
+
exit (1);
}
cap_free (zprivs_state.caps);
}
#elif defined (HAVE_SOLARIS_CAPABILITIES) /* !HAVE_LCAPS */
-\f
+
/* Solaris specific capability/privilege methods
*
* Resources:
* - http://blogs.sun.com/roller/page/gbrunett?entry=privilege_enabling_set_id_programs1
*/
+static pset_t *
+zprivs_caps_minimal ()
+{
+ pset_t *minimal;
+
+ if ((minimal = priv_str_to_set("basic", ",", NULL)) == NULL)
+ {
+ fprintf (stderr, "%s: couldn't get basic set!\n", __func__);
+ exit (1);
+ }
+
+ /* create a minimal privilege set from the basic set */
+ (void) priv_delset(minimal, PRIV_PROC_EXEC);
+ (void) priv_delset(minimal, PRIV_PROC_INFO);
+ (void) priv_delset(minimal, PRIV_PROC_SESSION);
+ (void) priv_delset(minimal, PRIV_FILE_LINK_ANY);
+
+ return minimal;
+}
+
/* convert zebras privileges to system capabilities */
static pset_t *
zcaps2sys (zebra_capabilities_t *zcaps, int num)
int
zprivs_change_caps (zebra_privs_ops_t op)
{
+ pset_t *privset;
/* 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 privileged caps!", __func__);
+ exit (1);
+ }
+
+ assert (zprivs_state.caps);
+ if (!zprivs_state.caps)
{
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
+
+ /* to raise: copy original permitted as our working effective set
+ * to lower: copy regular effective set stored in zprivs_state.caps
*/
if (op == ZPRIVS_RAISE)
- priv_copyset (zprivs_state.syscaps_p, zprivs_state.caps);
+ privset = zprivs_state.syscaps_p;
else if (op == ZPRIVS_LOWER)
- priv_emptyset (zprivs_state.caps);
+ privset = zprivs_state.caps;
else
return -1;
- if (setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps) != 0)
+ if (setppriv (PRIV_SET, PRIV_EFFECTIVE, privset) != 0)
return -1;
return 0;
}
else
{
- if (priv_isemptyset (effective) == B_TRUE)
+ if (priv_isequalset (effective, zprivs_state.syscaps_p))
+ result = ZPRIVS_RAISED;
+ else if (priv_isequalset (effective, zprivs_state.caps))
result = ZPRIVS_LOWERED;
else
- result = ZPRIVS_RAISED;
+ result = ZPRIVS_UNKNOWN;
}
- if (effective)
- priv_freeset (effective);
-
+ priv_freeset (effective);
return result;
}
zprivs_caps_init (struct zebra_privs_t *zprivs)
{
pset_t *basic;
- pset_t *empty;
+ pset_t *minimal;
/* the specified sets */
zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p);
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
*/
/* we have caps, we have no need to ever change back the original user
* change real, effective and saved to the specified user.
*/
- if (zprivs_state.zuid)
+ /* only change uid if we don't have the correct one */
+ if ((zprivs_state.zuid) && (zprivs_state.zsuid != zprivs_state.zuid))
{
if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
{
exit (1);
}
- /* now clear the effective set and we're ready to go */
- if (setppriv (PRIV_SET, PRIV_EFFECTIVE, empty))
+ /* we need a minimal basic set for 'effective', potentially for inheritable too */
+ minimal = zprivs_caps_minimal();
+
+ /* now set the effective set with a subset of basic privileges */
+ if (setppriv (PRIV_SET, PRIV_EFFECTIVE, minimal))
{
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;
+ /* we'll use the minimal set as our working-storage privset */
+ zprivs_state.caps = minimal;
/* set methods for the caller to use */
zprivs->change = zprivs_change_caps;
{
assert (zprivs_state.caps);
- /* clear all capabilities */
- priv_emptyset (zprivs_state.caps);
+ /* clear all capabilities by using working-storage privset */
setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps);
setppriv (PRIV_SET, PRIV_PERMITTED, zprivs_state.caps);
setppriv (PRIV_SET, PRIV_INHERITABLE, zprivs_state.caps);
#error "Neither Solaris nor Linux capabilities, dazed and confused..."
#endif /* HAVE_LCAPS */
#endif /* HAVE_CAPABILITIES */
-\f
+
int
zprivs_change_uid (zebra_privs_ops_t op)
{
-
+ if (zprivs_state.zsuid == zprivs_state.zuid)
+ return 0;
if (op == ZPRIVS_RAISE)
return seteuid (zprivs_state.zsuid);
else if (op == ZPRIVS_LOWER)
return zprivs_null_state;
}
+#ifndef HAVE_GETGROUPLIST
+/* Solaris 11 has no getgrouplist() */
+static int
+getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups)
+{
+ struct group *grp;
+ size_t usridx;
+ int pos = 0, ret;
+
+ if (pos < *ngroups)
+ groups[pos] = group;
+ pos++;
+
+ setgrent();
+ while ((grp = getgrent()))
+ {
+ if (grp->gr_gid == group)
+ continue;
+ for (usridx = 0; grp->gr_mem[usridx] != NULL; usridx++)
+ if (!strcmp (grp->gr_mem[usridx], user))
+ {
+ if (pos < *ngroups)
+ groups[pos] = grp->gr_gid;
+ pos++;
+ break;
+ }
+ }
+ endgrent();
+
+ ret = (pos <= *ngroups) ? pos : -1;
+ *ngroups = pos;
+ return ret;
+}
+#endif /* HAVE_GETGROUPLIST */
+
void
zprivs_init(struct zebra_privs_t *zprivs)
{
struct passwd *pwentry = NULL;
struct group *grentry = NULL;
+ gid_t groups[NGROUPS_MAX];
+ int i, ngroups = 0;
+ int found = 0;
if (!zprivs)
{
exit (1);
}
+ if (zprivs->vty_group)
+ {
+ /* in a "NULL" setup, this is allowed to fail too, but still try. */
+ if ((grentry = getgrnam (zprivs->vty_group)))
+ zprivs_state.vtygrp = grentry->gr_gid;
+ else
+ zprivs_state.vtygrp = (gid_t)-1;
+ }
+
/* NULL privs */
if (! (zprivs->user || zprivs->group
|| zprivs->cap_num_p || zprivs->cap_num_i) )
if (zprivs->user)
{
- if ( (pwentry = getpwnam (zprivs->user)) )
- {
- zprivs_state.zuid = pwentry->pw_uid;
- }
- else
+ if ( (pwentry = getpwnam (zprivs->user)) == NULL )
{
/* cant use log.h here as it depends on vty */
fprintf (stderr, "privs_init: could not lookup user %s\n",
zprivs->user);
exit (1);
}
+
+ zprivs_state.zuid = pwentry->pw_uid;
+ zprivs_state.zgid = pwentry->pw_gid;
}
grentry = NULL;
- if (zprivs->vty_group)
- /* Add the vty_group to the supplementary groups so it can be chowned to */
+ if (zprivs->group)
{
- if ( (grentry = getgrnam (zprivs->vty_group)) )
+ if ( (grentry = getgrnam (zprivs->group)) == NULL )
{
- zprivs_state.vtygrp = grentry->gr_gid;
- if ( setgroups (1, &zprivs_state.vtygrp) )
- {
- fprintf (stderr, "privs_init: could not setgroups, %s\n",
- safe_strerror (errno) );
- exit (1);
- }
+ fprintf (stderr, "privs_init: could not lookup group %s\n",
+ zprivs->group);
+ exit (1);
}
- else
+
+ zprivs_state.zgid = grentry->gr_gid;
+ }
+
+ if (zprivs->user)
+ {
+ ngroups = sizeof(groups);
+ if (getgrouplist (zprivs->user, zprivs_state.zgid, groups, &ngroups) < 0)
{
- fprintf (stderr, "privs_init: could not lookup vty group %s\n",
- zprivs->vty_group);
+ /* cant use log.h here as it depends on vty */
+ fprintf (stderr, "privs_init: could not getgrouplist for user %s\n",
+ zprivs->user);
exit (1);
}
}
-
- if (zprivs->group)
+
+ if (zprivs->vty_group)
+ /* Add the vty_group to the supplementary groups so it can be chowned to */
{
- if ( (grentry = getgrnam (zprivs->group)) )
+ if (zprivs_state.vtygrp == (gid_t)-1)
{
- zprivs_state.zgid = grentry->gr_gid;
+ fprintf (stderr, "privs_init: could not lookup vty group %s\n",
+ zprivs->vty_group);
+ exit (1);
}
- else
+
+ for ( i = 0; i < ngroups; i++ )
+ if ( groups[i] == zprivs_state.vtygrp )
+ {
+ found++;
+ break;
+ }
+
+ if (!found)
{
- fprintf (stderr, "privs_init: could not lookup group %s\n",
- zprivs->group);
+ fprintf (stderr, "privs_init: user(%s) is not part of vty group specified(%s)\n",
+ zprivs->user, zprivs->vty_group);
+ exit (1);
+ }
+ if ( i >= ngroups && ngroups < (int) ZEBRA_NUM_OF(groups) )
+ {
+ groups[i] = zprivs_state.vtygrp;
+ }
+ }
+
+ zprivs_state.zsuid = geteuid(); /* initial uid */
+ /* add groups only if we changed uid - otherwise skip */
+ if ((ngroups) && (zprivs_state.zsuid != zprivs_state.zuid))
+ {
+ if ( setgroups (ngroups, groups) )
+ {
+ fprintf (stderr, "privs_init: could not setgroups, %s\n",
+ safe_strerror (errno) );
exit (1);
}
+ }
+
+ /* change gid only if we changed uid - otherwise skip */
+ if ((zprivs_state.zgid) && (zprivs_state.zsuid != zprivs_state.zuid))
+ {
/* change group now, forever. uid we do later */
if ( setregid (zprivs_state.zgid, zprivs_state.zgid) )
{
* This is not worth that much security wise, but all we can do.
*/
zprivs_state.zsuid = geteuid();
- if ( zprivs_state.zuid )
+ /* only change uid if we don't have the correct one */
+ if (( zprivs_state.zuid ) && (zprivs_state.zsuid != zprivs_state.zuid))
{
if ( setreuid (-1, zprivs_state.zuid) )
{
#ifdef HAVE_CAPABILITIES
zprivs_caps_terminate();
#else /* !HAVE_CAPABILITIES */
- if (zprivs_state.zuid)
+ /* only change uid if we don't have the correct one */
+ if ((zprivs_state.zuid) && (zprivs_state.zsuid != zprivs_state.zuid))
{
if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
{