Browse Source

2005-09-29 Paul Jakma <paul.jakma@sun.com>

	* 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.
paul 13 years ago
parent
commit
ceacedba22
13 changed files with 557 additions and 159 deletions
  1. 11 0
      ChangeLog
  2. 1 1
      bgpd/bgp_main.c
  3. 19 2
      configure.ac
  4. 1 1
      isisd/isis_main.c
  5. 30 0
      lib/ChangeLog
  6. 478 140
      lib/privs.c
  7. 6 7
      lib/privs.h
  8. 4 0
      lib/zebra.h
  9. 1 1
      ospf6d/ospf6_main.c
  10. 2 3
      ospfd/ospf_main.c
  11. 1 1
      ripd/rip_main.c
  12. 1 1
      ripngd/ripng_main.c
  13. 2 2
      zebra/main.c

+ 11 - 0
ChangeLog

@@ -1,3 +1,14 @@
+2005-09-29 Paul Jakma <paul.jakma@sun.com>
+
+	* 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 <hasso at quagga.net>
 
 	* configure.ac: Test existance of strndup.

+ 1 - 1
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 =

+ 19 - 2
configure.ac

@@ -5,7 +5,7 @@
 ##  Copyright (c) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
 ##  Portions Copyright (c) 2003 Paul Jakma <paul@dishone.st>
 ##
-## $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 <priv.h>],[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)

+ 1 - 1
isisd/isis_main.c

@@ -52,7 +52,7 @@
 
 /* isisd privileges */
 zebra_capabilities_t _caps_p[] = {
-  ZCAP_RAW,
+  ZCAP_NET_RAW,
   ZCAP_BIND
 };
 

+ 30 - 0
lib/ChangeLog

@@ -1,3 +1,33 @@
+2005-09-29 Paul Jakma <paul.jakma@sun.com>
+
+	* 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 <alain.ritoux@6wind.com>
 
 	* filer.c: show protocol name in filter_show()

+ 478 - 140
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 <zebra.h>
 #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;
 }
 

+ 6 - 7
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 *);
 

+ 4 - 0
lib/zebra.h

@@ -120,6 +120,10 @@ typedef int socklen_t;
 #include <sys/prctl.h>
 #endif /* HAVE_LCAPS */
 
+#ifdef HAVE_SOLARIS_CAPABILITIES
+#include <priv.h>
+#endif /* HAVE_SOLARIS_CAPABILITIES */
+
 /* network include group */
 
 #include <sys/socket.h>

+ 1 - 1
ospf6d/ospf6_main.c

@@ -46,7 +46,7 @@
 /* ospf6d privileges */
 zebra_capabilities_t _caps_p [] =
 {
-  ZCAP_RAW,
+  ZCAP_NET_RAW,
   ZCAP_BIND
 };
 

+ 2 - 3
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 =

+ 1 - 1
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
 };
 

+ 1 - 1
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
 };
 

+ 2 - 2
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 */