]> git.ipfire.org Git - thirdparty/dbus.git/commitdiff
Add LSM-agnostic support for LinuxSecurityLabel credential
authorSimon McVittie <simon.mcvittie@collabora.co.uk>
Wed, 11 Feb 2015 13:19:15 +0000 (13:19 +0000)
committerSimon McVittie <simon.mcvittie@collabora.co.uk>
Wed, 18 Feb 2015 10:35:05 +0000 (10:35 +0000)
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=89041
Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
Acked-by: Stephen Smalley <sds@tycho.nsa.gov> (for SELinux)
Acked-by: John Johansen <john.johansen@canonical.com> (for AppArmor)
Acked-by: Casey Schaufler <casey@schaufler-ca.com> (for Smack)
Tested-by: Tyler Hicks <tyhicks@canonical.com>
bus/driver.c
dbus/dbus-auth.c
dbus/dbus-connection-internal.h
dbus/dbus-connection.c
dbus/dbus-credentials.c
dbus/dbus-credentials.h
dbus/dbus-sysdeps-unix.c
dbus/dbus-transport.c
dbus/dbus-transport.h

index 603504f658d2dcdfaa20c145acd452974a9d1297..442dd01de5d953fd97db94d3ba071b0851c71e34 100644 (file)
@@ -34,6 +34,7 @@
 #include "utils.h"
 
 #include <dbus/dbus-asv-util.h>
+#include <dbus/dbus-connection-internal.h>
 #include <dbus/dbus-string.h>
 #include <dbus/dbus-internals.h>
 #include <dbus/dbus-message.h>
@@ -1647,7 +1648,7 @@ bus_driver_handle_get_connection_credentials (DBusConnection *connection,
   DBusMessageIter reply_iter;
   DBusMessageIter array_iter;
   unsigned long ulong_val;
-  char *windows_sid;
+  char *s;
   const char *service;
 
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
@@ -1682,26 +1683,43 @@ bus_driver_handle_get_connection_credentials (DBusConnection *connection,
         goto oom;
     }
 
-  if (dbus_connection_get_windows_user (conn, &windows_sid))
+  if (dbus_connection_get_windows_user (conn, &s))
     {
       DBusString str;
       dbus_bool_t result;
 
-      if (windows_sid == NULL)
+      if (s == NULL)
         goto oom;
 
-      _dbus_string_init_const (&str, windows_sid);
+      _dbus_string_init_const (&str, s);
       result = _dbus_validate_utf8 (&str, 0, _dbus_string_get_length (&str));
       _dbus_string_free (&str);
       if (result)
         {
-          if (!_dbus_asv_add_string (&array_iter, "WindowsSID", windows_sid))
+          if (!_dbus_asv_add_string (&array_iter, "WindowsSID", s))
             {
-              dbus_free(windows_sid);
+              dbus_free (s);
               goto oom;
             }
         }
-      dbus_free(windows_sid);
+      dbus_free (s);
+    }
+
+  if (_dbus_connection_get_linux_security_label (conn, &s))
+    {
+      if (s == NULL)
+        goto oom;
+
+      /* use the GVariant bytestring convention for strings of unknown
+       * encoding: include the \0 in the payload, for zero-copy reading */
+      if (!_dbus_asv_add_byte_array (&array_iter, "LinuxSecurityLabel",
+                                     s, strlen (s) + 1))
+        {
+          dbus_free (s);
+          goto oom;
+        }
+
+      dbus_free (s);
     }
 
   if (!_dbus_asv_close (&reply_iter, &array_iter))
index 64ad2b06eb42fd5f88a34242d2a73714fd96bf24..1503d5f1f74fc8af3cf168fcc8650a4784ce6679 100644 (file)
@@ -1102,20 +1102,23 @@ handle_server_data_external_mech (DBusAuth         *auth,
                                               auth->desired_identity))
         return FALSE;
 
-      /* also copy process ID from the socket credentials
+      /* also copy misc process info from the socket credentials
        */
       if (!_dbus_credentials_add_credential (auth->authorized_identity,
                                              DBUS_CREDENTIAL_UNIX_PROCESS_ID,
                                              auth->credentials))
         return FALSE;
 
-      /* also copy audit data from the socket credentials
-       */
       if (!_dbus_credentials_add_credential (auth->authorized_identity,
                                              DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
                                              auth->credentials))
         return FALSE;
-      
+
+      if (!_dbus_credentials_add_credential (auth->authorized_identity,
+                                             DBUS_CREDENTIAL_LINUX_SECURITY_LABEL,
+                                             auth->credentials))
+        return FALSE;
+
       if (!send_ok (auth))
         return FALSE;
 
index 28974040d0467502f6c65cc08cf545f5aa7bbaa4..64ef3365def834efb6a0cf3069832b0f353dd147 100644 (file)
@@ -107,6 +107,9 @@ void              _dbus_connection_set_pending_fds_function       (DBusConnectio
                                                                    DBusPendingFdsChangeFunction callback,
                                                                    void *data);
 
+dbus_bool_t       _dbus_connection_get_linux_security_label       (DBusConnection  *connection,
+                                                                   char           **label_p);
+
 /* if DBUS_ENABLE_STATS */
 void _dbus_connection_get_stats (DBusConnection *connection,
                                  dbus_uint32_t  *in_messages,
index b574207d3fe0cc8c20c4e4315697cd5e83c37800..8952b75f538aa6a7f87b10a6381c83bc17beb086 100644 (file)
@@ -5322,6 +5322,32 @@ dbus_connection_set_unix_user_function (DBusConnection             *connection,
     (* old_free_function) (old_data);
 }
 
+/* Same calling convention as dbus_connection_get_windows_user */
+dbus_bool_t
+_dbus_connection_get_linux_security_label (DBusConnection  *connection,
+                                           char           **label_p)
+{
+  dbus_bool_t result;
+
+  _dbus_assert (connection != NULL);
+  _dbus_assert (label_p != NULL);
+
+  CONNECTION_LOCK (connection);
+
+  if (!_dbus_transport_try_to_authenticate (connection->transport))
+    result = FALSE;
+  else
+    result = _dbus_transport_get_linux_security_label (connection->transport,
+                                                       label_p);
+#ifndef __linux__
+  _dbus_assert (!result);
+#endif
+
+  CONNECTION_UNLOCK (connection);
+
+  return result;
+}
+
 /**
  * Gets the Windows user SID of the connection if known.  Returns
  * #TRUE if the ID is filled in.  Always returns #FALSE on non-Windows
index 7325125c2080a2374ef84a2a60add23ac1d4c5b8..151bb00f99b4bb3ef441c1efbdb71bc9ee112758 100644 (file)
@@ -50,6 +50,7 @@ struct DBusCredentials {
   dbus_uid_t unix_uid;
   dbus_pid_t pid;
   char *windows_sid;
+  char *linux_security_label;
   void *adt_audit_data;
   dbus_int32_t adt_audit_data_size;
 };
@@ -79,6 +80,7 @@ _dbus_credentials_new (void)
   creds->unix_uid = DBUS_UID_UNSET;
   creds->pid = DBUS_PID_UNSET;
   creds->windows_sid = NULL;
+  creds->linux_security_label = NULL;
   creds->adt_audit_data = NULL;
   creds->adt_audit_data_size = 0;
 
@@ -133,6 +135,7 @@ _dbus_credentials_unref (DBusCredentials    *credentials)
   if (credentials->refcount == 0)
     {
       dbus_free (credentials->windows_sid);
+      dbus_free (credentials->linux_security_label);
       dbus_free (credentials->adt_audit_data);
       dbus_free (credentials);
     }
@@ -192,6 +195,30 @@ _dbus_credentials_add_windows_sid (DBusCredentials    *credentials,
   return TRUE;
 }
 
+/**
+ * Add a Linux security label, as used by LSMs such as SELinux, Smack and
+ * AppArmor, to the credentials.
+ *
+ * @param credentials the object
+ * @param label the label
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_linux_security_label (DBusCredentials    *credentials,
+                                            const char         *label)
+{
+  char *copy;
+
+  copy = _dbus_strdup (label);
+  if (copy == NULL)
+    return FALSE;
+
+  dbus_free (credentials->linux_security_label);
+  credentials->linux_security_label = copy;
+
+  return TRUE;
+}
+
 /**
  * Add ADT audit data to the credentials.
  *
@@ -236,6 +263,8 @@ _dbus_credentials_include (DBusCredentials    *credentials,
       return credentials->unix_uid != DBUS_UID_UNSET;
     case DBUS_CREDENTIAL_WINDOWS_SID:
       return credentials->windows_sid != NULL;
+    case DBUS_CREDENTIAL_LINUX_SECURITY_LABEL:
+      return credentials->linux_security_label != NULL;
     case DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID:
       return credentials->adt_audit_data != NULL;
     }
@@ -283,6 +312,19 @@ _dbus_credentials_get_windows_sid (DBusCredentials    *credentials)
   return credentials->windows_sid;
 }
 
+/**
+ * Gets the Linux security label (as used by LSMs) from the credentials,
+ * or #NULL if the credentials object doesn't contain a security label.
+ *
+ * @param credentials the object
+ * @returns the security label
+ */
+const char *
+_dbus_credentials_get_linux_security_label (DBusCredentials *credentials)
+{
+  return credentials->linux_security_label;
+}
+
 /**
  * Gets the ADT audit data in the credentials, or #NULL if
  * the credentials object doesn't contain ADT audit data.
@@ -329,6 +371,10 @@ _dbus_credentials_are_superset (DBusCredentials    *credentials,
     (possible_subset->windows_sid == NULL ||
      (credentials->windows_sid && strcmp (possible_subset->windows_sid,
                                           credentials->windows_sid) == 0)) &&
+    (possible_subset->linux_security_label == NULL ||
+     (credentials->linux_security_label != NULL &&
+      strcmp (possible_subset->linux_security_label,
+              credentials->linux_security_label) == 0)) &&
     (possible_subset->adt_audit_data == NULL ||
      (credentials->adt_audit_data && memcmp (possible_subset->adt_audit_data,
                                              credentials->adt_audit_data,
@@ -348,6 +394,7 @@ _dbus_credentials_are_empty (DBusCredentials    *credentials)
     credentials->pid == DBUS_PID_UNSET &&
     credentials->unix_uid == DBUS_UID_UNSET &&
     credentials->windows_sid == NULL &&
+    credentials->linux_security_label == NULL &&
     credentials->adt_audit_data == NULL;
 }
 
@@ -387,6 +434,9 @@ _dbus_credentials_add_credentials (DBusCredentials    *credentials,
     _dbus_credentials_add_credential (credentials,
                                       DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
                                       other_credentials) &&
+    _dbus_credentials_add_credential (credentials,
+                                      DBUS_CREDENTIAL_LINUX_SECURITY_LABEL,
+                                      other_credentials) &&
     _dbus_credentials_add_credential (credentials,
                                       DBUS_CREDENTIAL_WINDOWS_SID,
                                       other_credentials);
@@ -427,6 +477,13 @@ _dbus_credentials_add_credential (DBusCredentials    *credentials,
       if (!_dbus_credentials_add_windows_sid (credentials, other_credentials->windows_sid))
         return FALSE;
     } 
+  else if (which == DBUS_CREDENTIAL_LINUX_SECURITY_LABEL &&
+           other_credentials->linux_security_label != NULL)
+    {
+      if (!_dbus_credentials_add_linux_security_label (credentials,
+            other_credentials->linux_security_label))
+        return FALSE;
+    }
   else if (which == DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID &&
            other_credentials->adt_audit_data != NULL) 
     {
@@ -449,6 +506,8 @@ _dbus_credentials_clear (DBusCredentials    *credentials)
   credentials->unix_uid = DBUS_UID_UNSET;
   dbus_free (credentials->windows_sid);
   credentials->windows_sid = NULL;
+  dbus_free (credentials->linux_security_label);
+  credentials->linux_security_label = NULL;
   dbus_free (credentials->adt_audit_data);
   credentials->adt_audit_data = NULL;
   credentials->adt_audit_data_size = 0;
@@ -540,6 +599,15 @@ _dbus_credentials_to_string_append (DBusCredentials    *credentials,
   else
     join = FALSE;
 
+  if (credentials->linux_security_label != NULL)
+    {
+      if (!_dbus_string_append_printf (string, "%slsm='%s'",
+                                       join ? " " : "",
+                                       credentials->linux_security_label))
+        goto oom;
+      join = TRUE;
+    }
+
   return TRUE;
 oom:
   return FALSE;
index abcc4bb3257ef467e7fbef4d05ef8c7e4553a03c..ab74eac81702614ed62d60ea6ec53f0402dd9b61 100644 (file)
@@ -34,6 +34,7 @@ typedef enum {
   DBUS_CREDENTIAL_UNIX_PROCESS_ID,
   DBUS_CREDENTIAL_UNIX_USER_ID,
   DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
+  DBUS_CREDENTIAL_LINUX_SECURITY_LABEL,
   DBUS_CREDENTIAL_WINDOWS_SID
 } DBusCredentialType;
 
@@ -47,6 +48,8 @@ dbus_bool_t      _dbus_credentials_add_unix_uid             (DBusCredentials
                                                              dbus_uid_t          uid);
 dbus_bool_t      _dbus_credentials_add_windows_sid          (DBusCredentials    *credentials,
                                                              const char         *windows_sid);
+dbus_bool_t      _dbus_credentials_add_linux_security_label (DBusCredentials    *credentials,
+                                                             const char         *label);
 dbus_bool_t      _dbus_credentials_add_adt_audit_data       (DBusCredentials    *credentials,
                                                              void               *audit_data,
                                                              dbus_int32_t        size);
@@ -55,6 +58,7 @@ dbus_bool_t      _dbus_credentials_include                  (DBusCredentials
 dbus_pid_t       _dbus_credentials_get_pid                  (DBusCredentials    *credentials);
 dbus_uid_t       _dbus_credentials_get_unix_uid             (DBusCredentials    *credentials);
 const char*      _dbus_credentials_get_windows_sid          (DBusCredentials    *credentials);
+const char *     _dbus_credentials_get_linux_security_label (DBusCredentials    *credentials);
 void *           _dbus_credentials_get_adt_audit_data       (DBusCredentials    *credentials);
 dbus_int32_t     _dbus_credentials_get_adt_audit_data_size  (DBusCredentials    *credentials);
 dbus_bool_t      _dbus_credentials_are_superset             (DBusCredentials    *credentials,
index dcfddd17c28c16d93eaa60c2844c0e9435a96658..8010df1341c08088db3d60fba1c23a397d908231 100644 (file)
@@ -1666,6 +1666,105 @@ write_credentials_byte (int             server_fd,
     }
 }
 
+/* return FALSE on OOM, TRUE otherwise, even if no credentials were found */
+static dbus_bool_t
+add_linux_security_label_to_credentials (int              client_fd,
+                                         DBusCredentials *credentials)
+{
+#if defined(__linux__) && defined(SO_PEERSEC)
+  DBusString buf;
+  socklen_t len = 1024;
+  dbus_bool_t oom = FALSE;
+
+  if (!_dbus_string_init_preallocated (&buf, len) ||
+      !_dbus_string_set_length (&buf, len))
+    return FALSE;
+
+  while (getsockopt (client_fd, SOL_SOCKET, SO_PEERSEC,
+         _dbus_string_get_data (&buf), &len) < 0)
+    {
+      int e = errno;
+
+      _dbus_verbose ("getsockopt failed with %s, len now %lu\n",
+                     _dbus_strerror (e), (unsigned long) len);
+
+      if (e != ERANGE || len <= _dbus_string_get_length (&buf))
+        {
+          _dbus_verbose ("Failed to getsockopt(SO_PEERSEC): %s\n",
+                         _dbus_strerror (e));
+          goto out;
+        }
+
+      /* If not enough space, len is updated to be enough.
+       * Try again with a large enough buffer. */
+      if (!_dbus_string_set_length (&buf, len))
+        {
+          oom = TRUE;
+          goto out;
+        }
+
+      _dbus_verbose ("will try again with %lu\n", (unsigned long) len);
+    }
+
+  if (len <= 0)
+    {
+      _dbus_verbose ("getsockopt(SO_PEERSEC) yielded <= 0 bytes: %lu\n",
+                     (unsigned long) len);
+      goto out;
+    }
+
+  if (len > _dbus_string_get_length (&buf))
+    {
+      _dbus_verbose ("%lu > %d", (unsigned long) len,
+                     _dbus_string_get_length (&buf));
+      _dbus_assert_not_reached ("getsockopt(SO_PEERSEC) overflowed");
+    }
+
+  if (_dbus_string_get_byte (&buf, len - 1) == 0)
+    {
+      /* the kernel included the trailing \0 in its count,
+       * but DBusString always has an extra \0 after the data anyway */
+      _dbus_verbose ("subtracting trailing \\0\n");
+      len--;
+    }
+
+  if (!_dbus_string_set_length (&buf, len))
+    {
+      _dbus_assert_not_reached ("shortening string should not lead to OOM");
+      oom = TRUE;
+      goto out;
+    }
+
+  if (strlen (_dbus_string_get_const_data (&buf)) != len)
+    {
+      /* LSM people on the linux-security-module@ mailing list say this
+       * should never happen: the label should be a bytestring with
+       * an optional trailing \0 */
+      _dbus_verbose ("security label from kernel had an embedded \\0, "
+                     "ignoring it\n");
+      goto out;
+    }
+
+  _dbus_verbose ("getsockopt(SO_PEERSEC): %lu bytes excluding \\0: %s\n",
+                 (unsigned long) len,
+                 _dbus_string_get_const_data (&buf));
+
+  if (!_dbus_credentials_add_linux_security_label (credentials,
+        _dbus_string_get_const_data (&buf)))
+    {
+      oom = TRUE;
+      goto out;
+    }
+
+out:
+  _dbus_string_free (&buf);
+  return !oom;
+#else
+  /* no error */
+  return TRUE;
+#endif
+}
+
 /**
  * Reads a single byte which must be nul (an error occurs otherwise),
  * and reads unix credentials if available. Clears the credentials
@@ -1993,6 +2092,12 @@ _dbus_read_credentials_socket  (int              client_fd,
         }
     }
 
+  if (!add_linux_security_label_to_credentials (client_fd, credentials))
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
   return TRUE;
 }
 
index f63e0ced402b1ba3bc5eaff65c37c765c29e9b4d..5ceab0a9a5d027d5c26589f1c296923a6d3566ab 100644 (file)
@@ -1425,6 +1425,33 @@ _dbus_transport_set_unix_user_function (DBusTransport             *transport,
   transport->free_unix_user_data = free_data_function;
 }
 
+dbus_bool_t
+_dbus_transport_get_linux_security_label (DBusTransport  *transport,
+                                          char          **label_p)
+{
+  DBusCredentials *auth_identity;
+
+  *label_p = NULL;
+
+  if (!transport->authenticated)
+    return FALSE;
+
+  auth_identity = _dbus_auth_get_identity (transport->auth);
+
+  if (_dbus_credentials_include (auth_identity,
+                                 DBUS_CREDENTIAL_LINUX_SECURITY_LABEL))
+    {
+      /* If no memory, we are supposed to return TRUE and set NULL */
+      *label_p = _dbus_strdup (_dbus_credentials_get_linux_security_label (auth_identity));
+
+      return TRUE;
+    }
+  else
+    {
+      return FALSE;
+    }
+}
+
 /**
  * See dbus_connection_get_windows_user().
  *
index 39c74c46666d2ce91307c02ea2cd11a08f8ca23e..843f2312caf5a64baa70b17dad03f7375dd7f841 100644 (file)
@@ -87,6 +87,9 @@ void               _dbus_transport_set_unix_user_function (DBusTransport
                                                            DBusFreeFunction           *old_free_data_function);
 dbus_bool_t        _dbus_transport_get_windows_user       (DBusTransport              *transport,
                                                            char                      **windows_sid_p);
+dbus_bool_t        _dbus_transport_get_linux_security_label (DBusTransport            *transport,
+                                                           char                      **label_p);
+
 void               _dbus_transport_set_windows_user_function (DBusTransport              *transport,
                                                               DBusAllowWindowsUserFunction   function,
                                                               void                       *data,