summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2003-02-20 03:43:18 +0000
committerHavoc Pennington <hp@redhat.com>2003-02-20 03:43:18 +0000
commit6b40feaff4114ab3498ad06e13063fceff4d48e9 (patch)
treef9e3fabcf592dc04c256c6ebcc923f95e6ee11e3
parent89ee9e6abf40b594c681479dfc4d18d892c93838 (diff)
2003-02-19 Havoc Pennington <hp@pobox.com>
* 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
-rw-r--r--ChangeLog24
-rw-r--r--Doxyfile.in2
-rw-r--r--dbus/dbus-errors.c21
-rw-r--r--dbus/dbus-errors.h1
-rw-r--r--dbus/dbus-internals.h2
-rw-r--r--dbus/dbus-keyring.c404
-rw-r--r--dbus/dbus-keyring.h30
-rw-r--r--dbus/dbus-sysdeps.c282
-rw-r--r--dbus/dbus-sysdeps.h10
-rw-r--r--dbus/dbus-threads.c1
10 files changed, 717 insertions, 60 deletions
diff --git a/ChangeLog b/ChangeLog
index 023b43d3..4404bdf3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2003-02-19 Havoc Pennington <hp@pobox.com>
+
+ * 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 <andersca@codefactory.se>
* configure.in:
diff --git a/Doxyfile.in b/Doxyfile.in
index 877b4ed5..f01db5d9 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 92cca03c..a469f7e6 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 6e83dae4..1b1a880c 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 9f9cba85..551a377f 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 da66da98..0f1dd1c9 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 c60c64ef..7ff4fdc4 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 f470c4b8..7a16eecf 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 3c95227d..b14833eb 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 a883057d..2a903a27 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++)