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>
This commit is contained in:
Simon McVittie 2015-02-11 13:19:15 +00:00
parent c966d90374
commit 96c3bcec77
9 changed files with 268 additions and 11 deletions

View 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))

View 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;

View 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,

View 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

View 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;

View 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,

View 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;
}

View 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().
*

View 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,