2003-02-19 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Thu, 20 Feb 2003 03:43:18 +0000 (03:43 +0000)
committerHavoc Pennington <hp@redhat.com>
Thu, 20 Feb 2003 03:43:18 +0000 (03:43 +0000)
* 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
Doxyfile.in
dbus/dbus-errors.c
dbus/dbus-errors.h
dbus/dbus-internals.h
dbus/dbus-keyring.c
dbus/dbus-keyring.h
dbus/dbus-sysdeps.c
dbus/dbus-sysdeps.h
dbus/dbus-threads.c

index 023b43d..4404bdf 100644 (file)
--- 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:
index 877b4ed..f01db5d 100644 (file)
@@ -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
 #---------------------------------------------------------------------------
index 92cca03..a469f7e 100644 (file)
@@ -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;
index 6e83dae..1b1a880 100644 (file)
@@ -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"
index 9f9cba8..551a377 100644 (file)
@@ -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;
 
index da66da9..0f1dd1c 100644 (file)
  * @{
  */
 
+/** 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 */
+
index c60c64e..7ff4fdc 100644 (file)
@@ -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;
index f470c4b..7a16eec 100644 (file)
@@ -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.
  *
index 3c95227..b14833e 100644 (file)
@@ -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);
 
index a883057..2a903a2 100644 (file)
@@ -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++)