Switch to libcap-ng, avoid linking libdbus against libcap[-ng]
authorColin Walters <walters@verbum.org>
Thu, 28 Jan 2010 00:38:44 +0000 (19:38 -0500)
committerColin Walters <walters@verbum.org>
Thu, 28 Jan 2010 22:01:24 +0000 (17:01 -0500)
(Commit message written by Colin Walters <walters@verbum.org>)

A current Fedora goal is to convert projects to libcap-ng which
more easily allows dropping Linux capabilities.  For software
which also links to libdbus, it's problematic to link against
libcap as well.

Though really, libdbus should have never linked against libcap
in the first place, which is another thing this patch changes
by moving the libcap-using bits out of dbus/ and into bus/.

https://bugzilla.redhat.com/show_bug.cgi?id=518541

bus/selinux.c
bus/selinux.h
configure.in
dbus/dbus-sysdeps-util-unix.c
dbus/dbus-sysdeps.h

index df9a00b..456723a 100644 (file)
@@ -22,6 +22,7 @@
  */
 #include <dbus/dbus-internals.h>
 #include <dbus/dbus-string.h>
+#include <dbus/dbus-userdb.h>
 #include "selinux.h"
 #include "services.h"
 #include "policy.h"
@@ -44,7 +45,9 @@
 #include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <grp.h>
 #ifdef HAVE_LIBAUDIT
+#include <cap-ng.h>
 #include <libaudit.h>
 #endif /* HAVE_LIBAUDIT */
 #endif /* HAVE_SELINUX */
@@ -143,13 +146,17 @@ log_callback (const char *fmt, ...)
 #ifdef HAVE_LIBAUDIT
   if (audit_fd >= 0)
   {
-    char buf[PATH_MAX*2];
+    capng_get_caps_process();
+    if (capng_have_capability(CAPNG_EFFECTIVE, CAP_AUDIT_WRITE))
+    {
+      char buf[PATH_MAX*2];
     
-    /* FIXME: need to change this to show real user */
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-    audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, NULL, NULL,
+      /* FIXME: need to change this to show real user */
+      vsnprintf(buf, sizeof(buf), fmt, ap);
+      audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, NULL, NULL,
                                NULL, getuid());
-    return;
+      return;
+    }
   }
 #endif /* HAVE_LIBAUDIT */
   
@@ -1010,3 +1017,104 @@ bus_selinux_shutdown (void)
 #endif /* HAVE_SELINUX */
 }
 
+/**
+ * Changes the user and group the bus is running as.
+ *
+ * @param user the user to become
+ * @param error return location for errors
+ * @returns #FALSE on failure
+ */
+dbus_bool_t
+_dbus_change_to_daemon_user  (const char    *user,
+                              DBusError     *error)
+{
+  dbus_uid_t uid;
+  dbus_gid_t gid;
+  DBusString u;
+
+  _dbus_string_init_const (&u, user);
+
+  if (!_dbus_get_user_id_and_primary_group (&u, &uid, &gid))
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "User '%s' does not appear to exist?",
+                      user);
+      return FALSE;
+    }
+
+#ifdef HAVE_LIBAUDIT
+  /* If we were root */
+  if (_dbus_geteuid () == 0)
+    {
+      int rc;
+
+      capng_clear (CAPNG_SELECT_BOTH);
+      capng_update (CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
+                    CAP_AUDIT_WRITE);
+      rc = capng_change_id (uid, gid, 0);
+      if (rc)
+        {
+          switch (rc) {
+            default:
+              dbus_set_error (error, DBUS_ERROR_FAILED,
+                              "Failed to drop capabilities: %s\n",
+                              _dbus_strerror (errno));
+              break;
+            case -4:
+              dbus_set_error (error, _dbus_error_from_errno (errno),
+                              "Failed to set GID to %lu: %s", gid,
+                              _dbus_strerror (errno));
+              break;
+            case -5:
+              _dbus_warn ("Failed to drop supplementary groups: %s\n",
+                          _dbus_strerror (errno));
+              break;
+            case -6:
+              dbus_set_error (error, _dbus_error_from_errno (errno),
+                              "Failed to set UID to %lu: %s", uid,
+                              _dbus_strerror (errno));
+              break;
+            case -7:
+              dbus_set_error (error, _dbus_error_from_errno (errno),
+                              "Failed to unset keep-capabilities: %s\n",
+                              _dbus_strerror (errno));
+              break;
+          }
+          return FALSE;
+        }
+    }
+#else
+  /* setgroups() only works if we are a privileged process,
+   * so we don't return error on failure; the only possible
+   * failure is that we don't have perms to do it.
+   *
+   * not sure this is right, maybe if setuid()
+   * is going to work then setgroups() should also work.
+   */
+  if (setgroups (0, NULL) < 0)
+    _dbus_warn ("Failed to drop supplementary groups: %s\n",
+                _dbus_strerror (errno));
+
+  /* Set GID first, or the setuid may remove our permission
+   * to change the GID
+   */
+  if (setgid (gid) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to set GID to %lu: %s", gid,
+                      _dbus_strerror (errno));
+      return FALSE;
+    }
+
+  if (setuid (uid) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Failed to set UID to %lu: %s", uid,
+                      _dbus_strerror (errno));
+      return FALSE;
+    }
+#endif /* !HAVE_LIBAUDIT */
+
+ return TRUE;
+}
+
index 3bab36d..f208fbe 100644 (file)
@@ -68,5 +68,7 @@ BusSELinuxID* bus_selinux_init_connection_id (DBusConnection *connection,
 
 
 void bus_selinux_audit_init(void);
+dbus_bool_t _dbus_change_to_daemon_user (const char *user,
+                                         DBusError  *error);
 
 #endif /* BUS_SELINUX_H */
index 7feb1a9..cb16eec 100644 (file)
@@ -851,7 +851,7 @@ else
     AC_CHECK_LIB(audit, audit_log_user_avc_message, 
                  have_libaudit=yes, have_libaudit=no)
     if test x$have_libaudit = xyes ; then
-        AC_CHECK_LIB(cap, cap_set_proc, 
+        AC_CHECK_LIB(cap-ng, capng_clear,
                  have_libaudit=yes, have_libaudit=no)
     fi
 fi
@@ -859,8 +859,7 @@ fi
 AM_CONDITIONAL(HAVE_LIBAUDIT, test x$have_libaudit = xyes)
 
 if test x$have_libaudit = xyes ; then
-    SELINUX_LIBS="$SELINUX_LIBS -laudit"
-    LIBS="-lcap $LIBS"
+    SELINUX_LIBS="$SELINUX_LIBS -laudit -lcap-ng"
     AC_DEFINE(HAVE_LIBAUDIT,1,[audit daemon SELinux support])
 fi
 
index 83f74fe..27cdbb0 100644 (file)
 #include <dirent.h>
 #include <sys/un.h>
 #include <syslog.h>
-#ifdef HAVE_LIBAUDIT
-#include <sys/prctl.h>
-#include <sys/capability.h>
-#include <libaudit.h>
-#endif /* HAVE_LIBAUDIT */
 
 #ifdef HAVE_SYS_SYSLIMITS_H
 #include <sys/syslimits.h>
@@ -308,155 +303,6 @@ _dbus_verify_daemon_user (const char *user)
   return _dbus_get_user_id_and_primary_group (&u, NULL, NULL);
 }
 
-/**
- * Changes the user and group the bus is running as.
- *
- * @param user the user to become
- * @param error return location for errors
- * @returns #FALSE on failure
- */
-dbus_bool_t
-_dbus_change_to_daemon_user  (const char    *user,
-                              DBusError     *error)
-{
-  dbus_uid_t uid;
-  dbus_gid_t gid;
-  DBusString u;
-#ifdef HAVE_LIBAUDIT
-  dbus_bool_t we_were_root;
-  cap_t new_caps;
-#endif
-  
-  _dbus_string_init_const (&u, user);
-  
-  if (!_dbus_get_user_id_and_primary_group (&u, &uid, &gid))
-    {
-      dbus_set_error (error, DBUS_ERROR_FAILED,
-                      "User '%s' does not appear to exist?",
-                      user);
-      return FALSE;
-    }
-  
-#ifdef HAVE_LIBAUDIT
-  we_were_root = _dbus_geteuid () == 0;
-  new_caps = NULL;
-  /* have a tmp set of caps that we use to transition to the usr/grp dbus should
-   * run as ... doesn't really help. But keeps people happy.
-   */
-    
-  if (we_were_root)
-    {
-      cap_value_t new_cap_list[] = { CAP_AUDIT_WRITE };
-      cap_value_t tmp_cap_list[] = { CAP_AUDIT_WRITE, CAP_SETUID, CAP_SETGID };
-      cap_t tmp_caps = cap_init();
-        
-      if (!tmp_caps || !(new_caps = cap_init ()))
-        {
-          dbus_set_error (error, DBUS_ERROR_FAILED,
-                          "Failed to initialize drop of capabilities: %s\n",
-                          _dbus_strerror (errno));
-
-          if (tmp_caps)
-            cap_free (tmp_caps);
-
-          return FALSE;
-        }
-
-      /* assume these work... */
-      cap_set_flag (new_caps, CAP_PERMITTED, 1, new_cap_list, CAP_SET);
-      cap_set_flag (new_caps, CAP_EFFECTIVE, 1, new_cap_list, CAP_SET);
-      cap_set_flag (tmp_caps, CAP_PERMITTED, 3, tmp_cap_list, CAP_SET);
-      cap_set_flag (tmp_caps, CAP_EFFECTIVE, 3, tmp_cap_list, CAP_SET);
-      
-      if (prctl (PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
-        {
-          dbus_set_error (error, _dbus_error_from_errno (errno),
-                          "Failed to set keep-capabilities: %s\n",
-                          _dbus_strerror (errno));
-          cap_free (tmp_caps);
-          goto fail;
-        }
-        
-      if (cap_set_proc (tmp_caps) == -1)
-        {
-          dbus_set_error (error, DBUS_ERROR_FAILED,
-                          "Failed to drop capabilities: %s\n",
-                          _dbus_strerror (errno));
-          cap_free (tmp_caps);
-          goto fail;
-        }
-      cap_free (tmp_caps);
-    }
-#endif /* HAVE_LIBAUDIT */
-  
-  /* setgroups() only works if we are a privileged process,
-   * so we don't return error on failure; the only possible
-   * failure is that we don't have perms to do it.
-   *
-   * not sure this is right, maybe if setuid()
-   * is going to work then setgroups() should also work.
-   */
-  if (setgroups (0, NULL) < 0)
-    _dbus_warn ("Failed to drop supplementary groups: %s\n",
-                _dbus_strerror (errno));
-  
-  /* Set GID first, or the setuid may remove our permission
-   * to change the GID
-   */
-  if (setgid (gid) < 0)
-    {
-      dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Failed to set GID to %lu: %s", gid,
-                      _dbus_strerror (errno));
-      goto fail;
-    }
-  
-  if (setuid (uid) < 0)
-    {
-      dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Failed to set UID to %lu: %s", uid,
-                      _dbus_strerror (errno));
-      goto fail;
-    }
-  
-#ifdef HAVE_LIBAUDIT
-  if (we_were_root)
-    {
-      if (cap_set_proc (new_caps))
-        {
-          dbus_set_error (error, DBUS_ERROR_FAILED,
-                          "Failed to drop capabilities: %s\n",
-                          _dbus_strerror (errno));
-          goto fail;
-        }
-      cap_free (new_caps);
-
-      /* should always work, if it did above */      
-      if (prctl (PR_SET_KEEPCAPS, 0, 0, 0, 0) == -1)
-        {
-          dbus_set_error (error, _dbus_error_from_errno (errno),
-                          "Failed to unset keep-capabilities: %s\n",
-                          _dbus_strerror (errno));
-          return FALSE;
-        }
-    }
-#endif
-
- return TRUE;
-
- fail:
-#ifdef HAVE_LIBAUDIT
- if (!we_were_root)
-   {
-     /* should always work, if it did above */
-     prctl (PR_SET_KEEPCAPS, 0, 0, 0, 0);
-     cap_free (new_caps);
-   }
-#endif
-
- return FALSE;
-}
-
 void 
 _dbus_init_system_log (void)
 {
index 30e7dff..b154f01 100644 (file)
@@ -418,8 +418,6 @@ dbus_bool_t _dbus_become_daemon   (const DBusString *pidfile,
                                    dbus_bool_t       keep_umask);
 
 dbus_bool_t _dbus_verify_daemon_user    (const char *user);
-dbus_bool_t _dbus_change_to_daemon_user (const char *user,
-                                         DBusError  *error);
 
 dbus_bool_t _dbus_write_pid_to_file_and_pipe (const DBusString *pidfile,
                                               DBusPipe         *print_pid_pipe,