summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2015-02-11 13:19:15 +0000
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2015-02-18 10:35:05 +0000
commit96c3bcec776f932fa1883f6a2597991d138e6925 (patch)
tree29dc0852500d439869a1135365996d2ca006d518
parentc966d903747dd6f8c57000e37ed6317af0c70b3d (diff)
downloaddbus-96c3bcec776f932fa1883f6a2597991d138e6925.tar.gz
Add LSM-agnostic support for LinuxSecurityLabel credential
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>
-rw-r--r--bus/driver.c32
-rw-r--r--dbus/dbus-auth.c11
-rw-r--r--dbus/dbus-connection-internal.h3
-rw-r--r--dbus/dbus-connection.c26
-rw-r--r--dbus/dbus-credentials.c68
-rw-r--r--dbus/dbus-credentials.h4
-rw-r--r--dbus/dbus-sysdeps-unix.c105
-rw-r--r--dbus/dbus-transport.c27
-rw-r--r--dbus/dbus-transport.h3
9 files changed, 268 insertions, 11 deletions
diff --git a/bus/driver.c b/bus/driver.c
index 603504f6..442dd01d 100644
--- a/bus/driver.c
+++ b/bus/driver.c
@@ -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))
diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c
index 64ad2b06..1503d5f1 100644
--- a/dbus/dbus-auth.c
+++ b/dbus/dbus-auth.c
@@ -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;
diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h
index 28974040..64ef3365 100644
--- a/dbus/dbus-connection-internal.h
+++ b/dbus/dbus-connection-internal.h
@@ -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,
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
index b574207d..8952b75f 100644
--- a/dbus/dbus-connection.c
+++ b/dbus/dbus-connection.c
@@ -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
diff --git a/dbus/dbus-credentials.c b/dbus/dbus-credentials.c
index 7325125c..151bb00f 100644
--- a/dbus/dbus-credentials.c
+++ b/dbus/dbus-credentials.c
@@ -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);
}
@@ -193,6 +196,30 @@ _dbus_credentials_add_windows_sid (DBusCredentials *credentials,
}
/**
+ * 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.
*
* @param credentials the object
@@ -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;
}
@@ -284,6 +313,19 @@ _dbus_credentials_get_windows_sid (DBusCredentials *credentials)
}
/**
+ * 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;
}
@@ -388,6 +435,9 @@ _dbus_credentials_add_credentials (DBusCredentials *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;
diff --git a/dbus/dbus-credentials.h b/dbus/dbus-credentials.h
index abcc4bb3..ab74eac8 100644
--- a/dbus/dbus-credentials.h
+++ b/dbus/dbus-credentials.h
@@ -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,
diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c
index dcfddd17..8010df13 100644
--- a/dbus/dbus-sysdeps-unix.c
+++ b/dbus/dbus-sysdeps-unix.c
@@ -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;
}
diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c
index f63e0ced..5ceab0a9 100644
--- a/dbus/dbus-transport.c
+++ b/dbus/dbus-transport.c
@@ -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().
*
diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h
index 39c74c46..843f2312 100644
--- a/dbus/dbus-transport.h
+++ b/dbus/dbus-transport.h
@@ -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,