X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dbus%2Fdbus-userdb.c;h=73f8fcefed1969f250ac917cb964f82450df8d2a;hb=757b80b9711d9733798c927495d74c7323e95400;hp=871fa5bd40b1f18261e1eb6baa8c7fec8078ce9f;hpb=fe1cb3a9a1fc6bcbfa1b3be74ac9d5867005210f;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-userdb.c b/dbus/dbus-userdb.c index 871fa5b..73f8fce 100644 --- a/dbus/dbus-userdb.c +++ b/dbus/dbus-userdb.c @@ -1,9 +1,9 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dbus-userdb.c User database abstraction * - * Copyright (C) 2003 Red Hat, Inc. + * Copyright (C) 2003, 2004 Red Hat, Inc. * - * Licensed under the Academic Free License version 1.2 + * Licensed under the Academic Free License version 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,88 +17,533 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ +#include +#define DBUS_USERDB_INCLUDES_PRIVATE 1 #include "dbus-userdb.h" #include "dbus-hash.h" #include "dbus-test.h" #include "dbus-internals.h" +#include "dbus-protocol.h" +#include "dbus-credentials.h" #include -typedef struct DBusUserEntry DBusUserEntry; +/** + * @addtogroup DBusInternalsUtils + * @{ + */ -struct DBusUserEntry +/** + * Frees the given #DBusUserInfo's members with _dbus_user_info_free() + * and also calls dbus_free() on the block itself + * + * @param info the info + */ +void +_dbus_user_info_free_allocated (DBusUserInfo *info) { - dbus_uid_t uid; + if (info == NULL) /* hash table will pass NULL */ + return; - dbus_gid_t *group_ids; - int n_group_ids; -}; + _dbus_user_info_free (info); + dbus_free (info); +} -struct DBusUserDatabase +/** + * Frees the given #DBusGroupInfo's members with _dbus_group_info_free() + * and also calls dbus_free() on the block itself + * + * @param info the info + */ +void +_dbus_group_info_free_allocated (DBusGroupInfo *info) { - int refcount; + if (info == NULL) /* hash table will pass NULL */ + return; - DBusHashTable *users; -}; + _dbus_group_info_free (info); + dbus_free (info); +} -static void -free_user_entry (void *data) +/** + * Frees the members of info + * (but not info itself) + * @param info the user info struct + */ +void +_dbus_user_info_free (DBusUserInfo *info) { - DBusUserEntry *entry = data; + dbus_free (info->group_ids); + dbus_free (info->username); + dbus_free (info->homedir); +} - if (entry == NULL) /* hash table will pass NULL */ - return; +/** + * 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); +} - dbus_free (entry->group_ids); - - dbus_free (entry); +/** + * Checks if a given string is actually a number + * and converts it if it is + * + * @param str the string to check + * @param num the memory location of the unsigned long to fill in + * @returns TRUE if str is a number and num is filled in + */ +dbus_bool_t +_dbus_is_a_number (const DBusString *str, + unsigned long *num) +{ + int end; + + if (_dbus_string_parse_uint (str, 0, num, &end) && + end == _dbus_string_get_length (str)) + return TRUE; + else + return FALSE; } -static DBusUserEntry* +/** + * Looks up a uid or username in the user database. Only one of name + * or UID can be provided. There are wrapper functions for this that + * are better to use, this one does no locking or anything on the + * database and otherwise sort of sucks. + * + * @param db the database + * @param uid the user ID or #DBUS_UID_UNSET + * @param username username or #NULL + * @param error error to fill in + * @returns the entry in the database + */ +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); - - entry = _dbus_hash_table_lookup_ulong (db->users, uid); - if (entry) - return entry; + _dbus_assert (uid != DBUS_UID_UNSET || username != NULL); + + /* See if the username is really a number */ + if (uid == DBUS_UID_UNSET) + { + unsigned long n; + + if (_dbus_is_a_number (username, &n)) + uid = n; + } + +#ifdef DBUS_ENABLE_USERDB_CACHE + if (uid != DBUS_UID_UNSET) + info = _dbus_hash_table_lookup_uintptr (db->users, uid); + else + info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username)); + + if (info) + { + _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n", + info->uid); + return info; + } else +#else + if (1) +#endif { - entry = dbus_new0 (DBusUserEntry, 1); - if (entry == NULL) + if (uid != DBUS_UID_UNSET) + _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n", + uid); + else + _dbus_verbose ("No cache for user \"%s\"\n", + _dbus_string_get_const_data (username)); + + 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 (uid != DBUS_UID_UNSET) + { + if (!_dbus_user_info_fill_uid (info, uid, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_user_info_free_allocated (info); + return NULL; + } + } + else + { + if (!_dbus_user_info_fill (info, username, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + _dbus_user_info_free_allocated (info); + return NULL; + } + } + + /* be sure we don't use these after here */ + uid = DBUS_UID_UNSET; + username = NULL; + + /* insert into hash */ + if (!_dbus_hash_table_insert_uintptr (db->users, info->uid, info)) { - _DBUS_ASSERT_ERROR_IS_SET (error); - free_user_entry (entry); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_user_info_free_allocated (info); return NULL; } - if (!_dbus_hash_table_insert_ulong (db->users, entry->uid, entry)) + if (!_dbus_hash_table_insert_string (db->users_by_name, + info->username, + info)) { + _dbus_hash_table_remove_uintptr (db->users, info->uid); dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - free_user_entry (entry); return NULL; } + + return info; + } +} - return entry; +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) +{ + if (system_db != NULL) + _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 = DBUS_ERROR_INIT; + const DBusUserInfo *info; + + system_db = _dbus_user_database_new (); + if (system_db == NULL) + return FALSE; + + 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; +} + +/** + * Locks global system user database. + */ +dbus_bool_t +_dbus_user_database_lock_system (void) +{ + if (_DBUS_LOCK (system_users)) + { + database_locked = TRUE; + return TRUE; + } + else + { + return FALSE; } } /** - * @addtogroup DBusInternalsUtils - * @{ + * 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; +} + +/** + * Flushes the system global user database; + */ +void +_dbus_user_database_flush_system (void) +{ + if (!_dbus_user_database_lock_system ()) + { + /* nothing to flush */ + return; + } + + if (system_db != NULL) + _dbus_user_database_flush (system_db); + + _dbus_user_database_unlock_system (); +} + +/** + * 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) +{ + if (!_dbus_user_database_lock_system ()) + return FALSE; + + 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) +{ + if (!_dbus_user_database_lock_system ()) + return FALSE; + + if (!init_system_db ()) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + *homedir = &process_homedir; + _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; + + /* FIXME: this can't distinguish ENOMEM from other errors */ + if (!_dbus_user_database_lock_system ()) + return FALSE; + + 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 the home directory for the given user. + * + * @param uid the uid + * @param homedir string to append home directory to + * @returns #TRUE if user existed and we appended their homedir + */ +dbus_bool_t +_dbus_homedir_from_uid (dbus_uid_t uid, + DBusString *homedir) +{ + DBusUserDatabase *db; + const DBusUserInfo *info; + + /* FIXME: this can't distinguish ENOMEM from other errors */ + if (!_dbus_user_database_lock_system ()) + return FALSE; + + 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; + } + + if (!_dbus_string_append (homedir, info->homedir)) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + _dbus_user_database_unlock_system (); + return TRUE; +} + +/** + * Adds the credentials corresponding to the given username. + * + * Used among other purposes to parses a desired identity provided + * from a client in the auth protocol. On UNIX this means parsing a + * UID, on Windows probably parsing an SID string. + * + * @todo this is broken because it treats OOM and parse error + * the same way. Needs a #DBusError. + * + * @param credentials credentials to fill in + * @param username the username + * @returns #TRUE if the username existed and we got some credentials + */ +dbus_bool_t +_dbus_credentials_add_from_user (DBusCredentials *credentials, + const DBusString *username) +{ + DBusUserDatabase *db; + const DBusUserInfo *info; + + /* FIXME: this can't distinguish ENOMEM from other errors */ + if (!_dbus_user_database_lock_system ()) + return FALSE; + + 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_credentials_add_unix_uid(credentials, info->uid)) + { + _dbus_user_database_unlock_system (); + return FALSE; + } + + _dbus_user_database_unlock_system (); + return TRUE; +} /** * Creates a new user database object used to look up and @@ -116,11 +561,27 @@ _dbus_user_database_new (void) db->refcount = 1; - db->users = _dbus_hash_table_new (DBUS_HASH_ULONG, - NULL, free_user_entry); - + db->users = _dbus_hash_table_new (DBUS_HASH_UINTPTR, + NULL, (DBusFreeFunction) _dbus_user_info_free_allocated); + if (db->users == NULL) goto failed; + + db->groups = _dbus_hash_table_new (DBUS_HASH_UINTPTR, + NULL, (DBusFreeFunction) _dbus_group_info_free_allocated); + + 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; @@ -130,16 +591,33 @@ _dbus_user_database_new (void) } /** + * Flush all information out of the user database. + */ +void +_dbus_user_database_flush (DBusUserDatabase *db) +{ + _dbus_hash_table_remove_all(db->users_by_name); + _dbus_hash_table_remove_all(db->groups_by_name); + _dbus_hash_table_remove_all(db->users); + _dbus_hash_table_remove_all(db->groups); +} + +#ifdef DBUS_BUILD_TESTS +/** * Increments refcount of user database. * @param db the database + * @returns the database */ -void +DBusUserDatabase * _dbus_user_database_ref (DBusUserDatabase *db) { _dbus_assert (db->refcount > 0); db->refcount += 1; + + return db; } +#endif /* DBUS_BUILD_TESTS */ /** * Decrements refcount of user database. @@ -155,74 +633,59 @@ _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); } } /** - * 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. + * Gets the user information for the given UID, + * returned user info should not be freed. * - * @param db the user database object + * @param db user database * @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 + * @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_groups (DBusUserDatabase *db, - dbus_uid_t uid, - dbus_gid_t **group_ids, - int *n_group_ids, - DBusError *error) +_dbus_user_database_get_uid (DBusUserDatabase *db, + dbus_uid_t uid, + const DBusUserInfo **info, + DBusError *error) { - DBusUserEntry *entry; - - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - *group_ids = NULL; - *n_group_ids = 0; - - entry = _dbus_user_database_lookup (db, uid, error); - if (entry == NULL) - { - _DBUS_ASSERT_ERROR_IS_SET (error); - return FALSE; - } - - if (entry->n_group_ids > 0) - { - *group_ids = dbus_new (dbus_gid_t, entry->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; - - memcpy (*group_ids, entry->group_ids, entry->n_group_ids * sizeof (dbus_gid_t)); - } - - return TRUE; + *info = _dbus_user_database_lookup (db, uid, NULL, error); + return *info != NULL; } -/** @} */ - -#ifdef DBUS_BUILD_TESTS /** - * Unit test for dbus-userdb.c. - * - * @returns #TRUE on success. + * 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_userdb_test (const char *test_data_dir) +_dbus_user_database_get_username (DBusUserDatabase *db, + const DBusString *username, + const DBusUserInfo **info, + DBusError *error) { - - return TRUE; + *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error); + return *info != NULL; } -#endif /* DBUS_BUILD_TESTS */ + +/** @} */ + +/* Tests in dbus-userdb-util.c */