From 88cd5da3c0ec86fed29942b062c2f7bf0f8fda44 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Thu, 17 Apr 2003 23:17:04 +0000 Subject: 2003-04-17 Havoc Pennington * dbus/dbus-userdb.c, dbus/dbus-sysdeps.c: redo all the passwd database usage so it all goes via the DBusUserDatabase cache. --- dbus/dbus-userdb.c | 656 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 619 insertions(+), 37 deletions(-) (limited to 'dbus/dbus-userdb.c') diff --git a/dbus/dbus-userdb.c b/dbus/dbus-userdb.c index 871fa5bd..ad691c57 100644 --- a/dbus/dbus-userdb.c +++ b/dbus/dbus-userdb.c @@ -26,80 +26,546 @@ #include "dbus-internals.h" #include -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 + { + _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_user_info_fill_uid (info, uid, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + free_user_info (info); + return NULL; + } + + if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + free_user_info (info); + return NULL; + } + + 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) { - entry = dbus_new0 (DBusUserEntry, 1); - if (entry == NULL) + _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_get_groups (uid, &entry->group_ids, &entry->n_group_ids, error)) + if (!_dbus_group_info_fill_gid (info, gid, error)) { _DBUS_ASSERT_ERROR_IS_SET (error); - free_user_entry (entry); + free_group_info (info); return NULL; } - if (!_dbus_hash_table_insert_ulong (db->users, entry->uid, entry)) + if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - free_user_entry (entry); + free_group_info (info); return NULL; } - return entry; + + 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 */ -- cgit