2003-04-17 Havoc Pennington <hp@redhat.com>

* dbus/dbus-userdb.c, dbus/dbus-sysdeps.c: redo all the passwd
	database usage so it all goes via the DBusUserDatabase cache.
This commit is contained in:
Havoc Pennington 2003-04-17 23:17:04 +00:00
parent 4219b08bfa
commit 88cd5da3c0
10 changed files with 1028 additions and 542 deletions

View file

@ -1,3 +1,8 @@
2003-04-17 Havoc Pennington <hp@redhat.com>
* dbus/dbus-userdb.c, dbus/dbus-sysdeps.c: redo all the passwd
database usage so it all goes via the DBusUserDatabase cache.
2003-04-17 Havoc Pennington <hp@redhat.com>
* dbus/dbus-mainloop.c (_dbus_loop_iterate): fix logic so that if

View file

@ -30,6 +30,7 @@
#include "dbus-hash.h"
#include "dbus-internals.h"
#include "dbus-marshal.h"
#include "dbus-userdb.h"
/**
* @defgroup DBusAuthScript code for running unit test scripts for DBusAuth
@ -373,7 +374,8 @@ _dbus_auth_script_run (const DBusString *filename)
goto out;
}
if (!_dbus_string_append_our_uid (&username))
if (!_dbus_string_append_uint (&username,
_dbus_getuid ()))
{
_dbus_warn ("no memory for userid\n");
_dbus_string_free (&username);
@ -407,7 +409,7 @@ _dbus_auth_script_run (const DBusString *filename)
goto out;
}
if (!_dbus_user_info_from_current_process (&u, NULL, NULL) ||
if (!_dbus_username_from_current_process (&u) ||
!_dbus_string_copy (u, 0, &username,
_dbus_string_get_length (&username)))
{

View file

@ -26,6 +26,7 @@
#include "dbus-internals.h"
#include "dbus-keyring.h"
#include "dbus-sha.h"
#include "dbus-userdb.h"
/* See doc/dbus-sasl-profile.txt */
@ -712,8 +713,7 @@ handle_client_initial_response_cookie_sha1_mech (DBusAuth *auth,
retval = FALSE;
if (!_dbus_user_info_from_current_process (&username,
NULL, NULL))
if (!_dbus_username_from_current_process (&username))
goto out_0;
if (!_dbus_string_base64_encode (username, 0,
@ -1000,8 +1000,8 @@ handle_server_data_external_mech (DBusAuth *auth,
}
else
{
if (!_dbus_credentials_from_uid_string (&auth->identity,
&auth->desired_identity))
if (!_dbus_uid_from_string (&auth->identity,
&auth->desired_identity.uid))
{
_dbus_verbose ("could not get credentials from uid string\n");
return send_rejected (auth);
@ -1066,7 +1066,8 @@ handle_client_initial_response_external_mech (DBusAuth *auth,
if (!_dbus_string_init (&plaintext))
return FALSE;
if (!_dbus_string_append_our_uid (&plaintext))
if (!_dbus_string_append_uint (&plaintext,
_dbus_getuid ()))
goto failed;
if (!_dbus_string_base64_encode (&plaintext, 0,

View file

@ -195,9 +195,9 @@ _DBUS_DECLARE_GLOBAL_LOCK (connection_slots);
_DBUS_DECLARE_GLOBAL_LOCK (server_slots);
_DBUS_DECLARE_GLOBAL_LOCK (atomic);
_DBUS_DECLARE_GLOBAL_LOCK (message_handler);
_DBUS_DECLARE_GLOBAL_LOCK (user_info);
_DBUS_DECLARE_GLOBAL_LOCK (bus);
_DBUS_DECLARE_GLOBAL_LOCK (shutdown_funcs);
_DBUS_DECLARE_GLOBAL_LOCK (system_users);
#define _DBUS_N_GLOBAL_LOCKS (8)
dbus_bool_t _dbus_threads_init_debug (void);

View file

@ -22,6 +22,7 @@
*/
#include "dbus-keyring.h"
#include "dbus-userdb.h"
#include <dbus/dbus-string.h>
#include <dbus/dbus-list.h>
#include <dbus/dbus-sysdeps.h>
@ -699,10 +700,9 @@ _dbus_keyring_new_homedir (const DBusString *username,
if (username == NULL)
{
const DBusString *const_homedir;
if (!_dbus_user_info_from_current_process (&username,
&const_homedir,
NULL))
if (!_dbus_username_from_current_process (&username) ||
!_dbus_homedir_from_current_process (&const_homedir))
goto failed;
if (!_dbus_string_copy (const_homedir, 0,

View file

@ -1310,85 +1310,52 @@ _dbus_string_parse_double (const DBusString *str,
* @addtogroup DBusInternalsUtils
* @{
*/
static dbus_bool_t
store_user_info (struct passwd *p,
DBusCredentials *credentials,
DBusString *homedir,
DBusString *username_out,
DBusError *error)
fill_user_info_from_passwd (struct passwd *p,
DBusUserInfo *info,
DBusError *error)
{
int old_homedir_len;
_dbus_assert (p->pw_name != NULL);
_dbus_assert (p->pw_dir != NULL);
if (credentials != NULL)
{
credentials->uid = p->pw_uid;
credentials->gid = p->pw_gid;
}
old_homedir_len = 0;
if (homedir != NULL)
{
old_homedir_len = _dbus_string_get_length (homedir);
if (!_dbus_string_append (homedir, p->pw_dir))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return FALSE;
}
}
info->uid = p->pw_uid;
info->primary_gid = p->pw_gid;
info->username = _dbus_strdup (p->pw_name);
info->homedir = _dbus_strdup (p->pw_dir);
if (username_out &&
!_dbus_string_append (username_out, p->pw_name))
if (info->username == NULL ||
info->homedir == NULL)
{
if (homedir)
_dbus_string_set_length (homedir, old_homedir_len);
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return FALSE;
}
_dbus_verbose ("Username %s has uid %d gid %d homedir %s\n",
p->pw_name, (int) p->pw_uid, (int) p->pw_gid,
p->pw_dir);
return TRUE;
}
/**
* Gets user info using either username or uid. Only
* one of these may be passed in, either username
* must be #NULL or uid must be < 0.
*
* @param username the username
* @param uid the user ID
* @param credentials to fill in or #NULL
* @param homedir string to append homedir to or #NULL
* @param username_out string to append username to or #NULL
* @param error return location for reason for failure
*
* @returns #TRUE on success
*/
static dbus_bool_t
get_user_info (const DBusString *username,
dbus_uid_t uid,
DBusCredentials *credentials,
DBusString *homedir,
DBusString *username_out,
DBusError *error)
fill_user_info (DBusUserInfo *info,
dbus_uid_t uid,
const DBusString *username,
DBusError *error)
{
const char *username_c_str;
const char *username_c;
/* exactly one of username/uid provided */
_dbus_assert (username != NULL || uid >= 0);
_dbus_assert (username != NULL || uid != DBUS_UID_UNSET);
_dbus_assert (username == NULL || uid == DBUS_UID_UNSET);
if (credentials)
_dbus_credentials_clear (credentials);
info->uid = DBUS_UID_UNSET;
info->primary_gid = DBUS_GID_UNSET;
info->group_ids = NULL;
info->n_group_ids = 0;
info->username = NULL;
info->homedir = NULL;
if (username != NULL)
username_c_str = _dbus_string_get_const_data (username);
username_c = _dbus_string_get_const_data (username);
else
username_c_str = NULL;
username_c = NULL;
/* For now assuming that the getpwnam() and getpwuid() flavors
* are always symmetrical, if not we have to add more configure
@ -1408,26 +1375,26 @@ get_user_info (const DBusString *username,
result = getpwuid_r (uid, &p_str, buf, sizeof (buf),
&p);
else
result = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf),
result = getpwnam_r (username_c, &p_str, buf, sizeof (buf),
&p);
#else
if (uid >= 0)
p = getpwuid_r (uid, &p_str, buf, sizeof (buf));
else
p = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf));
p = getpwnam_r (username_c, &p_str, buf, sizeof (buf));
result = 0;
#endif /* !HAVE_POSIX_GETPWNAME_R */
if (result == 0 && p == &p_str)
{
return store_user_info (p, credentials, homedir,
username_out, error);
if (!fill_user_info_from_passwd (p, info, error))
return FALSE;
}
else
{
dbus_set_error (error, DBUS_ERROR_FAILED,
dbus_set_error (error, _dbus_error_from_errno (errno),
"User \"%s\" unknown or no memory to allocate password entry\n",
username_c_str);
_dbus_verbose ("User %s unknown\n", username_c_str);
username_c ? username_c : "???");
_dbus_verbose ("User %s unknown\n", username_c ? username_c : "???");
return FALSE;
}
}
@ -1439,23 +1406,269 @@ get_user_info (const DBusString *username,
if (uid >= 0)
p = getpwuid (uid);
else
p = getpwnam (username_c_str);
p = getpwnam (username_c);
if (p != NULL)
{
return store_user_info (p, credentials, homedir,
username_out, error);
if (!fill_user_info_from_passwd (p, info, error))
return FALSE;
}
else
{
dbus_set_error (error, DBUS_ERROR_FAILED,
dbus_set_error (error, _dbus_error_from_errno (errno),
"User \"%s\" unknown or no memory to allocate password entry\n",
username_c_str);
_dbus_verbose ("User %s unknown\n", username_c_str);
username_c ? username_c : "???");
_dbus_verbose ("User %s unknown\n", username_c ? username_c : "???");
return FALSE;
}
}
#endif /* ! HAVE_GETPWNAM_R */
/* Fill this in so we can use it to get groups */
username_c = info->username;
#ifdef HAVE_GETGROUPLIST
{
gid_t *buf;
int buf_count;
int i;
buf_count = 17;
buf = dbus_new (gid_t, buf_count);
if (buf == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto failed;
}
if (getgrouplist (username_c,
info->primary_gid,
buf, &buf_count) < 0)
{
gid_t *new = dbus_realloc (buf, buf_count * sizeof (buf[0]));
if (new == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
dbus_free (buf);
goto failed;
}
buf = new;
getgrouplist (username_c, info->primary_gid, buf, &buf_count);
}
info->group_ids = dbus_new (dbus_gid_t, buf_count);
if (info->group_ids == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
dbus_free (buf);
goto failed;
}
for (i = 0; i < buf_count; ++i)
info->group_ids[i] = buf[i];
info->n_group_ids = buf_count;
dbus_free (buf);
}
#else /* HAVE_GETGROUPLIST */
{
/* We just get the one group ID */
info->group_ids = dbus_new (dbus_gid_t, 1);
if (info->group_ids == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto out;
}
info->n_group_ids = 1;
(info->group_ids)[0] = info->primary_gid;
}
#endif /* HAVE_GETGROUPLIST */
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
return TRUE;
failed:
_DBUS_ASSERT_ERROR_IS_SET (error);
_dbus_user_info_free (info);
return FALSE;
}
/**
* Gets user info for the given username.
*
* @param info user info object to initialize
* @param username the username
* @param error error return
* @returns #TRUE on success
*/
dbus_bool_t
_dbus_user_info_fill (DBusUserInfo *info,
const DBusString *username,
DBusError *error)
{
return fill_user_info (info, DBUS_UID_UNSET,
username, error);
}
/**
* Gets user info for the given user ID.
*
* @param info user info object to initialize
* @param username the username
* @param error error return
* @returns #TRUE on success
*/
dbus_bool_t
_dbus_user_info_fill_uid (DBusUserInfo *info,
dbus_uid_t uid,
DBusError *error)
{
return fill_user_info (info, uid,
NULL, error);
}
/**
* Frees the members of info
* (but not info itself)
* @param info the user info struct
*/
void
_dbus_user_info_free (DBusUserInfo *info)
{
dbus_free (info->group_ids);
dbus_free (info->username);
dbus_free (info->homedir);
}
static dbus_bool_t
fill_user_info_from_group (struct group *g,
DBusGroupInfo *info,
DBusError *error)
{
_dbus_assert (g->gr_name != NULL);
info->gid = g->gr_gid;
info->groupname = _dbus_strdup (g->gr_name);
/* info->members = dbus_strdupv (g->gr_mem) */
if (info->groupname == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return FALSE;
}
return TRUE;
}
static dbus_bool_t
fill_group_info (DBusGroupInfo *info,
dbus_gid_t gid,
const DBusString *groupname,
DBusError *error)
{
const char *group_c_str;
_dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET);
_dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET);
if (groupname)
group_c_str = _dbus_string_get_const_data (groupname);
else
group_c_str = NULL;
/* For now assuming that the getgrnam() and getgrgid() flavors
* always correspond to the pwnam flavors, if not we have
* to add more configure checks.
*/
#if defined (HAVE_POSIX_GETPWNAME_R) || defined (HAVE_NONPOSIX_GETPWNAME_R)
{
struct group *g;
int result;
char buf[1024];
struct group g_str;
g = NULL;
#ifdef HAVE_POSIX_GETPWNAME_R
if (group_c_str)
result = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf),
&g);
else
result = getgrgid_r (gid, &g_str, buf, sizeof (buf),
&g);
#else
p = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf));
result = 0;
#endif /* !HAVE_POSIX_GETPWNAME_R */
if (result == 0 && g == &g_str)
{
return fill_user_info_from_group (g, info, error);
}
else
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Group %s unknown or failed to look it up\n",
group_c_str ? group_c_str : "???");
return FALSE;
}
}
#else /* ! HAVE_GETPWNAM_R */
{
/* I guess we're screwed on thread safety here */
struct group *g;
g = getgrnam (group_c_str);
if (g != NULL)
{
return fill_user_info_from_group (g, info, error);
}
else
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Group %s unknown or failed to look it up\n",
group_c_str ? group_c_str : "???");
return FALSE;
}
}
#endif /* ! HAVE_GETPWNAM_R */
}
dbus_bool_t
_dbus_group_info_fill (DBusGroupInfo *info,
const DBusString *groupname,
DBusError *error)
{
return fill_group_info (info, DBUS_GID_UNSET,
groupname, error);
}
dbus_bool_t
_dbus_group_info_fill_gid (DBusGroupInfo *info,
dbus_gid_t gid,
DBusError *error)
{
return fill_group_info (info, gid, NULL, error);
}
/**
* Frees the members of info (but not info itself).
*
* @param info the group info
*/
void
_dbus_group_info_free (DBusGroupInfo *info)
{
dbus_free (info->groupname);
}
/**
@ -1472,200 +1685,6 @@ _dbus_credentials_clear (DBusCredentials *credentials)
credentials->gid = DBUS_GID_UNSET;
}
/**
* Gets the credentials corresponding to the given username.
*
* @param username the username
* @param credentials credentials to fill in
* @returns #TRUE if the username existed and we got some credentials
*/
dbus_bool_t
_dbus_credentials_from_username (const DBusString *username,
DBusCredentials *credentials)
{
return get_user_info (username, -1, credentials, NULL, NULL, NULL);
}
/**
* Gets user ID given username
*
* @param username the username
* @param uid return location for UID
* @returns #TRUE if username existed and we got the UID
*/
dbus_bool_t
_dbus_get_user_id (const DBusString *username,
dbus_uid_t *uid)
{
DBusCredentials creds;
if (!_dbus_credentials_from_username (username, &creds))
return FALSE;
if (creds.uid == DBUS_UID_UNSET)
return FALSE;
*uid = creds.uid;
return TRUE;
}
/**
* Gets the credentials corresponding to the given user ID.
*
* @param user_id the user ID
* @param credentials credentials to fill in
* @returns #TRUE if the username existed and we got some credentials
*/
dbus_bool_t
_dbus_credentials_from_user_id (unsigned long user_id,
DBusCredentials *credentials)
{
return get_user_info (NULL, user_id, credentials, NULL, NULL, NULL);
}
_DBUS_DEFINE_GLOBAL_LOCK (user_info);
typedef struct
{
DBusString name;
DBusString dir;
DBusCredentials creds;
} UserInfo;
static void
shutdown_user_info (void *data)
{
UserInfo *u = data;
_dbus_string_free (&u->name);
_dbus_string_free (&u->dir);
}
/**
* Gets information about the user running this process.
*
* @param username return location for username or #NULL
* @param homedir return location for home directory or #NULL
* @param credentials return location for credentials or #NULL
* @returns #TRUE on success
*/
dbus_bool_t
_dbus_user_info_from_current_process (const DBusString **username,
const DBusString **homedir,
const DBusCredentials **credentials)
{
static UserInfo u;
static int initialized_generation = 0;
if (!_DBUS_LOCK (user_info))
return FALSE;
if (initialized_generation != _dbus_current_generation)
{
if (!_dbus_string_init (&u.name))
{
_DBUS_UNLOCK (user_info);
return FALSE;
}
if (!_dbus_string_init (&u.dir))
{
_dbus_string_free (&u.name);
_DBUS_UNLOCK (user_info);
return FALSE;
}
_dbus_credentials_clear (&u.creds);
if (!get_user_info (NULL, getuid (),
&u.creds, &u.dir, &u.name, NULL))
goto fail_init;
if (!_dbus_register_shutdown_func (shutdown_user_info,
&u))
goto fail_init;
initialized_generation = _dbus_current_generation;
fail_init:
if (initialized_generation != _dbus_current_generation)
{
_dbus_string_free (&u.name);
_dbus_string_free (&u.dir);
_DBUS_UNLOCK (user_info);
return FALSE;
}
}
if (username)
*username = &u.name;
if (homedir)
*homedir = &u.dir;
if (credentials)
*credentials = &u.creds;
_DBUS_UNLOCK (user_info);
return TRUE;
}
/**
* Gets the home directory for the given user.
*
* @param username the username
* @param homedir string to append home directory to
* @returns #TRUE if user existed and we appended their homedir
*/
dbus_bool_t
_dbus_homedir_from_username (const DBusString *username,
DBusString *homedir)
{
return get_user_info (username, -1, NULL, homedir, NULL, NULL);
}
/**
* Gets credentials from a UID string. (Parses a string to a UID
* and converts to a DBusCredentials.)
*
* @param uid_str the UID in string form
* @param credentials credentials to fill in
* @returns #TRUE if successfully filled in some credentials
*/
dbus_bool_t
_dbus_credentials_from_uid_string (const DBusString *uid_str,
DBusCredentials *credentials)
{
int end;
long uid;
_dbus_credentials_clear (credentials);
if (_dbus_string_get_length (uid_str) == 0)
{
_dbus_verbose ("UID string was zero length\n");
return FALSE;
}
uid = -1;
end = 0;
if (!_dbus_string_parse_int (uid_str, 0, &uid,
&end))
{
_dbus_verbose ("could not parse string as a UID\n");
return FALSE;
}
if (end != _dbus_string_get_length (uid_str))
{
_dbus_verbose ("string contained trailing stuff after UID\n");
return FALSE;
}
credentials->uid = uid;
return TRUE;
}
/**
* Gets the credentials of the current process.
*
@ -1711,202 +1730,6 @@ _dbus_credentials_match (const DBusCredentials *expected_credentials,
return FALSE;
}
/**
* Gets group ID from group name.
*
* @param group_name name of the group
* @param gid location to store group ID
* @returns #TRUE if group was known
*/
dbus_bool_t
_dbus_get_group_id (const DBusString *group_name,
unsigned long *gid)
{
const char *group_c_str;
group_c_str = _dbus_string_get_const_data (group_name);
/* For now assuming that the getgrnam() and getgrgid() flavors
* always correspond to the pwnam flavors, if not we have
* to add more configure checks.
*/
#if defined (HAVE_POSIX_GETPWNAME_R) || defined (HAVE_NONPOSIX_GETPWNAME_R)
{
struct group *g;
int result;
char buf[1024];
struct group g_str;
g = NULL;
#ifdef HAVE_POSIX_GETPWNAME_R
result = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf),
&g);
#else
p = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf));
result = 0;
#endif /* !HAVE_POSIX_GETPWNAME_R */
if (result == 0 && g == &g_str)
{
*gid = g->gr_gid;
return TRUE;
}
else
{
_dbus_verbose ("Group %s unknown\n", group_c_str);
return FALSE;
}
}
#else /* ! HAVE_GETPWNAM_R */
{
/* I guess we're screwed on thread safety here */
struct group *g;
g = getgrnam (group_c_str);
if (g != NULL)
{
*gid = g->gr_gid;
return TRUE;
}
else
{
_dbus_verbose ("Group %s unknown\n", group_c_str);
return FALSE;
}
}
#endif /* ! HAVE_GETPWNAM_R */
}
/**
* Gets all groups for a particular user. Returns #FALSE
* if no memory, or user isn't known, but always initializes
* group_ids to a NULL array. Sets error to the reason
* for returning #FALSE.
*
* @param uid the user ID
* @param group_ids return location for array of group IDs
* @param n_group_ids return location for length of returned array
* @param error return location for error
* @returns #TRUE on success
*/
dbus_bool_t
_dbus_get_groups (unsigned long uid,
unsigned long **group_ids,
int *n_group_ids,
DBusError *error)
{
DBusCredentials creds;
DBusString username;
const char *username_c;
dbus_bool_t retval;
*group_ids = NULL;
*n_group_ids = 0;
retval = FALSE;
if (!_dbus_string_init (&username))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return FALSE;
}
if (!get_user_info (NULL, uid, &creds,
NULL, &username, error) ||
creds.gid == DBUS_GID_UNSET)
goto out;
username_c = _dbus_string_get_const_data (&username);
#ifdef HAVE_GETGROUPLIST
{
gid_t *buf;
int buf_count;
int i;
buf_count = 17;
buf = dbus_new (gid_t, buf_count);
if (buf == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto out;
}
if (getgrouplist (username_c,
creds.gid,
buf, &buf_count) < 0)
{
gid_t *new = dbus_realloc (buf, buf_count * sizeof (buf[0]));
if (new == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
dbus_free (buf);
goto out;
}
buf = new;
getgrouplist (username_c, creds.gid, buf, &buf_count);
}
*group_ids = dbus_new (unsigned long, buf_count);
if (*group_ids == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
dbus_free (buf);
goto out;
}
for (i = 0; i < buf_count; ++i)
(*group_ids)[i] = buf[i];
*n_group_ids = buf_count;
dbus_free (buf);
}
#else /* HAVE_GETGROUPLIST */
{
/* We just get the one group ID */
*group_ids = dbus_new (unsigned long, 1);
if (*group_ids == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto out;
}
*n_group_ids = 1;
(*group_ids)[0] = creds.gid;
}
#endif /* HAVE_GETGROUPLIST */
retval = TRUE;
out:
_dbus_string_free (&username);
if (retval)
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
else
_DBUS_ASSERT_ERROR_IS_SET (error);
return retval;
}
/**
* Appends the uid of the current process to the given string.
*
* @param str the string to append to
* @returns #TRUE on success
*/
dbus_bool_t
_dbus_string_append_our_uid (DBusString *str)
{
return _dbus_string_append_uint (str, getuid ());
}
/**
* Gets our process ID
* @returns process ID
@ -1917,6 +1740,25 @@ _dbus_getpid (void)
return getpid ();
}
/** Gets our UID
* @returns process UID
*/
dbus_uid_t
_dbus_getuid (void)
{
return getuid ();
}
/** Gets our GID
* @returns process GID
*/
dbus_gid_t
_dbus_getgid (void)
{
return getgid ();
}
_DBUS_DEFINE_GLOBAL_LOCK (atomic);
/**

View file

@ -124,34 +124,50 @@ dbus_bool_t _dbus_send_credentials_unix_socket (int server_fd,
void _dbus_credentials_clear (DBusCredentials *credentials);
dbus_bool_t _dbus_credentials_from_username (const DBusString *username,
DBusCredentials *credentials);
dbus_bool_t _dbus_credentials_from_user_id (dbus_uid_t user_id,
DBusCredentials *credentials);
dbus_bool_t _dbus_credentials_from_uid_string (const DBusString *uid_str,
DBusCredentials *credentials);
void _dbus_credentials_from_current_process (DBusCredentials *credentials);
dbus_bool_t _dbus_credentials_match (const DBusCredentials *expected_credentials,
const DBusCredentials *provided_credentials);
dbus_bool_t _dbus_get_user_id (const DBusString *username,
dbus_uid_t *uid);
dbus_bool_t _dbus_string_append_our_uid (DBusString *str);
dbus_bool_t _dbus_homedir_from_username (const DBusString *username,
DBusString *homedir);
dbus_bool_t _dbus_user_info_from_current_process (const DBusString **username,
const DBusString **homedir,
const DBusCredentials **credentials);
typedef struct DBusUserInfo DBusUserInfo;
typedef struct DBusGroupInfo DBusGroupInfo;
struct DBusUserInfo
{
dbus_uid_t uid; /**< UID */
dbus_gid_t primary_gid; /**< GID */
dbus_gid_t *group_ids; /**< Groups IDs, *including* above primary group */
int n_group_ids; /**< Size of group IDs array */
char *username; /**< Username */
char *homedir; /**< Home directory */
};
struct DBusGroupInfo
{
dbus_gid_t gid; /**< GID */
char *groupname; /**< Group name */
};
dbus_bool_t _dbus_user_info_fill (DBusUserInfo *info,
const DBusString *username,
DBusError *error);
dbus_bool_t _dbus_user_info_fill_uid (DBusUserInfo *info,
dbus_uid_t uid,
DBusError *error);
void _dbus_user_info_free (DBusUserInfo *info);
dbus_bool_t _dbus_group_info_fill (DBusGroupInfo *info,
const DBusString *groupname,
DBusError *error);
dbus_bool_t _dbus_group_info_fill_gid (DBusGroupInfo *info,
dbus_gid_t gid,
DBusError *error);
void _dbus_group_info_free (DBusGroupInfo *info);
dbus_bool_t _dbus_get_group_id (const DBusString *group_name,
dbus_gid_t *gid);
dbus_bool_t _dbus_get_groups (dbus_uid_t uid,
dbus_gid_t **group_ids,
int *n_group_ids,
DBusError *error);
unsigned long _dbus_getpid (void);
dbus_uid_t _dbus_getuid (void);
dbus_gid_t _dbus_getgid (void);
typedef int dbus_atomic_t;

View file

@ -226,9 +226,9 @@ init_global_locks (void)
LOCK_ADDR (server_slots),
LOCK_ADDR (atomic),
LOCK_ADDR (message_handler),
LOCK_ADDR (user_info),
LOCK_ADDR (bus),
LOCK_ADDR (shutdown_funcs)
LOCK_ADDR (shutdown_funcs),
LOCK_ADDR (system_users)
#undef LOCK_ADDR
};

View file

@ -26,80 +26,546 @@
#include "dbus-internals.h"
#include <string.h>
typedef struct DBusUserEntry DBusUserEntry;
struct DBusUserEntry
{
dbus_uid_t uid;
dbus_gid_t *group_ids;
int n_group_ids;
};
struct DBusUserDatabase
{
int refcount;
DBusHashTable *users;
DBusHashTable *groups;
DBusHashTable *users_by_name;
DBusHashTable *groups_by_name;
};
static void
free_user_entry (void *data)
free_user_info (void *data)
{
DBusUserEntry *entry = data;
DBusUserInfo *info = data;
if (entry == NULL) /* hash table will pass NULL */
if (info == NULL) /* hash table will pass NULL */
return;
dbus_free (entry->group_ids);
dbus_free (entry);
_dbus_user_info_free (info);
dbus_free (info);
}
static DBusUserEntry*
static void
free_group_info (void *data)
{
DBusGroupInfo *info = data;
if (info == NULL) /* hash table will pass NULL */
return;
_dbus_group_info_free (info);
dbus_free (info);
}
static DBusUserInfo*
_dbus_user_database_lookup (DBusUserDatabase *db,
dbus_uid_t uid,
const DBusString *username,
DBusError *error)
{
DBusUserEntry *entry;
DBusUserInfo *info;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
if (uid != DBUS_UID_UNSET)
info = _dbus_hash_table_lookup_ulong (db->users, uid);
else
info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
entry = _dbus_hash_table_lookup_ulong (db->users, uid);
if (entry)
return entry;
if (info)
{
_dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
uid);
return info;
}
else
{
entry = dbus_new0 (DBusUserEntry, 1);
if (entry == NULL)
_dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
uid);
info = dbus_new0 (DBusUserInfo, 1);
if (info == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return NULL;
}
if (!_dbus_get_groups (uid, &entry->group_ids, &entry->n_group_ids, error))
if (!_dbus_user_info_fill_uid (info, uid, error))
{
_DBUS_ASSERT_ERROR_IS_SET (error);
free_user_entry (entry);
free_user_info (info);
return NULL;
}
if (!_dbus_hash_table_insert_ulong (db->users, entry->uid, entry))
if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
free_user_entry (entry);
free_user_info (info);
return NULL;
}
return entry;
if (!_dbus_hash_table_insert_string (db->users_by_name,
info->username,
info))
{
_dbus_hash_table_remove_ulong (db->users, info->uid);
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return NULL;
}
return info;
}
}
static DBusGroupInfo*
_dbus_user_database_lookup_group (DBusUserDatabase *db,
dbus_gid_t gid,
const DBusString *groupname,
DBusError *error)
{
DBusGroupInfo *info;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
if (gid != DBUS_GID_UNSET)
info = _dbus_hash_table_lookup_ulong (db->groups, gid);
else
info = _dbus_hash_table_lookup_string (db->groups_by_name,
_dbus_string_get_const_data (groupname));
if (info)
{
_dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
gid);
return info;
}
else
{
_dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
gid);
info = dbus_new0 (DBusGroupInfo, 1);
if (info == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return NULL;
}
if (!_dbus_group_info_fill_gid (info, gid, error))
{
_DBUS_ASSERT_ERROR_IS_SET (error);
free_group_info (info);
return NULL;
}
if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
free_group_info (info);
return NULL;
}
if (!_dbus_hash_table_insert_string (db->groups_by_name,
info->groupname,
info))
{
_dbus_hash_table_remove_ulong (db->groups, info->gid);
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return NULL;
}
return info;
}
}
_DBUS_DEFINE_GLOBAL_LOCK(system_users);
static dbus_bool_t database_locked = FALSE;
static DBusUserDatabase *system_db = NULL;
static DBusString process_username;
static DBusString process_homedir;
static void
shutdown_system_db (void *data)
{
_dbus_user_database_unref (system_db);
system_db = NULL;
_dbus_string_free (&process_username);
_dbus_string_free (&process_homedir);
}
static dbus_bool_t
init_system_db (void)
{
_dbus_assert (database_locked);
if (system_db == NULL)
{
DBusError error;
const DBusUserInfo *info;
system_db = _dbus_user_database_new ();
if (system_db == NULL)
return FALSE;
dbus_error_init (&error);
if (!_dbus_user_database_get_uid (system_db,
_dbus_getuid (),
&info,
&error))
{
_dbus_user_database_unref (system_db);
system_db = NULL;
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
{
dbus_error_free (&error);
return FALSE;
}
else
{
/* This really should not happen. */
_dbus_warn ("Could not get password database information for UID of current process: %s\n",
error.message);
dbus_error_free (&error);
return FALSE;
}
}
if (!_dbus_string_init (&process_username))
{
_dbus_user_database_unref (system_db);
system_db = NULL;
return FALSE;
}
if (!_dbus_string_init (&process_homedir))
{
_dbus_string_free (&process_username);
_dbus_user_database_unref (system_db);
system_db = NULL;
return FALSE;
}
if (!_dbus_string_append (&process_username,
info->username) ||
!_dbus_string_append (&process_homedir,
info->homedir) ||
!_dbus_register_shutdown_func (shutdown_system_db, NULL))
{
_dbus_string_free (&process_username);
_dbus_string_free (&process_homedir);
_dbus_user_database_unref (system_db);
system_db = NULL;
return FALSE;
}
}
return TRUE;
}
/**
* @addtogroup DBusInternalsUtils
* @{
*/
/**
* Locks global system user database.
*/
void
_dbus_user_database_lock_system (void)
{
_DBUS_LOCK (system_users);
database_locked = TRUE;
}
/**
* Unlocks global system user database.
*/
void
_dbus_user_database_unlock_system (void)
{
database_locked = FALSE;
_DBUS_UNLOCK (system_users);
}
/**
* Gets the system global user database;
* must be called with lock held (_dbus_user_database_lock_system()).
*
* @returns the database or #NULL if no memory
*/
DBusUserDatabase*
_dbus_user_database_get_system (void)
{
_dbus_assert (database_locked);
init_system_db ();
return system_db;
}
/**
* Gets username of user owning current process. The returned string
* is valid until dbus_shutdown() is called.
*
* @param username place to store pointer to username
* @returns #FALSE if no memory
*/
dbus_bool_t
_dbus_username_from_current_process (const DBusString **username)
{
_dbus_user_database_lock_system ();
if (!init_system_db ())
{
_dbus_user_database_unlock_system ();
return FALSE;
}
*username = &process_username;
_dbus_user_database_unlock_system ();
return TRUE;
}
/**
* Gets homedir of user owning current process. The returned string
* is valid until dbus_shutdown() is called.
*
* @param homedir place to store pointer to homedir
* @returns #FALSE if no memory
*/
dbus_bool_t
_dbus_homedir_from_current_process (const DBusString **homedir)
{
_dbus_user_database_lock_system ();
if (!init_system_db ())
{
_dbus_user_database_unlock_system ();
return FALSE;
}
*homedir = &process_homedir;
_dbus_user_database_unlock_system ();
return TRUE;
}
/**
* Gets user ID given username
*
* @param username the username
* @param uid return location for UID
* @returns #TRUE if username existed and we got the UID
*/
dbus_bool_t
_dbus_get_user_id (const DBusString *username,
dbus_uid_t *uid)
{
DBusCredentials creds;
if (!_dbus_credentials_from_username (username, &creds))
return FALSE;
if (creds.uid == DBUS_UID_UNSET)
return FALSE;
*uid = creds.uid;
return TRUE;
}
/**
* Gets group ID given groupname
*
* @param groupname the groupname
* @param gid return location for GID
* @returns #TRUE if group name existed and we got the GID
*/
dbus_bool_t
_dbus_get_group_id (const DBusString *groupname,
dbus_gid_t *gid)
{
DBusUserDatabase *db;
const DBusGroupInfo *info;
_dbus_user_database_lock_system ();
db = _dbus_user_database_get_system ();
if (db == NULL)
{
_dbus_user_database_unlock_system ();
return FALSE;
}
if (!_dbus_user_database_get_groupname (db, groupname,
&info, NULL))
{
_dbus_user_database_unlock_system ();
return FALSE;
}
*gid = info->gid;
_dbus_user_database_unlock_system ();
return TRUE;
}
/**
* Gets the home directory for the given user.
*
* @param username the username
* @param homedir string to append home directory to
* @returns #TRUE if user existed and we appended their homedir
*/
dbus_bool_t
_dbus_homedir_from_username (const DBusString *username,
DBusString *homedir)
{
DBusUserDatabase *db;
const DBusUserInfo *info;
_dbus_user_database_lock_system ();
db = _dbus_user_database_get_system ();
if (db == NULL)
{
_dbus_user_database_unlock_system ();
return FALSE;
}
if (!_dbus_user_database_get_username (db, username,
&info, NULL))
{
_dbus_user_database_unlock_system ();
return FALSE;
}
if (!_dbus_string_append (homedir, info->homedir))
{
_dbus_user_database_unlock_system ();
return FALSE;
}
_dbus_user_database_unlock_system ();
return TRUE;
}
/**
* Gets a UID from a UID string.
*
* @param uid_str the UID in string form
* @param uid UID to fill in
* @returns #TRUE if successfully filled in UID
*/
dbus_bool_t
_dbus_uid_from_string (const DBusString *uid_str,
dbus_uid_t *uid)
{
int end;
long val;
if (_dbus_string_get_length (uid_str) == 0)
{
_dbus_verbose ("UID string was zero length\n");
return FALSE;
}
val = -1;
end = 0;
if (!_dbus_string_parse_int (uid_str, 0, &val,
&end))
{
_dbus_verbose ("could not parse string as a UID\n");
return FALSE;
}
if (end != _dbus_string_get_length (uid_str))
{
_dbus_verbose ("string contained trailing stuff after UID\n");
return FALSE;
}
*uid = val;
return TRUE;
}
/**
* Gets the credentials corresponding to the given username.
*
* @param username the username
* @param credentials credentials to fill in
* @returns #TRUE if the username existed and we got some credentials
*/
dbus_bool_t
_dbus_credentials_from_username (const DBusString *username,
DBusCredentials *credentials)
{
DBusUserDatabase *db;
const DBusUserInfo *info;
_dbus_user_database_lock_system ();
db = _dbus_user_database_get_system ();
if (db == NULL)
{
_dbus_user_database_unlock_system ();
return FALSE;
}
if (!_dbus_user_database_get_username (db, username,
&info, NULL))
{
_dbus_user_database_unlock_system ();
return FALSE;
}
credentials->pid = DBUS_PID_UNSET;
credentials->uid = info->uid;
credentials->gid = info->primary_gid;
_dbus_user_database_unlock_system ();
return TRUE;
}
/**
* Gets the credentials corresponding to the given UID.
*
* @param uid the UID
* @param credentials credentials to fill in
* @returns #TRUE if the UID existed and we got some credentials
*/
dbus_bool_t
_dbus_credentials_from_uid (dbus_uid_t uid,
DBusCredentials *credentials)
{
DBusUserDatabase *db;
const DBusUserInfo *info;
_dbus_user_database_lock_system ();
db = _dbus_user_database_get_system ();
if (db == NULL)
{
_dbus_user_database_unlock_system ();
return FALSE;
}
if (!_dbus_user_database_get_uid (db, uid,
&info, NULL))
{
_dbus_user_database_unlock_system ();
return FALSE;
}
_dbus_assert (info->uid == uid);
credentials->pid = DBUS_PID_UNSET;
credentials->uid = info->uid;
credentials->gid = info->primary_gid;
_dbus_user_database_unlock_system ();
return TRUE;
}
/**
* Creates a new user database object used to look up and
* cache user information.
@ -117,10 +583,26 @@ _dbus_user_database_new (void)
db->refcount = 1;
db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
NULL, free_user_entry);
NULL, free_user_info);
if (db->users == NULL)
goto failed;
db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
NULL, free_group_info);
if (db->groups == NULL)
goto failed;
db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
NULL, NULL);
if (db->users_by_name == NULL)
goto failed;
db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
NULL, NULL);
if (db->groups_by_name == NULL)
goto failed;
return db;
@ -155,6 +637,15 @@ _dbus_user_database_unref (DBusUserDatabase *db)
{
if (db->users)
_dbus_hash_table_unref (db->users);
if (db->groups)
_dbus_hash_table_unref (db->groups);
if (db->users_by_name)
_dbus_hash_table_unref (db->users_by_name);
if (db->groups_by_name)
_dbus_hash_table_unref (db->groups_by_name);
dbus_free (db);
}
@ -180,37 +671,116 @@ _dbus_user_database_get_groups (DBusUserDatabase *db,
int *n_group_ids,
DBusError *error)
{
DBusUserEntry *entry;
DBusUserInfo *info;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
*group_ids = NULL;
*n_group_ids = 0;
entry = _dbus_user_database_lookup (db, uid, error);
if (entry == NULL)
info = _dbus_user_database_lookup (db, uid, NULL, error);
if (info == NULL)
{
_DBUS_ASSERT_ERROR_IS_SET (error);
return FALSE;
}
if (entry->n_group_ids > 0)
if (info->n_group_ids > 0)
{
*group_ids = dbus_new (dbus_gid_t, entry->n_group_ids);
*group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
if (*group_ids == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return FALSE;
}
*n_group_ids = entry->n_group_ids;
*n_group_ids = info->n_group_ids;
memcpy (*group_ids, entry->group_ids, entry->n_group_ids * sizeof (dbus_gid_t));
memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
}
return TRUE;
}
/**
* Gets the user information for the given UID,
* returned user info should not be freed.
*
* @param db user database
* @param uid the user ID
* @param info return location for const ref to user info
* @param error error location
* @returns #FALSE if error is set
*/
dbus_bool_t
_dbus_user_database_get_uid (DBusUserDatabase *db,
dbus_uid_t uid,
const DBusUserInfo **info,
DBusError *error)
{
*info = _dbus_user_database_lookup (db, uid, NULL, error);
return *info != NULL;
}
/**
* Gets the user information for the given GID,
* returned group info should not be freed.
*
* @param db user database
* @param gid the group ID
* @param info return location for const ref to group info
* @param error error location
* @returns #FALSE if error is set
*/
dbus_bool_t
_dbus_user_database_get_gid (DBusUserDatabase *db,
dbus_gid_t gid,
const DBusGroupInfo **info,
DBusError *error)
{
*info = _dbus_user_database_lookup_group (db, gid, NULL, error);
return *info != NULL;
}
/**
* Gets the user information for the given username.
*
* @param db user database
* @param username the user name
* @param info return location for const ref to user info
* @param error error location
* @returns #FALSE if error is set
*/
dbus_bool_t
_dbus_user_database_get_username (DBusUserDatabase *db,
const DBusString *username,
const DBusUserInfo **info,
DBusError *error)
{
*info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
return *info != NULL;
}
/**
* Gets the user information for the given group name,
* returned group info should not be freed.
*
* @param db user database
* @param groupname the group name
* @param info return location for const ref to group info
* @param error error location
* @returns #FALSE if error is set
*/
dbus_bool_t
_dbus_user_database_get_groupname (DBusUserDatabase *db,
const DBusString *groupname,
const DBusGroupInfo **info,
DBusError *error)
{
*info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
return *info != NULL;
}
/** @} */
#ifdef DBUS_BUILD_TESTS
@ -222,7 +792,19 @@ _dbus_user_database_get_groups (DBusUserDatabase *db,
dbus_bool_t
_dbus_userdb_test (const char *test_data_dir)
{
const DBusString *username;
const DBusString *homedir;
if (!_dbus_username_from_current_process (&username))
_dbus_assert_not_reached ("didn't get username");
if (!_dbus_homedir_from_current_process (&homedir))
_dbus_assert_not_reached ("didn't get homedir");
printf (" Current user: %s homedir: %s\n",
_dbus_string_get_const_data (username),
_dbus_string_get_const_data (homedir));
return TRUE;
}
#endif /* DBUS_BUILD_TESTS */

View file

@ -30,14 +30,52 @@ DBUS_BEGIN_DECLS;
typedef struct DBusUserDatabase DBusUserDatabase;
DBusUserDatabase* _dbus_user_database_new (void);
void _dbus_user_database_ref (DBusUserDatabase *db);
void _dbus_user_database_unref (DBusUserDatabase *db);
dbus_bool_t _dbus_user_database_get_groups (DBusUserDatabase *db,
dbus_uid_t uid,
dbus_gid_t **group_ids,
int *n_group_ids,
DBusError *error);
DBusUserDatabase* _dbus_user_database_new (void);
void _dbus_user_database_ref (DBusUserDatabase *db);
void _dbus_user_database_unref (DBusUserDatabase *db);
dbus_bool_t _dbus_user_database_get_groups (DBusUserDatabase *db,
dbus_uid_t uid,
dbus_gid_t **group_ids,
int *n_group_ids,
DBusError *error);
dbus_bool_t _dbus_user_database_get_uid (DBusUserDatabase *db,
dbus_uid_t uid,
const DBusUserInfo **info,
DBusError *error);
dbus_bool_t _dbus_user_database_get_gid (DBusUserDatabase *db,
dbus_gid_t gid,
const DBusGroupInfo **info,
DBusError *error);
dbus_bool_t _dbus_user_database_get_username (DBusUserDatabase *db,
const DBusString *username,
const DBusUserInfo **info,
DBusError *error);
dbus_bool_t _dbus_user_database_get_groupname (DBusUserDatabase *db,
const DBusString *groupname,
const DBusGroupInfo **info,
DBusError *error);
DBusUserDatabase* _dbus_user_database_get_system (void);
void _dbus_user_database_lock_system (void);
void _dbus_user_database_unlock_system (void);
dbus_bool_t _dbus_username_from_current_process (const DBusString **username);
dbus_bool_t _dbus_homedir_from_current_process (const DBusString **homedir);
dbus_bool_t _dbus_homedir_from_username (const DBusString *username,
DBusString *homedir);
dbus_bool_t _dbus_get_user_id (const DBusString *username,
dbus_uid_t *uid);
dbus_bool_t _dbus_get_group_id (const DBusString *group_name,
dbus_gid_t *gid);
dbus_bool_t _dbus_uid_from_string (const DBusString *uid_str,
dbus_uid_t *uid);
dbus_bool_t _dbus_credentials_from_username (const DBusString *username,
DBusCredentials *credentials);
dbus_bool_t _dbus_credentials_from_uid (dbus_uid_t user_id,
DBusCredentials *credentials);
DBUS_END_DECLS;