From 6b40feaff4114ab3498ad06e13063fceff4d48e9 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Thu, 20 Feb 2003 03:43:18 +0000 Subject: [PATCH] 2003-02-19 Havoc Pennington * Doxyfile.in (PREDEFINED): put DOXYGEN_SHOULD_SKIP_THIS in Doxyfile.in, not Doxyfile * dbus/dbus-keyring.c: do some hacking on this * dbus/dbus-sysdeps.c (_dbus_delete_file): new * dbus/dbus-errors.c (dbus_set_error_const): do not call dbus_error_init (dbus_set_error): remove dbus_error_init, check for message == NULL *before* we sprintf into it, and add @todo about including system headers in this file * dbus/dbus-sysdeps.c (_dbus_create_file_exclusively): new * dbus/dbus-errors.h (DBUS_ERROR_FAILED): add * dbus/dbus-sysdeps.c (get_user_info): break this function out to get various bits of user information based on either username or user ID (_dbus_homedir_from_username): new function --- ChangeLog | 24 +++ Doxyfile.in | 2 +- dbus/dbus-errors.c | 21 +-- dbus/dbus-errors.h | 1 + dbus/dbus-internals.h | 2 + dbus/dbus-keyring.c | 404 +++++++++++++++++++++++++++++++++++++++++++++++++- dbus/dbus-keyring.h | 30 ++-- dbus/dbus-sysdeps.c | 282 +++++++++++++++++++++++++++++++---- dbus/dbus-sysdeps.h | 10 ++ dbus/dbus-threads.c | 1 + 10 files changed, 717 insertions(+), 60 deletions(-) diff --git a/ChangeLog b/ChangeLog index 023b43d..4404bdf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +2003-02-19 Havoc Pennington + + * Doxyfile.in (PREDEFINED): put DOXYGEN_SHOULD_SKIP_THIS in + Doxyfile.in, not Doxyfile + + * dbus/dbus-keyring.c: do some hacking on this + + * dbus/dbus-sysdeps.c (_dbus_delete_file): new + + * dbus/dbus-errors.c (dbus_set_error_const): do not call + dbus_error_init + (dbus_set_error): remove dbus_error_init, check for message == + NULL *before* we sprintf into it, and add @todo about including + system headers in this file + + * dbus/dbus-sysdeps.c (_dbus_create_file_exclusively): new + + * dbus/dbus-errors.h (DBUS_ERROR_FAILED): add + + * dbus/dbus-sysdeps.c (get_user_info): break this function out to + get various bits of user information based on either username + or user ID + (_dbus_homedir_from_username): new function + 2003-02-19 Anders Carlsson * configure.in: diff --git a/Doxyfile.in b/Doxyfile.in index 877b4ed..f01db5d 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -143,7 +143,7 @@ EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = -PREDEFINED = "DBUS_BEGIN_DECLS=" "DBUS_END_DECLS=" +PREDEFINED = "DBUS_BEGIN_DECLS=" "DBUS_END_DECLS=" "DOXYGEN_SHOULD_SKIP_THIS" EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- diff --git a/dbus/dbus-errors.c b/dbus/dbus-errors.c index 92cca03..a469f7e 100644 --- a/dbus/dbus-errors.c +++ b/dbus/dbus-errors.c @@ -183,10 +183,6 @@ dbus_error_free (DBusError *error) * Assigns an error name and message to a DBusError. * Does nothing if error is #NULL. * - * @todo calling dbus_error_init() in here is no good, - * for the same reason a GError* has to be set to NULL - * before you pass it in. - * * @param error the error. * @param name the error name (not copied!!!) * @param message the error message (not copied!!!) @@ -200,8 +196,6 @@ dbus_set_error_const (DBusError *error, if (error == NULL) return; - - dbus_error_init (error); real = (DBusRealError *)error; @@ -216,6 +210,9 @@ dbus_set_error_const (DBusError *error, * * If no memory can be allocated for the error message, * an out-of-memory error message will be set instead. + * + * @todo stdio.h shouldn't be included in this file, + * should write _dbus_string_append_printf instead * * @param error the error. * @param name the error name (not copied!!!) @@ -237,16 +234,11 @@ dbus_set_error (DBusError *error, return; va_start (args, format); - /* Measure the message length */ - message_length = vsnprintf (&c, 1,format, args) + 1; - - message = dbus_malloc (message_length); - + message_length = vsnprintf (&c, 1, format, args) + 1; va_end (args); - va_start (args, format); - vsprintf (message, format, args2); + message = dbus_malloc (message_length); if (!message) { @@ -255,9 +247,10 @@ dbus_set_error (DBusError *error, return; } + va_start (args, format); + vsprintf (message, format, args2); va_end (args); - dbus_error_init (error); real = (DBusRealError *)error; real->name = name; diff --git a/dbus/dbus-errors.h b/dbus/dbus-errors.h index 6e83dae..1b1a880 100644 --- a/dbus/dbus-errors.h +++ b/dbus/dbus-errors.h @@ -49,6 +49,7 @@ struct DBusError void *padding1; /**< placeholder */ }; +#define DBUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed" #define DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND "org.freedesktop.DBus.Activate.ServiceNotFound" #define DBUS_ERROR_SPAWN_FORK_FAILED "org.freedesktop.DBus.Error.Spawn.ForkFailed" #define DBUS_ERROR_SPAWN_FAILED "org.freedesktop.DBus.Error.Spawn.Failed" diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index 9f9cba8..551a377 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -159,6 +159,8 @@ DBusMutex *_dbus_list_init_lock (void); DBusMutex *_dbus_allocated_slots_init_lock (void); DBusMutex *_dbus_atomic_init_lock (void); DBusMutex *_dbus_message_handler_init_lock (void); +DBusMutex *_dbus_user_info_init_lock (void); + DBUS_END_DECLS; diff --git a/dbus/dbus-keyring.c b/dbus/dbus-keyring.c index da66da9..0f1dd1c 100644 --- a/dbus/dbus-keyring.c +++ b/dbus/dbus-keyring.c @@ -53,6 +53,13 @@ * @{ */ +/** The maximum time a key can be alive before we switch to a + * new one. This isn't super-reliably enforced, since + * system clocks can change or be wrong, but we make + * a best effort to only use keys for a short time. + */ +#define MAX_KEY_LIFETIME_SECONDS (60*5) + typedef struct { dbus_int32_t id; /**< identifier used to refer to the key */ @@ -60,10 +67,6 @@ typedef struct unsigned long creation_time; /**< when the key was generated, * as unix timestamp */ - - DBusString context; /**< Name of kind of server using this - * key, for example "login_session_bus" - */ DBusString secret; /**< the actual key */ @@ -77,11 +80,153 @@ typedef struct */ struct DBusKeyring { - DBusString filename; /**< File containing keyring */ - DBusString lock_filename; /**< Lock file for changing keyring */ + int refcount; /**< Reference count */ + DBusString directory; /**< Directory the below two items are inside */ + DBusString filename; /**< Keyring filename */ + DBusString filename_lock; /**< Name of lockfile */ + DBusKey *keys; /**< Keys loaded from the file */ + int n_keys; /**< Number of keys */ +}; + +static DBusKeyring* +_dbus_keyring_new (void) +{ + DBusKeyring *keyring; + + keyring = dbus_new0 (DBusKeyring, 1); + if (keyring == NULL) + goto out_0; + if (!_dbus_string_init (&keyring->directory)) + goto out_1; + + if (!_dbus_string_init (&keyring->filename)) + goto out_2; + + if (!_dbus_string_init (&keyring->filename_lock)) + goto out_3; + + keyring->refcount = 1; + keyring->keys = NULL; + keyring->n_keys = 0; + + return keyring; -}; + out_3: + _dbus_string_free (&keyring->filename); + out_2: + _dbus_string_free (&keyring->directory); + out_1: + dbus_free (keyring); + out_0: + return NULL; +} + +static void +free_keys (DBusKey *keys, + int n_keys) +{ + int i; + + /* should be safe for args NULL, 0 */ + + i = 0; + while (i < n_keys) + { + _dbus_string_free (&keys[i].secret); + ++i; + } + + dbus_free (keys); +} + +/* Our locking scheme is highly unreliable. However, there is + * unfortunately no reliable locking scheme in user home directories; + * between bugs in Linux NFS, people using Tru64 or other total crap + * NFS, AFS, random-file-system-of-the-week, and so forth, fcntl() in + * homedirs simply generates tons of bug reports. This has been + * learned through hard experience with GConf, unfortunately. + * + * This bad hack might work better for the kind of lock we have here, + * which we don't expect to hold for any length of time. Crashing + * while we hold it should be unlikely, and timing out such that we + * delete a stale lock should also be unlikely except when the + * filesystem is running really slowly. Stuff might break in corner + * cases but as long as it's not a security-level breakage it should + * be OK. + */ + +/** Maximum number of timeouts waiting for lock before we decide it's stale */ +#define MAX_LOCK_TIMEOUTS 6 +/** Length of each timeout while waiting for a lock */ +#define LOCK_TIMEOUT 500 + +static dbus_bool_t +_dbus_keyring_lock (DBusKeyring *keyring) +{ + int n_timeouts; + + n_timeouts = 0; + while (n_timeouts < MAX_LOCK_TIMEOUTS) + { + DBusError error; + + dbus_error_init (&error); + if (_dbus_create_file_exclusively (&keyring->filename_lock, + &error)) + break; + + _dbus_verbose ("Did not get lock file: %s\n", + error.message); + dbus_error_free (&error); + + _dbus_sleep_milliseconds (LOCK_TIMEOUT); + + ++n_timeouts; + } + + if (n_timeouts == MAX_LOCK_TIMEOUTS) + { + _dbus_verbose ("Lock file timed out, assuming stale\n"); + + _dbus_delete_file (&keyring->filename_lock); + + if (!_dbus_create_file_exclusively (&keyring->filename_lock, + NULL)) + { + _dbus_verbose ("Couldn't create lock file after trying to delete the stale one, giving up\n"); + return FALSE; + } + } + + return TRUE; +} + +static void +_dbus_keyring_unlock (DBusKeyring *keyring) +{ + if (!_dbus_delete_file (&keyring->filename_lock)) + _dbus_warn ("Failed to delete lock file\n"); +} + +/** + * Reloads the keyring file, optionally adds one new key to the file, + * removes all expired keys from the file, then resaves the file. + * Stores the keys from the file in keyring->keys. + * + * @param keyring the keyring + * @param add_new #TRUE to add a new key to the file before resave + * @param error return location for errors + * @returns #FALSE on failure + */ +static dbus_bool_t +_dbus_keyring_reload (DBusKeyring *keyring, + dbus_bool_t add_new, + DBusError *error) +{ + /* FIXME */ + +} /** @} */ /* end of internals */ @@ -91,5 +236,250 @@ struct DBusKeyring * @{ */ +/** + * Increments reference count of the keyring + * + * @param keyring the keyring + */ +void +_dbus_keyring_ref (DBusKeyring *keyring) +{ + keyring->refcount += 1; +} + +/** + * Decrements refcount and finalizes if it reaches + * zero. + * + * @param keyring the keyring + */ +void +_dbus_keyring_unref (DBusKeyring *keyring) +{ + keyring->refcount -= 1; + + if (keyring->refcount == 0) + { + _dbus_string_free (&keyring->filename); + _dbus_string_free (&keyring->filename_lock); + _dbus_string_free (&keyring->directory); + free_keys (keyring->keys, keyring->n_keys); + dbus_free (keyring); + } +} + +/** + * Creates a new keyring that lives in the ~/.dbus-keyrings + * directory of the given user. If the username is #NULL, + * uses the user owning the current process. + * + * @param username username to get keyring for, or #NULL + * @param context which keyring to get + * @param error return location for errors + * @returns the keyring or #NULL on error + */ +DBusKeyring* +_dbus_keyring_new_homedir (const DBusString *username, + const DBusString *context, + DBusError *error) +{ + DBusString homedir; + DBusKeyring *keyring; + dbus_bool_t error_set; + DBusString dotdir; + DBusString lock_extension; + + keyring = NULL; + error_set = FALSE; + + if (!_dbus_string_init (&homedir, _DBUS_INT_MAX)) + return FALSE; + + _dbus_string_init_const (&dotdir, ".dbus-keyrings"); + _dbus_string_init_const (&lock_extension, ".lock"); + + if (username == NULL) + { + const DBusString *const_homedir; + + if (!_dbus_user_info_from_current_process (&username, + &const_homedir, + NULL)) + goto failed; + + if (!_dbus_string_copy (const_homedir, 0, + &homedir, 0)) + goto failed; + } + else + { + if (!_dbus_homedir_from_username (username, &homedir)) + goto failed; + } + + keyring = _dbus_keyring_new (); + if (keyring == NULL) + goto failed; + + /* should have been validated already, but paranoia check here */ + if (!_dbus_keyring_validate_context (context)) + { + error_set = TRUE; + dbus_set_error_const (error, + DBUS_ERROR_FAILED, + "Invalid context in keyring creation"); + goto failed; + } + + if (!_dbus_string_copy (&homedir, 0, + &keyring->directory, 0)) + goto failed; + + if (!_dbus_concat_dir_and_file (&keyring->directory, + &dotdir)) + goto failed; + + if (!_dbus_string_copy (&keyring->directory, 0, + &keyring->filename, 0)) + goto failed; + + if (!_dbus_concat_dir_and_file (&keyring->filename, + context)) + goto failed; + + if (!_dbus_string_copy (&keyring->filename, 0, + &keyring->filename_lock, 0)) + goto failed; + + if (!_dbus_concat_dir_and_file (&keyring->filename_lock, + &lock_extension)) + goto failed; + + return keyring; + + failed: + if (!error_set) + dbus_set_error_const (error, + DBUS_ERROR_NO_MEMORY, + "No memory to create keyring"); + if (keyring) + _dbus_keyring_unref (keyring); + _dbus_string_free (&homedir); + return FALSE; + +} + +/** + * Checks whether the context is a valid context. + * Contexts that might cause confusion when used + * in filenames are not allowed (contexts can't + * start with a dot or contain dir separators). + * + * @param context the context + * @returns #TRUE if valid + */ +dbus_bool_t +_dbus_keyring_validate_context (const DBusString *context) +{ + if (_dbus_string_length (context) == 0) + { + _dbus_verbose ("context is zero-length\n"); + return FALSE; + } + + if (!_dbus_string_validate_ascii (context, 0, + _dbus_string_get_length (context))) + { + _dbus_verbose ("context not valid ascii\n"); + return FALSE; + } + + /* no directory separators */ + if (_dbus_string_find (context, 0, "/", NULL)) + { + _dbus_verbose ("context contains a slash\n"); + return FALSE; + } + + if (_dbus_string_find (context, 0, "\\", NULL)) + { + _dbus_verbose ("context contains a backslash\n"); + return FALSE; + } + + /* prevent attempts to use dotfiles or ".." or ".lock" + * all of which might allow some kind of attack + */ + if (_dbus_string_find (context, 0, ".", NULL)) + { + _dbus_verbose ("context contains a dot\n"); + return FALSE; + } + + return TRUE; +} + +static DBusKey* +find_recent_key (DBusKeyring *keyring) +{ + int i; + long tv_sec, tv_usec; + + _dbus_get_current_time (&tv_sec, &tv_usec); + + i = 0; + while (i < keyring->n_keys) + { + DBusKey *key = &keyring->keys[i]; + + if (tv_sec - MAX_KEY_LIFETIME_SECONDS < key->creation_time) + return key; + + ++i; + } + + return NULL; +} + +/** + * Gets a recent key to use for authentication. + * If no recent key exists, creates one. Returns + * the key ID. If a key can't be written to the keyring + * file so no recent key can be created, returns -1. + * All valid keys are > 0. + * + * @param keyring the keyring + * @param error error on failure + * @returns key ID to use for auth, or -1 on failure + */ +int +_dbus_keyring_get_best_key (DBusKeyring *keyring, + DBusError **error) +{ + DBusKey *key; + + key = find_recent_key (keyring); + if (key) + return key->id; + + /* All our keys are too old, or we've never loaded the + * keyring. Create a new one. + */ + if (!_dbus_keyring_reload (keyring, TRUE, + error)) + return -1; + + key = find_recent_key (keyring); + if (key) + return key->id; + else + { + dbus_set_error_const (error, + DBUS_ERROR_FAILED, + "No recent-enough key found in keyring, and unable to create a new key"); + return -1; + } +} /** @} */ /* end of exposed API */ + diff --git a/dbus/dbus-keyring.h b/dbus/dbus-keyring.h index c60c64e..7ff4fdc 100644 --- a/dbus/dbus-keyring.h +++ b/dbus/dbus-keyring.h @@ -30,19 +30,23 @@ DBUS_BEGIN_DECLS; typedef struct DBusKeyring DBusKeyring; - -DBusKeyring* _dbus_keyring_load (const char *context, - DBusResultCode *result); -void _dbus_keyring_ref (DBusKeyring *keyring); -void _dbus_keyring_unref (DBusKeyring *keyring); -dbus_bool_t _dbus_keyring_create_challenge (DBusKeyring *keyring, - DBusString *challenge); -dbus_bool_t _dbus_keyring_compute_response (DBusKeyring *keyring, - const DBusString *challenge, - DBusString *response); -dbus_bool_t _dbus_keyring_check_response (DBusKeyring *keyring, - const DBusString *challenge, - const DBusString *response); +DBusKeyring* _dbus_keyring_new_homedir (const DBusString *username, + const DBusString *context, + DBusError *error); +void _dbus_keyring_ref (DBusKeyring *keyring); +void _dbus_keyring_unref (DBusKeyring *keyring); +dbus_bool_t _dbus_keyring_validate_context (const DBusString *context); +int _dbus_keyring_get_best_key (DBusKeyring *keyring, + DBusError **error); +dbus_bool_t _dbus_keyring_create_challenge (DBusString *challenge); +dbus_bool_t _dbus_keyring_compute_response (DBusKeyring *keyring, + int key_id, + const DBusString *challenge, + DBusString *response); +dbus_bool_t _dbus_keyring_check_response (DBusKeyring *keyring, + int key_id, + const DBusString *challenge, + const DBusString *response); DBUS_END_DECLS; diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index f470c4b..7a16eec 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -982,16 +982,67 @@ _dbus_string_parse_double (const DBusString *str, * @{ */ +static dbus_bool_t +store_user_info (struct passwd *p, + DBusCredentials *credentials, + DBusString *homedir, + DBusString *username_out) +{ + int old_homedir_len; + + 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_verbose ("No memory to get homedir\n"); + return FALSE; + } + } + + if (username_out && + !_dbus_string_append (username_out, p->pw_name)) + { + if (homedir) + _dbus_string_set_length (homedir, old_homedir_len); + _dbus_verbose ("No memory to get username\n"); + 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 the credentials corresponding to the given username. + * 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 credentials credentials to fill in - * @returns #TRUE if the username existed and we got some credentials + * @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 + * + * @returns #TRUE on success */ -dbus_bool_t -_dbus_credentials_from_username (const DBusString *username, - DBusCredentials *credentials) +static dbus_bool_t +get_user_info (const DBusString *username, + int uid, + DBusCredentials *credentials, + DBusString *homedir, + DBusString *username_out) { const char *username_c_str; @@ -999,7 +1050,19 @@ _dbus_credentials_from_username (const DBusString *username, credentials->uid = -1; credentials->gid = -1; - _dbus_string_get_const_data (username, &username_c_str); + /* exactly one of username/uid provided */ + _dbus_assert (username != NULL || uid >= 0); + _dbus_assert (username == NULL || uid < 0); + + if (username != NULL) + _dbus_string_get_const_data (username, &username_c_str); + else + username_c_str = NULL; + + /* For now assuming that the getpwnam() and getpwuid() flavors + * are always symmetrical, if not we have to add more configure + * checks + */ #if defined (HAVE_POSIX_GETPWNAME_R) || defined (HAVE_NONPOSIX_GETPWNAME_R) { @@ -1010,20 +1073,23 @@ _dbus_credentials_from_username (const DBusString *username, p = NULL; #ifdef HAVE_POSIX_GETPWNAME_R - result = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf), - &p); + if (uid >= 0) + result = getpwuid_r (uid, &p_str, buf, sizeof (buf), + &p); + else + result = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf), + &p); #else - p = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf)); + if (uid >= 0) + p = getpwuid_r (uid, &p_str, buf, sizeof (buf)); + else + p = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf)); result = 0; -#endif +#endif /* !HAVE_POSIX_GETPWNAME_R */ if (result == 0 && p == &p_str) { - credentials->uid = p->pw_uid; - credentials->gid = p->pw_gid; - - _dbus_verbose ("Username %s has uid %d gid %d\n", - username_c_str, credentials->uid, credentials->gid); - return TRUE; + return store_user_info (p, credentials, homedir, + username_out); } else { @@ -1036,16 +1102,15 @@ _dbus_credentials_from_username (const DBusString *username, /* I guess we're screwed on thread safety here */ struct passwd *p; - p = getpwnam (username_c_str); + if (uid >= 0) + p = getpwuid (uid); + else + p = getpwnam (username_c_str); if (p != NULL) { - credentials->uid = p->pw_uid; - credentials->gid = p->pw_gid; - - _dbus_verbose ("Username %s has uid %d gid %d\n", - username_c_str, credentials->uid, credentials->gid); - return TRUE; + return store_user_info (p, credentials, homedir, + username_out); } else { @@ -1053,7 +1118,112 @@ _dbus_credentials_from_username (const DBusString *username, return FALSE; } } -#endif +#endif /* ! HAVE_GETPWNAM_R */ +} + +/** + * 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); +} + +static DBusMutex *user_info_lock = NULL; +/** + * Initializes the global mutex for the process's user information. + * + * @returns the mutex + */ +DBusMutex * +_dbus_user_info_init_lock (void) +{ + user_info_lock = dbus_mutex_new (); + return user_info_lock; +} + +/** + * 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 DBusString name; + static DBusString dir; + static DBusCredentials creds; + static dbus_bool_t initialized = FALSE; + + if (!dbus_mutex_lock (user_info_lock)) + return FALSE; + + if (!initialized) + { + if (!_dbus_string_init (&name, _DBUS_INT_MAX)) + { + dbus_mutex_unlock (user_info_lock); + return FALSE; + } + + if (!_dbus_string_init (&dir, _DBUS_INT_MAX)) + { + _dbus_string_free (&name); + dbus_mutex_unlock (user_info_lock); + return FALSE; + } + + creds.uid = -1; + creds.gid = -1; + creds.pid = -1; + + if (!get_user_info (NULL, getuid (), + &creds, &dir, &name)) + { + _dbus_string_free (&name); + _dbus_string_free (&dir); + dbus_mutex_unlock (user_info_lock); + return FALSE; + } + + initialized = TRUE; + } + + if (username) + *username = &name; + if (homedir) + *homedir = &dir; + if (credentials) + *credentials = &creds; + + dbus_mutex_unlock (user_info_lock); + + 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); } /** @@ -1506,6 +1676,68 @@ _dbus_string_save_to_file (const DBusString *str, return DBUS_RESULT_SUCCESS; } +/** Creates the given file, failing if the file already exists. + * + * @param filename the filename + * @param error error location + * @returns #TRUE if we created the file and it didn't exist + */ +dbus_bool_t +_dbus_create_file_exclusively (const DBusString *filename, + DBusError *error) +{ + int fd; + const char *filename_c; + + _dbus_string_get_const_data (filename, &filename_c); + + fd = open (filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT, + 0600); + if (fd < 0) + { + dbus_set_error (error, + DBUS_ERROR_FAILED, + "Could not create file %s: %s\n", + filename_c, + _dbus_errno_to_string (errno)); + return FALSE; + } + + if (close (fd) < 0) + { + dbus_set_error (error, + DBUS_ERROR_FAILED, + "Could not close file %s: %s\n", + filename_c, + _dbus_errno_to_string (errno)); + return FALSE; + } + + return TRUE; +} + +/** + * Deletes the given file. + * + * @param filename the filename + * @param error error location + * + * @returns #TRUE if unlink() succeeded + */ +dbus_bool_t +_dbus_delete_file (const DBusString *filename, + DBusError *error) +{ + const char *filename_c; + + _dbus_string_get_const_data (filename, &filename_c); + + if (unlink (filename_c) < 0) + return FALSE; + else + return TRUE; +} + /** * Appends the given filename to the given directory. * diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 3c95227..b14833e 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -104,6 +104,11 @@ dbus_bool_t _dbus_credentials_match (const DBusCredentials *expec 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 int dbus_atomic_t; @@ -138,6 +143,11 @@ DBusResultCode _dbus_file_get_contents (DBusString *str, DBusResultCode _dbus_string_save_to_file (const DBusString *str, const DBusString *filename); +dbus_bool_t _dbus_create_file_exclusively (const DBusString *filename, + DBusError *error); +dbus_bool_t _dbus_delete_file (const DBusString *filename, + DBusError *error); + dbus_bool_t _dbus_concat_dir_and_file (DBusString *dir, const DBusString *next_component); diff --git a/dbus/dbus-threads.c b/dbus/dbus-threads.c index a883057..2a903a2 100644 --- a/dbus/dbus-threads.c +++ b/dbus/dbus-threads.c @@ -208,6 +208,7 @@ init_static_locks(void) {&_dbus_allocated_slots_init_lock}, {&_dbus_atomic_init_lock}, {&_dbus_message_handler_init_lock}, + {&_dbus_user_info_init_lock} }; for (i = 0; i < _DBUS_N_ELEMENTS (static_locks); i++) -- 2.7.4