mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2026-01-06 04:50:14 +01:00
sysdeps: Get complete group vector from Linux SO_PEERGROUPS if possible
Signed-off-by: Simon McVittie <smcv@collabora.com> Bug: https://bugs.freedesktop.org/show_bug.cgi?id=103737 Reviewed-by: Philip Withnall <withnall@endlessm.com>
This commit is contained in:
parent
5a07eda31e
commit
b6648ca655
4 changed files with 262 additions and 0 deletions
|
|
@ -1165,6 +1165,11 @@ handle_server_data_external_mech (DBusAuth *auth,
|
|||
auth->credentials))
|
||||
return FALSE;
|
||||
|
||||
if (!_dbus_credentials_add_credential (auth->authorized_identity,
|
||||
DBUS_CREDENTIAL_UNIX_GROUP_IDS,
|
||||
auth->credentials))
|
||||
return FALSE;
|
||||
|
||||
if (!_dbus_credentials_add_credential (auth->authorized_identity,
|
||||
DBUS_CREDENTIAL_LINUX_SECURITY_LABEL,
|
||||
auth->credentials))
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
*/
|
||||
#include <config.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "dbus-credentials.h"
|
||||
#include "dbus-internals.h"
|
||||
|
|
@ -48,6 +49,8 @@
|
|||
struct DBusCredentials {
|
||||
int refcount;
|
||||
dbus_uid_t unix_uid;
|
||||
dbus_gid_t *unix_gids;
|
||||
size_t n_unix_gids;
|
||||
dbus_pid_t pid;
|
||||
char *windows_sid;
|
||||
char *linux_security_label;
|
||||
|
|
@ -78,6 +81,8 @@ _dbus_credentials_new (void)
|
|||
|
||||
creds->refcount = 1;
|
||||
creds->unix_uid = DBUS_UID_UNSET;
|
||||
creds->unix_gids = NULL;
|
||||
creds->n_unix_gids = 0;
|
||||
creds->pid = DBUS_PID_UNSET;
|
||||
creds->windows_sid = NULL;
|
||||
creds->linux_security_label = NULL;
|
||||
|
|
@ -134,6 +139,7 @@ _dbus_credentials_unref (DBusCredentials *credentials)
|
|||
credentials->refcount -= 1;
|
||||
if (credentials->refcount == 0)
|
||||
{
|
||||
dbus_free (credentials->unix_gids);
|
||||
dbus_free (credentials->windows_sid);
|
||||
dbus_free (credentials->linux_security_label);
|
||||
dbus_free (credentials->adt_audit_data);
|
||||
|
|
@ -172,6 +178,63 @@ _dbus_credentials_add_unix_uid(DBusCredentials *credentials,
|
|||
|
||||
}
|
||||
|
||||
static int
|
||||
cmp_gidp (const void *a_, const void *b_)
|
||||
{
|
||||
const dbus_gid_t *a = a_;
|
||||
const dbus_gid_t *b = b_;
|
||||
|
||||
if (*a < *b)
|
||||
return -1;
|
||||
|
||||
if (*a > *b)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add UNIX group IDs to the credentials, replacing any group IDs that
|
||||
* might already have been present.
|
||||
*
|
||||
* @param credentials the object
|
||||
* @param gids the group IDs, which will be freed by the DBusCredentials object
|
||||
* @param n_gids the number of group IDs
|
||||
*/
|
||||
void
|
||||
_dbus_credentials_take_unix_gids (DBusCredentials *credentials,
|
||||
dbus_gid_t *gids,
|
||||
size_t n_gids)
|
||||
{
|
||||
/* So we can compare arrays via a simple memcmp */
|
||||
qsort (gids, n_gids, sizeof (dbus_gid_t), cmp_gidp);
|
||||
|
||||
dbus_free (credentials->unix_gids);
|
||||
credentials->unix_gids = gids;
|
||||
credentials->n_unix_gids = n_gids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Unix group IDs.
|
||||
*
|
||||
* @param credentials the object
|
||||
* @param gids the group IDs, which will be freed by the DBusCredentials object
|
||||
* @param n_gids the number of group IDs
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_credentials_get_unix_gids (DBusCredentials *credentials,
|
||||
const dbus_gid_t **gids,
|
||||
size_t *n_gids)
|
||||
{
|
||||
if (gids != NULL)
|
||||
*gids = credentials->unix_gids;
|
||||
|
||||
if (n_gids != NULL)
|
||||
*n_gids = credentials->n_unix_gids;
|
||||
|
||||
return (credentials->unix_gids != NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Windows user SID to the credentials.
|
||||
*
|
||||
|
|
@ -261,6 +324,8 @@ _dbus_credentials_include (DBusCredentials *credentials,
|
|||
return credentials->pid != DBUS_PID_UNSET;
|
||||
case DBUS_CREDENTIAL_UNIX_USER_ID:
|
||||
return credentials->unix_uid != DBUS_UID_UNSET;
|
||||
case DBUS_CREDENTIAL_UNIX_GROUP_IDS:
|
||||
return credentials->unix_gids != NULL;
|
||||
case DBUS_CREDENTIAL_WINDOWS_SID:
|
||||
return credentials->windows_sid != NULL;
|
||||
case DBUS_CREDENTIAL_LINUX_SECURITY_LABEL:
|
||||
|
|
@ -368,6 +433,10 @@ _dbus_credentials_are_superset (DBusCredentials *credentials,
|
|||
possible_subset->pid == credentials->pid) &&
|
||||
(possible_subset->unix_uid == DBUS_UID_UNSET ||
|
||||
possible_subset->unix_uid == credentials->unix_uid) &&
|
||||
(possible_subset->unix_gids == NULL ||
|
||||
(possible_subset->n_unix_gids == credentials->n_unix_gids &&
|
||||
memcmp (possible_subset->unix_gids, credentials->unix_gids,
|
||||
sizeof (dbus_gid_t) * credentials->n_unix_gids) == 0)) &&
|
||||
(possible_subset->windows_sid == NULL ||
|
||||
(credentials->windows_sid && strcmp (possible_subset->windows_sid,
|
||||
credentials->windows_sid) == 0)) &&
|
||||
|
|
@ -393,6 +462,8 @@ _dbus_credentials_are_empty (DBusCredentials *credentials)
|
|||
return
|
||||
credentials->pid == DBUS_PID_UNSET &&
|
||||
credentials->unix_uid == DBUS_UID_UNSET &&
|
||||
credentials->unix_gids == NULL &&
|
||||
credentials->n_unix_gids == 0 &&
|
||||
credentials->windows_sid == NULL &&
|
||||
credentials->linux_security_label == NULL &&
|
||||
credentials->adt_audit_data == NULL;
|
||||
|
|
@ -431,6 +502,9 @@ _dbus_credentials_add_credentials (DBusCredentials *credentials,
|
|||
_dbus_credentials_add_credential (credentials,
|
||||
DBUS_CREDENTIAL_UNIX_USER_ID,
|
||||
other_credentials) &&
|
||||
_dbus_credentials_add_credential (credentials,
|
||||
DBUS_CREDENTIAL_UNIX_GROUP_IDS,
|
||||
other_credentials) &&
|
||||
_dbus_credentials_add_credential (credentials,
|
||||
DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
|
||||
other_credentials) &&
|
||||
|
|
@ -471,6 +545,22 @@ _dbus_credentials_add_credential (DBusCredentials *credentials,
|
|||
if (!_dbus_credentials_add_unix_uid (credentials, other_credentials->unix_uid))
|
||||
return FALSE;
|
||||
}
|
||||
else if (which == DBUS_CREDENTIAL_UNIX_GROUP_IDS &&
|
||||
other_credentials->unix_gids != NULL)
|
||||
{
|
||||
dbus_gid_t *gids;
|
||||
|
||||
gids = dbus_new (dbus_gid_t, other_credentials->n_unix_gids);
|
||||
|
||||
if (gids == NULL)
|
||||
return FALSE;
|
||||
|
||||
memcpy (gids, other_credentials->unix_gids,
|
||||
sizeof (dbus_gid_t) * other_credentials->n_unix_gids);
|
||||
|
||||
_dbus_credentials_take_unix_gids (credentials, gids,
|
||||
other_credentials->n_unix_gids);
|
||||
}
|
||||
else if (which == DBUS_CREDENTIAL_WINDOWS_SID &&
|
||||
other_credentials->windows_sid != NULL)
|
||||
{
|
||||
|
|
@ -504,6 +594,9 @@ _dbus_credentials_clear (DBusCredentials *credentials)
|
|||
{
|
||||
credentials->pid = DBUS_PID_UNSET;
|
||||
credentials->unix_uid = DBUS_UID_UNSET;
|
||||
dbus_free (credentials->unix_gids);
|
||||
credentials->unix_gids = NULL;
|
||||
credentials->n_unix_gids = 0;
|
||||
dbus_free (credentials->windows_sid);
|
||||
credentials->windows_sid = NULL;
|
||||
dbus_free (credentials->linux_security_label);
|
||||
|
|
@ -590,6 +683,22 @@ _dbus_credentials_to_string_append (DBusCredentials *credentials,
|
|||
}
|
||||
else
|
||||
join = FALSE;
|
||||
|
||||
if (credentials->unix_gids != NULL)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < credentials->n_unix_gids; i++)
|
||||
{
|
||||
if (!_dbus_string_append_printf (string, "%sgid=" DBUS_GID_FORMAT,
|
||||
join ? " " : "",
|
||||
credentials->unix_gids[i]))
|
||||
goto oom;
|
||||
|
||||
join = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (credentials->windows_sid != NULL)
|
||||
{
|
||||
if (!_dbus_string_append_printf (string, "%ssid=%s", join ? " " : "", credentials->windows_sid))
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ DBUS_BEGIN_DECLS
|
|||
typedef enum {
|
||||
DBUS_CREDENTIAL_UNIX_PROCESS_ID,
|
||||
DBUS_CREDENTIAL_UNIX_USER_ID,
|
||||
DBUS_CREDENTIAL_UNIX_GROUP_IDS,
|
||||
DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
|
||||
DBUS_CREDENTIAL_LINUX_SECURITY_LABEL,
|
||||
DBUS_CREDENTIAL_WINDOWS_SID
|
||||
|
|
@ -53,6 +54,10 @@ DBUS_PRIVATE_EXPORT
|
|||
dbus_bool_t _dbus_credentials_add_unix_uid (DBusCredentials *credentials,
|
||||
dbus_uid_t uid);
|
||||
DBUS_PRIVATE_EXPORT
|
||||
void _dbus_credentials_take_unix_gids (DBusCredentials *credentials,
|
||||
dbus_gid_t *gids,
|
||||
size_t n_gids);
|
||||
DBUS_PRIVATE_EXPORT
|
||||
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,
|
||||
|
|
@ -68,6 +73,10 @@ dbus_pid_t _dbus_credentials_get_pid (DBusCredentials
|
|||
DBUS_PRIVATE_EXPORT
|
||||
dbus_uid_t _dbus_credentials_get_unix_uid (DBusCredentials *credentials);
|
||||
DBUS_PRIVATE_EXPORT
|
||||
dbus_bool_t _dbus_credentials_get_unix_gids (DBusCredentials *credentials,
|
||||
const dbus_gid_t **gids,
|
||||
size_t *n_gids);
|
||||
DBUS_PRIVATE_EXPORT
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -1748,6 +1748,129 @@ write_credentials_byte (int server_fd,
|
|||
}
|
||||
}
|
||||
|
||||
/* return FALSE on OOM, TRUE otherwise, even if no groups were found */
|
||||
static dbus_bool_t
|
||||
add_groups_to_credentials (int client_fd,
|
||||
DBusCredentials *credentials,
|
||||
dbus_gid_t primary)
|
||||
{
|
||||
#if defined(__linux__) && defined(SO_PEERGROUPS)
|
||||
_DBUS_STATIC_ASSERT (sizeof (gid_t) <= sizeof (dbus_gid_t));
|
||||
gid_t *buf = NULL;
|
||||
socklen_t len = 1024;
|
||||
dbus_bool_t oom = FALSE;
|
||||
/* libdbus has a different representation of group IDs just to annoy you */
|
||||
dbus_gid_t *converted_gids = NULL;
|
||||
dbus_bool_t need_primary = TRUE;
|
||||
size_t n_gids;
|
||||
size_t i;
|
||||
|
||||
n_gids = ((size_t) len) / sizeof (gid_t);
|
||||
buf = dbus_new (gid_t, n_gids);
|
||||
|
||||
if (buf == NULL)
|
||||
return FALSE;
|
||||
|
||||
while (getsockopt (client_fd, SOL_SOCKET, SO_PEERGROUPS, buf, &len) < 0)
|
||||
{
|
||||
int e = errno;
|
||||
gid_t *replacement;
|
||||
|
||||
_dbus_verbose ("getsockopt failed with %s, len now %lu\n",
|
||||
_dbus_strerror (e), (unsigned long) len);
|
||||
|
||||
if (e != ERANGE || (size_t) len <= n_gids * sizeof (gid_t))
|
||||
{
|
||||
_dbus_verbose ("Failed to getsockopt(SO_PEERGROUPS): %s\n",
|
||||
_dbus_strerror (e));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If not enough space, len is updated to be enough.
|
||||
* Try again with a large enough buffer. */
|
||||
n_gids = ((size_t) len) / sizeof (gid_t);
|
||||
replacement = dbus_realloc (buf, len);
|
||||
|
||||
if (replacement == NULL)
|
||||
{
|
||||
oom = TRUE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = replacement;
|
||||
_dbus_verbose ("will try again with %lu\n", (unsigned long) len);
|
||||
}
|
||||
|
||||
if (len <= 0)
|
||||
{
|
||||
_dbus_verbose ("getsockopt(SO_PEERGROUPS) yielded <= 0 bytes: %ld\n",
|
||||
(long) len);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len > n_gids * sizeof (gid_t))
|
||||
{
|
||||
_dbus_verbose ("%lu > %zu", (unsigned long) len, n_gids * sizeof (gid_t));
|
||||
_dbus_assert_not_reached ("getsockopt(SO_PEERGROUPS) overflowed");
|
||||
}
|
||||
|
||||
if (len % sizeof (gid_t) != 0)
|
||||
{
|
||||
_dbus_verbose ("getsockopt(SO_PEERGROUPS) did not return an "
|
||||
"integer multiple of sizeof(gid_t): %lu should be "
|
||||
"divisible by %zu",
|
||||
(unsigned long) len, sizeof (gid_t));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Allocate an extra space for the primary group ID */
|
||||
n_gids = ((size_t) len) / sizeof (gid_t);
|
||||
|
||||
/* If n_gids is less than this, then (n_gids + 1) certainly doesn't
|
||||
* overflow, and neither does multiplying that by sizeof(dbus_gid_t).
|
||||
* This is using _DBUS_INT32_MAX as a conservative lower bound for
|
||||
* the maximum size_t. */
|
||||
if (n_gids >= (_DBUS_INT32_MAX / sizeof (dbus_gid_t)) - 1)
|
||||
{
|
||||
_dbus_verbose ("getsockopt(SO_PEERGROUPS) returned a huge number "
|
||||
"of groups (%lu bytes), ignoring",
|
||||
(unsigned long) len);
|
||||
goto out;
|
||||
}
|
||||
|
||||
converted_gids = dbus_new (dbus_gid_t, n_gids + 1);
|
||||
|
||||
if (converted_gids == NULL)
|
||||
{
|
||||
oom = TRUE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < n_gids; i++)
|
||||
{
|
||||
converted_gids[i] = (dbus_gid_t) buf[i];
|
||||
|
||||
if (converted_gids[i] == primary)
|
||||
need_primary = FALSE;
|
||||
}
|
||||
|
||||
if (need_primary && primary != DBUS_GID_UNSET)
|
||||
{
|
||||
converted_gids[n_gids] = primary;
|
||||
n_gids++;
|
||||
}
|
||||
|
||||
_dbus_credentials_take_unix_gids (credentials, converted_gids, n_gids);
|
||||
|
||||
out:
|
||||
dbus_free (buf);
|
||||
return !oom;
|
||||
#else
|
||||
/* no error */
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* 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,
|
||||
|
|
@ -1896,6 +2019,7 @@ _dbus_read_credentials_socket (DBusSocket client_fd,
|
|||
struct iovec iov;
|
||||
char buf;
|
||||
dbus_uid_t uid_read;
|
||||
dbus_gid_t primary_gid_read;
|
||||
dbus_pid_t pid_read;
|
||||
int bytes_read;
|
||||
|
||||
|
|
@ -1915,6 +2039,7 @@ _dbus_read_credentials_socket (DBusSocket client_fd,
|
|||
_DBUS_STATIC_ASSERT (sizeof (gid_t) <= sizeof (dbus_gid_t));
|
||||
|
||||
uid_read = DBUS_UID_UNSET;
|
||||
primary_gid_read = DBUS_GID_UNSET;
|
||||
pid_read = DBUS_PID_UNSET;
|
||||
|
||||
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
|
||||
|
|
@ -2001,6 +2126,12 @@ _dbus_read_credentials_socket (DBusSocket client_fd,
|
|||
{
|
||||
pid_read = cr.pid;
|
||||
uid_read = cr.uid;
|
||||
#ifdef __linux__
|
||||
/* Do other platforms have cr.gid? (Not that it really matters,
|
||||
* because the gid is useless to us unless we know the complete
|
||||
* group vector, which we only know on Linux.) */
|
||||
primary_gid_read = cr.gid;
|
||||
#endif
|
||||
}
|
||||
#elif defined(HAVE_UNPCBID) && defined(LOCAL_PEEREID)
|
||||
/* Another variant of the above - used on NetBSD
|
||||
|
|
@ -2181,6 +2312,14 @@ _dbus_read_credentials_socket (DBusSocket client_fd,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/* We don't put any groups in the credentials unless we can put them
|
||||
* all there. */
|
||||
if (!add_groups_to_credentials (client_fd.fd, credentials, primary_gid_read))
|
||||
{
|
||||
_DBUS_SET_OOM (error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue