]> git.proxmox.com Git - mirror_frr.git/blobdiff - lib/privs.c
Merge remote-tracking branch 'origin/stable/3.0'
[mirror_frr.git] / lib / privs.c
index f4117e2634100b77ade4590f2417b2e0e24c06f2..767ab667e78e908eb44e8492caaac299ea241388 100644 (file)
@@ -2,7 +2,7 @@
  * 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.
  *
@@ -27,6 +27,9 @@
 #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
@@ -47,7 +50,7 @@ struct _pset {
 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;
@@ -56,7 +59,7 @@ typedef priv_set_t *pstorage_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.
  */
@@ -102,8 +105,7 @@ static struct
 #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,            }, },
@@ -117,7 +119,12 @@ static struct
   /* 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           }, },
@@ -134,7 +141,7 @@ static struct
   [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 */
@@ -243,14 +250,9 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
       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) )
         {
@@ -259,6 +261,9 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
           exit (1);
         }
     }
+
+  if ( !zprivs_state.syscaps_p )
+    return;
   
   if ( !(zprivs_state.caps = cap_init()) )
     {
@@ -294,7 +299,28 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
    */
   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);
     }
   
@@ -334,7 +360,7 @@ zprivs_caps_terminate (void)
   cap_free (zprivs_state.caps);
 }
 #elif defined (HAVE_SOLARIS_CAPABILITIES) /* !HAVE_LCAPS */
-\f
+
 /* Solaris specific capability/privilege methods 
  *
  * Resources:
@@ -343,6 +369,26 @@ zprivs_caps_terminate (void)
  * - 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)
@@ -371,26 +417,34 @@ 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;
@@ -418,15 +472,15 @@ zprivs_state_caps (void)
     }
   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;
 }
 
@@ -434,7 +488,7 @@ static void
 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);
@@ -462,14 +516,6 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
   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
    */
@@ -486,7 +532,8 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
   /* 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) )
         {
@@ -512,16 +559,19 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
       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;
@@ -533,8 +583,7 @@ zprivs_caps_terminate (void)
 {
   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);
@@ -551,11 +600,12 @@ zprivs_caps_terminate (void)
 #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)
@@ -582,11 +632,49 @@ zprivs_state_null (void)
   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)
     {
@@ -594,6 +682,15 @@ zprivs_init(struct zebra_privs_t *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) )
@@ -605,54 +702,88 @@ zprivs_init(struct zebra_privs_t *zprivs)
 
   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) )
         {
@@ -672,7 +803,8 @@ zprivs_init(struct zebra_privs_t *zprivs)
    * 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) )
         {
@@ -699,7 +831,8 @@ zprivs_terminate (struct zebra_privs_t *zprivs)
 #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) )
         {