2003-03-04 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Wed, 5 Mar 2003 02:01:34 +0000 (02:01 +0000)
committerHavoc Pennington <hp@redhat.com>
Wed, 5 Mar 2003 02:01:34 +0000 (02:01 +0000)
* test/data/auth/*: adapt to changes

* dbus/dbus-auth-script.c (_dbus_auth_script_run): add
USERID_BASE64 and change USERNAME_BASE64 to put in username not
userid

* dbus/dbus-keyring.c (_dbus_keyring_validate_context): prevent
more stuff from being in a context name, to make the protocol
simpler to deal with

* dbus/dbus-errors.c (dbus_error_has_name): new function
(dbus_error_is_set): new function

* dbus/dbus-auth.c: replace DBUS_STUPID_TEST_MECH auth
with DBUS_COOKIE_SHA1, implement DBUS_COOKIE_SHA1

* dbus/dbus-connection.c (dbus_connection_flush): also read
messages during a flush operation

* dbus/Makefile.am: remove dbus-md5 since it isn't currently used.

21 files changed:
ChangeLog
dbus/Makefile.am
dbus/dbus-auth-script.c
dbus/dbus-auth.c
dbus/dbus-auth.h
dbus/dbus-connection.c
dbus/dbus-errors.c
dbus/dbus-errors.h
dbus/dbus-keyring.c
dbus/dbus-keyring.h
dbus/dbus-message.c
dbus/dbus-sysdeps.c
dbus/dbus-test.c
doc/dbus-specification.sgml
test/data/auth/external-failed.auth-script
test/data/auth/external-root.auth-script
test/data/auth/external-silly.auth-script
test/data/auth/external-successful.auth-script
test/data/auth/extra-bytes.auth-script
test/data/auth/fail-after-n-attempts.auth-script
test/data/auth/fallback.auth-script

index c897b38..ab0abd9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2003-03-04  Havoc Pennington  <hp@pobox.com>
+
+       * test/data/auth/*: adapt to changes
+
+       * dbus/dbus-auth-script.c (_dbus_auth_script_run): add
+       USERID_BASE64 and change USERNAME_BASE64 to put in username not
+       userid
+
+       * dbus/dbus-keyring.c (_dbus_keyring_validate_context): prevent
+       more stuff from being in a context name, to make the protocol 
+       simpler to deal with
+
+       * dbus/dbus-errors.c (dbus_error_has_name): new function
+       (dbus_error_is_set): new function
+
+       * dbus/dbus-auth.c: replace DBUS_STUPID_TEST_MECH auth 
+       with DBUS_COOKIE_SHA1, implement DBUS_COOKIE_SHA1
+       
+       * dbus/dbus-connection.c (dbus_connection_flush): also read
+       messages during a flush operation
+
+       * dbus/Makefile.am: remove dbus-md5 since it isn't currently used.
+
 2003-03-05  Anders Carlsson  <andersca@codefactory.se>
 
        * configure.in: Check for gethostbyname on Solaris.
index b0b001a..dfcfb7d 100644 (file)
@@ -32,8 +32,6 @@ libdbus_1_la_SOURCES=                         \
        dbus-errors.c                           \
        dbus-keyring.c                          \
        dbus-keyring.h                          \
-       dbus-md5.c                              \
-       dbus-md5.h                              \
        dbus-memory.c                           \
        dbus-message.c                          \
        dbus-message-handler.c                  \
@@ -63,6 +61,9 @@ libdbus_1_la_SOURCES=                         \
        dbus-watch.c                            \
        dbus-watch.h
 
+##     dbus-md5.c                              \
+##     dbus-md5.h                              \
+
 
 ## this library is linked into both libdbus and the bus 
 ## itself, but does not export any symbols from libdbus.
index 4dccfe9..732b451 100644 (file)
@@ -191,9 +191,12 @@ _dbus_auth_script_run (const DBusString *filename)
   DBusAuth *auth;
   DBusString from_auth;
   DBusAuthState state;
+  DBusString context;
   
   retval = FALSE;
   auth = NULL;
+
+  _dbus_string_init_const (&context, "org_freedesktop_test");
   
   if (!_dbus_string_init (&file, _DBUS_INT_MAX))
     return FALSE;
@@ -299,6 +302,7 @@ _dbus_auth_script_run (const DBusString *filename)
 
           _dbus_credentials_from_current_process (&creds);
           _dbus_auth_set_credentials (auth, &creds);
+          _dbus_auth_set_context (auth, &context);
         }
       else if (auth == NULL)
         {
@@ -359,24 +363,60 @@ _dbus_auth_script_run (const DBusString *filename)
               goto out;
             }
 
-          /* Replace USERNAME_BASE64 with our username in base64 */
+          /* Replace USERID_BASE64 with our username in base64 */
           {
             int where;
             
             if (_dbus_string_find (&to_send, 0,
-                                   "USERNAME_BASE64", &where))
+                                   "USERID_BASE64", &where))
               {
                 DBusString username;
 
                 if (!_dbus_string_init (&username, _DBUS_INT_MAX))
                   {
-                    _dbus_warn ("no memory for username\n");
+                    _dbus_warn ("no memory for userid\n");
                     _dbus_string_free (&to_send);
                     goto out;
                   }
 
                 if (!_dbus_string_append_our_uid (&username))
                   {
+                    _dbus_warn ("no memory for userid\n");
+                    _dbus_string_free (&username);
+                    _dbus_string_free (&to_send);
+                    goto out;
+                  }
+
+                _dbus_string_delete (&to_send, where, strlen ("USERID_BASE64"));
+                
+                if (!_dbus_string_base64_encode (&username, 0,
+                                                 &to_send, where))
+                  {
+                    _dbus_warn ("no memory to subst USERID_BASE64\n");
+                    _dbus_string_free (&username);
+                    _dbus_string_free (&to_send);
+                    goto out;
+                  }
+
+                _dbus_string_free (&username);
+              }
+            else if (_dbus_string_find (&to_send, 0,
+                                        "USERNAME_BASE64", &where))
+              {
+                DBusString username;
+                const DBusString *u;
+                
+                if (!_dbus_string_init (&username, _DBUS_INT_MAX))
+                  {
+                    _dbus_warn ("no memory for username\n");
+                    _dbus_string_free (&to_send);
+                    goto out;
+                  }
+
+                if (!_dbus_user_info_from_current_process (&u, NULL, NULL) ||
+                    !_dbus_string_copy (u, 0, &username,
+                                        _dbus_string_get_length (&username)))
+                  {
                     _dbus_warn ("no memory for username\n");
                     _dbus_string_free (&username);
                     _dbus_string_free (&to_send);
index f078136..516a51a 100644 (file)
@@ -24,6 +24,8 @@
 #include "dbus-string.h"
 #include "dbus-list.h"
 #include "dbus-internals.h"
+#include "dbus-keyring.h"
+#include "dbus-sha.h"
 
 /* See doc/dbus-sasl-profile.txt */
 
  * @todo some SASL profiles require sending the empty string as a
  * challenge/response, but we don't currently allow that in our
  * protocol.
+ *
+ * @todo DBusAuth really needs to be rewritten as an explicit state
+ * machine. Right now it's too hard to prove to yourself by inspection
+ * that it works.
+ *
+ * @todo right now sometimes both ends will block waiting for input
+ * from the other end, e.g. if there's an error during
+ * DBUS_COOKIE_SHA1.
+ *
+ * @todo the cookie keyring needs to be cached globally not just
+ * per-auth (which raises threadsafety issues too)
+ * 
+ * @todo grep FIXME in dbus-auth.c
  */
 
 /**
@@ -132,9 +147,18 @@ struct DBusAuth
                                           *   as.
                                           */
   
-  DBusCredentials credentials;      /**< Credentials, fields may be -1 */
+  DBusCredentials credentials;      /**< Credentials read from socket,
+                                     * fields may be -1
+                                     */
 
   DBusCredentials authorized_identity; /**< Credentials that are authorized */
+
+  DBusCredentials desired_identity;    /**< Identity client has requested */
+  
+  DBusString context;               /**< Cookie scope */
+  DBusKeyring *keyring;             /**< Keyring for cookie mechanism. */
+  int cookie_id;                    /**< ID of cookie to use */
+  DBusString challenge;             /**< Challenge sent to client */
   
   unsigned int needed_memory : 1;   /**< We needed memory to continue since last
                                      * successful getting something done
@@ -254,6 +278,13 @@ _dbus_auth_new (int size)
   auth->authorized_identity.pid = -1;
   auth->authorized_identity.uid = -1;
   auth->authorized_identity.gid = -1;
+
+  auth->desired_identity.pid = -1;
+  auth->desired_identity.uid = -1;
+  auth->desired_identity.gid = -1;
+  
+  auth->keyring = NULL;
+  auth->cookie_id = -1;
   
   /* note that we don't use the max string length feature,
    * because you can't use that feature if you're going to
@@ -264,27 +295,39 @@ _dbus_auth_new (int size)
    */
   
   if (!_dbus_string_init (&auth->incoming, _DBUS_INT_MAX))
-    {
-      dbus_free (auth);
-      return NULL;
-    }
+    goto enomem_0;
 
   if (!_dbus_string_init (&auth->outgoing, _DBUS_INT_MAX))
-    {
-      _dbus_string_free (&auth->incoming);
-      dbus_free (auth);
-      return NULL;
-    }
-  
+    goto enomem_1;
+    
   if (!_dbus_string_init (&auth->identity, _DBUS_INT_MAX))
-    {
-      _dbus_string_free (&auth->incoming);
-      _dbus_string_free (&auth->outgoing);
-      dbus_free (auth);
-      return NULL;
-    }
+    goto enomem_2;
+
+  if (!_dbus_string_init (&auth->context, _DBUS_INT_MAX))
+    goto enomem_3;
+
+  if (!_dbus_string_init (&auth->challenge, _DBUS_INT_MAX))
+    goto enomem_4;
+
+  /* default context if none is specified */
+  if (!_dbus_string_append (&auth->context, "org_freedesktop_general"))
+    goto enomem_5;
   
   return auth;
+
+ enomem_5:
+  _dbus_string_free (&auth->challenge);
+ enomem_4:
+  _dbus_string_free (&auth->context);
+ enomem_3:
+  _dbus_string_free (&auth->identity);
+ enomem_2:
+  _dbus_string_free (&auth->incoming);
+ enomem_1:
+  _dbus_string_free (&auth->outgoing);
+ enomem_0:
+  dbus_free (auth);
+  return NULL;
 }
 
 static void
@@ -295,9 +338,14 @@ shutdown_mech (DBusAuth *auth)
   auth->authenticated = FALSE;
   auth->already_asked_for_initial_response = FALSE;
   _dbus_string_set_length (&auth->identity, 0);
+  
   auth->authorized_identity.pid = -1;
   auth->authorized_identity.uid = -1;
   auth->authorized_identity.gid = -1;
+
+  auth->desired_identity.pid = -1;
+  auth->desired_identity.uid = -1;
+  auth->desired_identity.gid = -1;
   
   if (auth->mech != NULL)
     {
@@ -313,74 +361,601 @@ shutdown_mech (DBusAuth *auth)
     }
 }
 
+/* Returns TRUE but with an empty string hash if the
+ * cookie_id isn't known. As with all this code
+ * TRUE just means we had enough memory.
+ */
 static dbus_bool_t
-handle_server_data_stupid_test_mech (DBusAuth         *auth,
-                                     const DBusString *data)
+sha1_compute_hash (DBusAuth         *auth,
+                   int               cookie_id,
+                   const DBusString *server_challenge,
+                   const DBusString *client_challenge,
+                   DBusString       *hash)
 {
-  if (!_dbus_string_append (&auth->outgoing,
-                            "OK\r\n"))
+  DBusString cookie;
+  DBusString to_hash;
+  dbus_bool_t retval;
+  
+  _dbus_assert (auth->keyring != NULL);
+
+  retval = FALSE;
+  
+  if (!_dbus_string_init (&cookie, _DBUS_INT_MAX))
     return FALSE;
 
-  _dbus_credentials_from_current_process (&auth->authorized_identity);
+  if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id,
+                                  &cookie))
+    goto out_0;
 
-  auth->authenticated_pending_begin = TRUE;
+  if (_dbus_string_get_length (&cookie) == 0)
+    {
+      retval = TRUE;
+      goto out_0;
+    }
+
+  if (!_dbus_string_init (&to_hash, _DBUS_INT_MAX))
+    goto out_0;
   
-  return TRUE;
+  if (!_dbus_string_copy (server_challenge, 0,
+                          &to_hash, _dbus_string_get_length (&to_hash)))
+    goto out_1;
+
+  if (!_dbus_string_append (&to_hash, ":"))
+    goto out_1;
+  
+  if (!_dbus_string_copy (client_challenge, 0,
+                          &to_hash, _dbus_string_get_length (&to_hash)))
+    goto out_1;
+
+  if (!_dbus_string_append (&to_hash, ":"))
+    goto out_1;
+
+  if (!_dbus_string_copy (&cookie, 0,
+                          &to_hash, _dbus_string_get_length (&to_hash)))
+    goto out_1;
+
+  if (!_dbus_sha_compute (&to_hash, hash))
+    goto out_1;
+  
+  retval = TRUE;
+
+ out_1:
+  _dbus_string_zero (&to_hash);
+  _dbus_string_free (&to_hash);
+ out_0:
+  _dbus_string_zero (&cookie);
+  _dbus_string_free (&cookie);
+  return retval;
 }
 
-static void
-handle_server_shutdown_stupid_test_mech (DBusAuth *auth)
+/* http://www.ietf.org/rfc/rfc2831.txt suggests at least 64 bits of
+ * entropy, we use 128
+ */
+#define N_CHALLENGE_BYTES (128/8)
+
+static dbus_bool_t
+sha1_handle_first_client_response (DBusAuth         *auth,
+                                   const DBusString *data)
 {
+  /* We haven't sent a challenge yet, we're expecting a desired
+   * username from the client.
+   */
+  DBusString tmp;
+  DBusString tmp2;
+  dbus_bool_t retval;
+  int old_len;
+  DBusError error;
+  
+  retval = FALSE;
+
+  _dbus_string_set_length (&auth->challenge, 0);
+  
+  if (_dbus_string_get_length (data) > 0)
+    {
+      if (_dbus_string_get_length (&auth->identity) > 0)
+        {
+          /* Tried to send two auth identities, wtf */
+          _dbus_verbose ("client tried to send auth identity, but we already have one\n");
+          return send_rejected (auth);
+        }
+      else
+        {
+          /* this is our auth identity */
+          if (!_dbus_string_copy (data, 0, &auth->identity, 0))
+            return FALSE;
+        }
+    }
+      
+  if (!_dbus_credentials_from_username (data, &auth->desired_identity))
+    {
+      _dbus_verbose ("Did not get a valid username from client\n");
+      return send_rejected (auth);
+    }
+      
+  if (!_dbus_string_init (&tmp, _DBUS_INT_MAX))
+    return FALSE;
+
+  if (!_dbus_string_init (&tmp2, _DBUS_INT_MAX))
+    {
+      _dbus_string_free (&tmp);
+      return FALSE;
+    }
+
+  old_len = _dbus_string_get_length (&auth->outgoing);
+  
+  /* we cache the keyring for speed, so here we drop it if it's the
+   * wrong one. FIXME caching the keyring here is useless since we use
+   * a different DBusAuth for every connection.
+   */
+  if (auth->keyring &&
+      !_dbus_keyring_is_for_user (auth->keyring,
+                                  data))
+    {
+      _dbus_keyring_unref (auth->keyring);
+      auth->keyring = NULL;
+    }
+  
+  if (auth->keyring == NULL)
+    {
+      DBusError error;
+
+      dbus_error_init (&error);
+      auth->keyring = _dbus_keyring_new_homedir (data,
+                                                 &auth->context,
+                                                 &error);
+
+      if (auth->keyring == NULL)
+        {
+          if (dbus_error_has_name (&error,
+                                   DBUS_ERROR_NO_MEMORY))
+            {
+              dbus_error_free (&error);
+              goto out;
+            }
+          else
+            {
+              _dbus_assert (dbus_error_is_set (&error));
+              _dbus_verbose ("Error loading keyring: %s\n",
+                             error.message);
+              if (send_rejected (auth))
+                retval = TRUE; /* retval is only about mem */
+              dbus_error_free (&error);
+              goto out;
+            }
+        }
+      else
+        {
+          _dbus_assert (!dbus_error_is_set (&error));
+        }
+    }
+
+  _dbus_assert (auth->keyring != NULL);
+
+  dbus_error_init (&error);
+  auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error);
+  if (auth->cookie_id < 0)
+    {
+      _dbus_assert (dbus_error_is_set (&error));
+      _dbus_verbose ("Could not get a cookie ID to send to client: %s\n",
+                     error.message);
+      if (send_rejected (auth))
+        retval = TRUE;
+      dbus_error_free (&error);
+      goto out;
+    }
+  else
+    {
+      _dbus_assert (!dbus_error_is_set (&error));
+    }
 
+  if (!_dbus_string_copy (&auth->context, 0,
+                          &tmp2, _dbus_string_get_length (&tmp2)))
+    goto out;
+
+  if (!_dbus_string_append (&tmp2, " "))
+    goto out;
+
+  if (!_dbus_string_append_int (&tmp2, auth->cookie_id))
+    goto out;
+
+  if (!_dbus_string_append (&tmp2, " "))
+    goto out;  
+  
+  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
+    goto out;
+
+  _dbus_string_set_length (&auth->challenge, 0);
+  if (!_dbus_string_hex_encode (&tmp, 0, &auth->challenge, 0))
+    goto out;
+  
+  if (!_dbus_string_hex_encode (&tmp, 0, &tmp2,
+                                _dbus_string_get_length (&tmp2)))
+    goto out;
+
+  if (!_dbus_string_append (&auth->outgoing,
+                            "DATA "))
+    goto out;
+  
+  if (!_dbus_string_base64_encode (&tmp2, 0, &auth->outgoing,
+                                   _dbus_string_get_length (&auth->outgoing)))
+    goto out;
+
+  if (!_dbus_string_append (&auth->outgoing,
+                            "\r\n"))
+    goto out;
+      
+  retval = TRUE;
+  
+ out:
+  _dbus_string_zero (&tmp);
+  _dbus_string_free (&tmp);
+  _dbus_string_zero (&tmp2);
+  _dbus_string_free (&tmp2);
+  if (!retval)
+    _dbus_string_set_length (&auth->outgoing, old_len);
+  return retval;
 }
 
 static dbus_bool_t
-handle_client_data_stupid_test_mech (DBusAuth         *auth,
-                                     const DBusString *data)
+sha1_handle_second_client_response (DBusAuth         *auth,
+                                    const DBusString *data)
 {
+  /* We are expecting a response which is the hex-encoded client
+   * challenge, space, then SHA-1 hash of the concatenation of our
+   * challenge, ":", client challenge, ":", secret key, all
+   * hex-encoded.
+   */
+  int i;
+  DBusString client_challenge;
+  DBusString client_hash;
+  dbus_bool_t retval;
+  DBusString correct_hash;
   
-  return TRUE;
+  retval = FALSE;
+  
+  if (!_dbus_string_find_blank (data, 0, &i))
+    {
+      _dbus_verbose ("no space separator in client response\n");
+      return send_rejected (auth);
+    }
+  
+  if (!_dbus_string_init (&client_challenge, _DBUS_INT_MAX))
+    goto out_0;
+
+  if (!_dbus_string_init (&client_hash, _DBUS_INT_MAX))
+    goto out_1;  
+
+  if (!_dbus_string_copy_len (data, 0, i, &client_challenge,
+                              0))
+    goto out_2;
+
+  _dbus_string_skip_blank (data, i, &i);
+  
+  if (!_dbus_string_copy_len (data, i,
+                              _dbus_string_get_length (data) - i,
+                              &client_hash,
+                              0))
+    goto out_2;
+
+  if (_dbus_string_get_length (&client_challenge) == 0 ||
+      _dbus_string_get_length (&client_hash) == 0)
+    {
+      _dbus_verbose ("zero-length client challenge or hash\n");
+      if (send_rejected (auth))
+        retval = TRUE;
+      goto out_2;
+    }
+
+  if (!_dbus_string_init (&correct_hash, _DBUS_INT_MAX))
+    goto out_2;
+
+  if (!sha1_compute_hash (auth, auth->cookie_id,
+                          &auth->challenge, 
+                          &client_challenge,
+                          &correct_hash))
+    goto out_3;
+
+  /* if cookie_id was invalid, then we get an empty hash */
+  if (_dbus_string_get_length (&correct_hash) == 0)
+    {
+      if (send_rejected (auth))
+        retval = TRUE;
+      goto out_3;
+    }
+  
+  if (!_dbus_string_equal (&client_hash, &correct_hash))
+    {
+      if (send_rejected (auth))
+        retval = TRUE;
+      goto out_3;
+    }
+      
+  if (!_dbus_string_append (&auth->outgoing,
+                            "OK\r\n"))
+    goto out_3;
+
+  _dbus_verbose ("authenticated client with UID %d using DBUS_COOKIE_SHA1\n",
+                 auth->desired_identity.uid);
+  
+  auth->authorized_identity = auth->desired_identity;
+  auth->authenticated_pending_begin = TRUE;
+  retval = TRUE;
+  
+ out_3:
+  _dbus_string_zero (&correct_hash);
+  _dbus_string_free (&correct_hash);
+ out_2:
+  _dbus_string_zero (&client_hash);
+  _dbus_string_free (&client_hash);
+ out_1:
+  _dbus_string_free (&client_challenge);
+ out_0:
+  return retval;
 }
 
-static void
-handle_client_shutdown_stupid_test_mech (DBusAuth *auth)
+static dbus_bool_t
+handle_server_data_cookie_sha1_mech (DBusAuth         *auth,
+                                     const DBusString *data)
 {
+  if (auth->cookie_id < 0)
+    return sha1_handle_first_client_response (auth, data);
+  else
+    return sha1_handle_second_client_response (auth, data);
+}
 
+static void
+handle_server_shutdown_cookie_sha1_mech (DBusAuth *auth)
+{
+  auth->cookie_id = -1;  
+  _dbus_string_set_length (&auth->challenge, 0);
 }
 
-/* the stupid test mech is a base64-encoded string;
- * all the inefficiency, none of the security!
- */
 static dbus_bool_t
-handle_encode_stupid_test_mech (DBusAuth         *auth,
-                                const DBusString *plaintext,
-                                DBusString       *encoded)
+handle_client_initial_response_cookie_sha1_mech (DBusAuth   *auth,
+                                                 DBusString *response)
 {
-  if (!_dbus_string_base64_encode (plaintext, 0, encoded,
-                                   _dbus_string_get_length (encoded)))
-    return FALSE;
+  const DBusString *username;
+  dbus_bool_t retval;
+
+  retval = FALSE;
+
+  if (!_dbus_user_info_from_current_process (&username,
+                                             NULL, NULL))
+    goto out_0;
+
+  if (!_dbus_string_base64_encode (username, 0,
+                                   response,
+                                   _dbus_string_get_length (response)))
+    goto out_0;
+
+  retval = TRUE;
   
-  return TRUE;
+ out_0:
+  return retval;
 }
 
+/* FIXME if we send the server an error, right now both sides
+ * just hang. Server has to reject on getting an error, or
+ * client has to cancel. Should be in the spec.
+ */
 static dbus_bool_t
-handle_decode_stupid_test_mech (DBusAuth         *auth,
-                                const DBusString *encoded,
-                                DBusString       *plaintext)
+handle_client_data_cookie_sha1_mech (DBusAuth         *auth,
+                                     const DBusString *data)
 {
-  if (!_dbus_string_base64_decode (encoded, 0, plaintext,
-                                   _dbus_string_get_length (plaintext)))
-    return FALSE;
+  /* The data we get from the server should be the cookie context
+   * name, the cookie ID, and the server challenge, separated by
+   * spaces. We send back our challenge string and the correct hash.
+   */
+  dbus_bool_t retval;
+  DBusString context;
+  DBusString cookie_id_str;
+  DBusString server_challenge;
+  DBusString client_challenge;
+  DBusString correct_hash;
+  DBusString tmp;
+  int i, j;
+  long val;
+  int old_len;
   
-  return TRUE;
+  retval = FALSE;                 
+  
+  if (!_dbus_string_find_blank (data, 0, &i))
+    {
+      if (_dbus_string_append (&auth->outgoing,
+                               "ERROR \"Server did not send context/ID/challenge properly\"\r\n"))
+        retval = TRUE;
+      goto out_0;
+    }
+
+  if (!_dbus_string_init (&context, _DBUS_INT_MAX))
+    goto out_0;
+
+  if (!_dbus_string_copy_len (data, 0, i,
+                              &context, 0))
+    goto out_1;
+  
+  _dbus_string_skip_blank (data, i, &i);
+  if (!_dbus_string_find_blank (data, i, &j))
+    {
+      if (_dbus_string_append (&auth->outgoing,
+                               "ERROR \"Server did not send context/ID/challenge properly\"\r\n"))
+        retval = TRUE;
+      goto out_1;
+    }
+
+  if (!_dbus_string_init (&cookie_id_str, _DBUS_INT_MAX))
+    goto out_1;
+  
+  if (!_dbus_string_copy_len (data, i, j - i,
+                              &cookie_id_str, 0))
+    goto out_2;  
+
+  if (!_dbus_string_init (&server_challenge, _DBUS_INT_MAX))
+    goto out_2;
+
+  i = j;
+  _dbus_string_skip_blank (data, i, &i);
+  j = _dbus_string_get_length (data);
+
+  if (!_dbus_string_copy_len (data, i, j - i,
+                              &server_challenge, 0))
+    goto out_3;
+
+  if (!_dbus_keyring_validate_context (&context))
+    {
+      if (_dbus_string_append (&auth->outgoing,
+                               "ERROR \"Server sent invalid cookie context\"\r\n"))
+        retval = TRUE;
+      goto out_3;
+    }
+
+  if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL))
+    {
+      if (_dbus_string_append (&auth->outgoing,
+                               "ERROR \"Could not parse cookie ID as an integer\"\r\n"))
+        retval = TRUE;
+      goto out_3;
+    }
+
+  if (_dbus_string_get_length (&server_challenge) == 0)
+    {
+      if (_dbus_string_append (&auth->outgoing,
+                               "ERROR \"Empty server challenge string\"\r\n"))
+        retval = TRUE;
+      goto out_3;
+    }
+
+  if (auth->keyring == NULL)
+    {
+      DBusError error;
+
+      dbus_error_init (&error);
+      auth->keyring = _dbus_keyring_new_homedir (NULL,
+                                                 &context,
+                                                 &error);
+
+      if (auth->keyring == NULL)
+        {
+          if (dbus_error_has_name (&error,
+                                   DBUS_ERROR_NO_MEMORY))
+            {
+              dbus_error_free (&error);
+              goto out_3;
+            }
+          else
+            {
+              _dbus_assert (dbus_error_is_set (&error));
+              _dbus_verbose ("Error loading keyring: %s\n",
+                             error.message);
+              
+              if (_dbus_string_append (&auth->outgoing,
+                                       "ERROR \"Could not load cookie file\"\r\n"))
+                retval = TRUE; /* retval is only about mem */
+              
+              dbus_error_free (&error);
+              goto out_3;
+            }
+        }
+      else
+        {
+          _dbus_assert (!dbus_error_is_set (&error));
+        }
+    }
+  
+  _dbus_assert (auth->keyring != NULL);
+  
+  if (!_dbus_string_init (&tmp, _DBUS_INT_MAX))
+    goto out_3;
+  
+  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
+    goto out_4;
+
+  if (!_dbus_string_init (&client_challenge, _DBUS_INT_MAX))
+    goto out_4;
+
+  if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0))
+    goto out_5;
+
+  if (!_dbus_string_init (&correct_hash, _DBUS_INT_MAX))
+    goto out_6;
+  
+  if (!sha1_compute_hash (auth, val,
+                          &server_challenge,
+                          &client_challenge,
+                          &correct_hash))
+    goto out_6;
+
+  if (_dbus_string_get_length (&correct_hash) == 0)
+    {
+      /* couldn't find the cookie ID or something */
+      if (_dbus_string_append (&auth->outgoing,
+                               "ERROR \"Don't have the requested cookie ID\"\r\n"))
+        retval = TRUE;
+      goto out_6;
+    }
+  
+  _dbus_string_set_length (&tmp, 0);
+  
+  if (!_dbus_string_copy (&client_challenge, 0, &tmp,
+                          _dbus_string_get_length (&tmp)))
+    goto out_6;
+
+  if (!_dbus_string_append (&tmp, " "))
+    goto out_6;
+
+  if (!_dbus_string_copy (&correct_hash, 0, &tmp,
+                          _dbus_string_get_length (&tmp)))
+    goto out_6;
+
+  old_len = _dbus_string_get_length (&auth->outgoing);
+  if (!_dbus_string_append (&auth->outgoing, "DATA "))
+    goto out_6;
+
+  if (!_dbus_string_base64_encode (&tmp, 0,
+                                   &auth->outgoing,
+                                   _dbus_string_get_length (&auth->outgoing)))
+    {
+      _dbus_string_set_length (&auth->outgoing, old_len);
+      goto out_6;
+    }
+
+  if (!_dbus_string_append (&auth->outgoing, "\r\n"))
+    {
+      _dbus_string_set_length (&auth->outgoing, old_len);
+      goto out_6;
+    }
+  
+  retval = TRUE;
+
+ out_6:
+  _dbus_string_zero (&correct_hash);
+  _dbus_string_free (&correct_hash);
+ out_5:
+  _dbus_string_free (&client_challenge);
+ out_4:
+  _dbus_string_zero (&tmp);
+  _dbus_string_free (&tmp);
+ out_3:
+  _dbus_string_free (&server_challenge);
+ out_2:
+  _dbus_string_free (&cookie_id_str);
+ out_1:
+  _dbus_string_free (&context);
+ out_0:
+  return retval;
+}
+
+static void
+handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth)
+{
+  auth->cookie_id = -1;  
+  _dbus_string_set_length (&auth->challenge, 0);
 }
 
 static dbus_bool_t
 handle_server_data_external_mech (DBusAuth         *auth,
                                   const DBusString *data)
 {
-  DBusCredentials desired_identity;
-
   if (auth->credentials.uid < 0)
     {
       _dbus_verbose ("no credentials, mechanism EXTERNAL can't authenticate\n");
@@ -418,9 +993,9 @@ handle_server_data_external_mech (DBusAuth         *auth,
         return FALSE;
     }
 
-  desired_identity.pid = -1;
-  desired_identity.uid = -1;
-  desired_identity.gid = -1;
+  auth->desired_identity.pid = -1;
+  auth->desired_identity.uid = -1;
+  auth->desired_identity.gid = -1;
   
   /* If auth->identity is still empty here, then client
    * responded with an empty string after we poked it for
@@ -429,37 +1004,37 @@ handle_server_data_external_mech (DBusAuth         *auth,
    */
   if (_dbus_string_get_length (&auth->identity) == 0)
     {
-      desired_identity.uid = auth->credentials.uid;
+      auth->desired_identity.uid = auth->credentials.uid;
     }
   else
     {
       if (!_dbus_credentials_from_uid_string (&auth->identity,
-                                              &desired_identity))
+                                              &auth->desired_identity))
         {
           _dbus_verbose ("could not get credentials from uid string\n");
           return send_rejected (auth);
         }
     }
 
-  if (desired_identity.uid < 0)
+  if (auth->desired_identity.uid < 0)
     {
-      _dbus_verbose ("desired UID %d is no good\n", desired_identity.uid);
+      _dbus_verbose ("desired UID %d is no good\n", auth->desired_identity.uid);
       return send_rejected (auth);
     }
   
-  if (_dbus_credentials_match (&desired_identity,
+  if (_dbus_credentials_match (&auth->desired_identity,
                                &auth->credentials))
     {
-      /* client has authenticated */
-      _dbus_verbose ("authenticated client with UID %d matching socket credentials UID %d\n",
-                     desired_identity.uid,
-                     auth->credentials.uid);
-      
+      /* client has authenticated */      
       if (!_dbus_string_append (&auth->outgoing,
                                 "OK\r\n"))
         return FALSE;
 
-      auth->authorized_identity.uid = desired_identity.uid;
+      _dbus_verbose ("authenticated client with UID %d matching socket credentials UID %d\n",
+                     auth->desired_identity.uid,
+                     auth->credentials.uid);
+      
+      auth->authorized_identity.uid = auth->desired_identity.uid;
       
       auth->authenticated_pending_begin = TRUE;
       
@@ -469,7 +1044,7 @@ handle_server_data_external_mech (DBusAuth         *auth,
     {
       _dbus_verbose ("credentials uid=%d gid=%d do not allow uid=%d gid=%d\n",
                      auth->credentials.uid, auth->credentials.gid,
-                     desired_identity.uid, desired_identity.gid);
+                     auth->desired_identity.uid, auth->desired_identity.gid);
       return send_rejected (auth);
     }
 }
@@ -544,17 +1119,14 @@ all_mechanisms[] = {
     handle_client_data_external_mech,
     NULL, NULL,
     handle_client_shutdown_external_mech },
-  /* Obviously this has to die for production use */
-  { "DBUS_STUPID_TEST_MECH",
-    handle_server_data_stupid_test_mech,
-    handle_encode_stupid_test_mech,
-    handle_decode_stupid_test_mech,
-    handle_server_shutdown_stupid_test_mech,
-    NULL,
-    handle_client_data_stupid_test_mech,
-    handle_encode_stupid_test_mech,
-    handle_decode_stupid_test_mech,
-    handle_client_shutdown_stupid_test_mech },
+  { "DBUS_COOKIE_SHA1",
+    handle_server_data_cookie_sha1_mech,
+    NULL, NULL,
+    handle_server_shutdown_cookie_sha1_mech,
+    handle_client_initial_response_cookie_sha1_mech,
+    handle_client_data_cookie_sha1_mech,
+    NULL, NULL,
+    handle_client_shutdown_cookie_sha1_mech },
   { NULL, NULL }
 };
 
@@ -762,6 +1334,16 @@ process_data_server (DBusAuth         *auth,
           _dbus_string_free (&decoded);
           return FALSE;
         }
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+      if (_dbus_string_validate_ascii (&decoded, 0,
+                                       _dbus_string_get_length (&decoded)))
+        {
+          const char *s;
+          _dbus_string_get_const_data (&decoded, &s);
+          _dbus_verbose ("data: '%s'\n", s);
+        }
+#endif
       
       if (!(* auth->mech->server_data_func) (auth, &decoded))
         {
@@ -988,7 +1570,6 @@ process_ok (DBusAuth         *auth,
   return TRUE;
 }
 
-
 static dbus_bool_t
 process_data_client (DBusAuth         *auth,
                      const DBusString *command,
@@ -1006,6 +1587,16 @@ process_data_client (DBusAuth         *auth,
           _dbus_string_free (&decoded);
           return FALSE;
         }
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+      if (_dbus_string_validate_ascii (&decoded, 0,
+                                       _dbus_string_get_length (&decoded)))
+        {
+          const char *s;
+          _dbus_string_get_const_data (&decoded, &s);
+          _dbus_verbose ("data: '%s'\n", s);
+        }
+#endif
       
       if (!(* auth->mech->client_data_func) (auth, &decoded))
         {
@@ -1268,6 +1859,9 @@ _dbus_auth_unref (DBusAuth *auth)
           _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
         }
 
+      if (auth->keyring)
+        _dbus_keyring_unref (auth->keyring);
+      
       _dbus_string_free (&auth->identity);
       _dbus_string_free (&auth->incoming);
       _dbus_string_free (&auth->outgoing);
@@ -1383,6 +1977,12 @@ void
 _dbus_auth_bytes_sent (DBusAuth *auth,
                        int       bytes_sent)
 {
+  {
+    const char *s;
+    _dbus_string_get_const_data (&auth->outgoing, &s);
+    _dbus_verbose ("Sent %d bytes of: %s\n", bytes_sent, s);
+  }
+  
   _dbus_string_delete (&auth->outgoing,
                        0, bytes_sent);
   
@@ -1607,6 +2207,22 @@ _dbus_auth_get_identity (DBusAuth               *auth,
     }
 }
 
+/**
+ * Sets the "authentication context" which scopes cookies
+ * with the DBUS_COOKIE_SHA1 auth mechanism for example.
+ *
+ * @param auth the auth conversation
+ * @param context the context
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_auth_set_context (DBusAuth               *auth,
+                        const DBusString       *context)
+{
+  return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context),
+                                   &auth->context, 0, _dbus_string_get_length (context));
+}
+
 /** @} */
 
 #ifdef DBUS_BUILD_TESTS
index 7346f99..0339a4f 100644 (file)
@@ -68,6 +68,8 @@ void          _dbus_auth_set_credentials   (DBusAuth               *auth,
 
 void          _dbus_auth_get_identity      (DBusAuth               *auth,
                                             DBusCredentials        *credentials);
+dbus_bool_t   _dbus_auth_set_context       (DBusAuth               *auth,
+                                            const DBusString       *context);
 
 DBUS_END_DECLS;
 
index 134736d..3611b87 100644 (file)
@@ -1367,9 +1367,16 @@ dbus_connection_send_message_with_reply_and_block (DBusConnection     *connectio
 void
 dbus_connection_flush (DBusConnection *connection)
 {
+  /* We have to specify DBUS_ITERATION_DO_READING here
+   * because otherwise we could have two apps deadlock
+   * if they are both doing a flush(), and the kernel
+   * buffers fill up.
+   */
+  
   dbus_mutex_lock (connection->mutex);
   while (connection->n_outgoing > 0)
     _dbus_connection_do_iteration (connection,
+                                   DBUS_ITERATION_DO_READING |
                                    DBUS_ITERATION_DO_WRITING |
                                    DBUS_ITERATION_BLOCK,
                                    -1);
index 3aca9f1..e57d353 100644 (file)
@@ -200,6 +200,8 @@ dbus_set_error_const (DBusError  *error,
   /* it's a bug to pile up errors */
   _dbus_assert (error->name == NULL);
   _dbus_assert (error->message == NULL);
+  _dbus_assert (name != NULL);
+  _dbus_assert (message != NULL);
   
   real = (DBusRealError *)error;
   
@@ -209,6 +211,48 @@ dbus_set_error_const (DBusError  *error,
 }
 
 /**
+ * Checks whether the error is set and has the given
+ * name.
+ * @param error the error
+ * @param name the name
+ * @returns #TRUE if the given named error occurred
+ */
+dbus_bool_t
+dbus_error_has_name (const DBusError *error,
+                     const char      *name)
+{
+  _dbus_assert (error != NULL);
+  _dbus_assert (name != NULL);
+  _dbus_assert ((error->name != NULL && error->message != NULL) ||
+                (error->name == NULL && error->message == NULL));
+  
+  if (error->name != NULL)
+    {
+      DBusString str1, str2;
+      _dbus_string_init_const (&str1, error->name);
+      _dbus_string_init_const (&str2, name);
+      return _dbus_string_equal (&str1, &str2);
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * Checks whether an error occurred (the error is set).
+ *
+ * @param error the error object
+ * @returns #TRUE if an error occurred
+ */
+dbus_bool_t
+dbus_error_is_set (const DBusError *error)
+{
+  _dbus_assert (error != NULL);
+  _dbus_assert ((error->name != NULL && error->message != NULL) ||
+                (error->name == NULL && error->message == NULL));
+  return error->name != NULL;
+}
+
+/**
  * Assigns an error name and message to a DBusError.
  * Does nothing if error is #NULL.
  *
@@ -240,6 +284,8 @@ dbus_set_error (DBusError  *error,
   /* it's a bug to pile up errors */
   _dbus_assert (error->name == NULL);
   _dbus_assert (error->message == NULL);
+  _dbus_assert (name != NULL);
+  _dbus_assert (format != NULL);
   
   va_start (args, format);
   /* Measure the message length */
index 1b1a880..e6b8846 100644 (file)
@@ -81,16 +81,20 @@ typedef enum
   DBUS_RESULT_FILE_NOT_FOUND   /**< File doesn't exist */
 } DBusResultCode;
 
-void        dbus_error_init      (DBusError  *error);
-void        dbus_error_free      (DBusError  *error);
-void        dbus_set_error       (DBusError  *error,
-                                 const char *name,
-                                 const char *message,
-                                 ...);
-void        dbus_set_error_const (DBusError  *error,
-                                 const char *name,
-                                 const char *message); 
-                                  
+void        dbus_error_init      (DBusError       *error);
+void        dbus_error_free      (DBusError       *error);
+void        dbus_set_error       (DBusError       *error,
+                                  const char      *name,
+                                  const char      *message,
+                                  ...);
+void        dbus_set_error_const (DBusError       *error,
+                                  const char      *name,
+                                  const char      *message);
+dbus_bool_t dbus_error_has_name  (const DBusError *error,
+                                  const char      *name);
+dbus_bool_t dbus_error_is_set    (const DBusError *error);
+
+
 void        dbus_set_result       (DBusResultCode *code_address,
                                    DBusResultCode  code);
 const char* dbus_result_to_string (DBusResultCode  code);
index d4aae2f..c5c6a0b 100644 (file)
@@ -92,6 +92,7 @@ typedef struct
 struct DBusKeyring
 {
   int refcount;             /**< Reference count */
+  DBusString username;      /**< Username keyring is for */
   DBusString directory;     /**< Directory the below two items are inside */
   DBusString filename;      /**< Keyring filename */
   DBusString filename_lock; /**< Name of lockfile */
@@ -117,12 +118,17 @@ _dbus_keyring_new (void)
   if (!_dbus_string_init (&keyring->filename_lock, _DBUS_INT_MAX))
     goto out_3;
 
+  if (!_dbus_string_init (&keyring->username, _DBUS_INT_MAX))
+    goto out_4;
+  
   keyring->refcount = 1;
   keyring->keys = NULL;
   keyring->n_keys = 0;
 
   return keyring;
-  
+
+ out_4:
+  _dbus_string_free (&keyring->username);
  out_3:
   _dbus_string_free (&keyring->filename);
  out_2:
@@ -614,6 +620,7 @@ _dbus_keyring_reload (DBusKeyring *keyring,
       i = 0;
       while (i < n_keys)
         {
+          _dbus_string_zero (&keys[i].secret);
           _dbus_string_free (&keys[i].secret);
           ++i;
         }
@@ -659,6 +666,7 @@ _dbus_keyring_unref (DBusKeyring *keyring)
 
   if (keyring->refcount == 0)
     {
+      _dbus_string_free (&keyring->username);
       _dbus_string_free (&keyring->filename);
       _dbus_string_free (&keyring->filename_lock);
       _dbus_string_free (&keyring->directory);
@@ -715,6 +723,8 @@ _dbus_keyring_new_homedir (const DBusString *username,
         goto failed;
     }
 
+  _dbus_assert (username != NULL);    
+  
   keyring = _dbus_keyring_new ();
   if (keyring == NULL)
     goto failed;
@@ -728,7 +738,11 @@ _dbus_keyring_new_homedir (const DBusString *username,
                             "Invalid context in keyring creation");
       goto failed;
     }
-      
+
+  if (!_dbus_string_copy (username, 0,
+                          &keyring->username, 0))
+    goto failed;
+  
   if (!_dbus_string_copy (&homedir, 0,
                           &keyring->directory, 0))
     goto failed;
@@ -795,6 +809,9 @@ _dbus_keyring_new_homedir (const DBusString *username,
  * in filenames are not allowed (contexts can't
  * start with a dot or contain dir separators).
  *
+ * @todo this is the most inefficient implementation
+ * imaginable.
+ *
  * @param context the context
  * @returns #TRUE if valid
  */
@@ -836,6 +853,25 @@ _dbus_keyring_validate_context (const DBusString *context)
       return FALSE;
     }
 
+  /* no spaces/tabs, those are used for separators in the protocol */
+  if (_dbus_string_find_blank (context, 0, NULL))
+    {
+      _dbus_verbose ("context contains a blank\n");
+      return FALSE;
+    }
+
+  if (_dbus_string_find (context, 0, "\n", NULL))
+    {
+      _dbus_verbose ("context contains a newline\n");
+      return FALSE;
+    }
+
+  if (_dbus_string_find (context, 0, "\r", NULL))
+    {
+      _dbus_verbose ("context contains a carriage return\n");
+      return FALSE;
+    }
+  
   return TRUE;
 }
 
@@ -904,6 +940,51 @@ _dbus_keyring_get_best_key (DBusKeyring  *keyring,
     }
 }
 
+/**
+ * Checks whether the keyring is for the given username.
+ *
+ * @param keyring the keyring
+ * @param username the username to check
+ *
+ * @returns #TRUE if the keyring belongs to the given user
+ */
+dbus_bool_t
+_dbus_keyring_is_for_user (DBusKeyring       *keyring,
+                           const DBusString  *username)
+{
+  return _dbus_string_equal (&keyring->username,
+                             username);
+}
+
+/**
+ * Gets the hex-encoded secret key for the given ID.
+ * Returns #FALSE if not enough memory. Returns #TRUE
+ * but empty key on any other error such as unknown
+ * key ID.
+ *
+ * @param keyring the keyring
+ * @param key_id the key ID
+ * @param hex_key string to append hex-encoded key to
+ * @returns #TRUE if we had enough memory
+ */
+dbus_bool_t
+_dbus_keyring_get_hex_key (DBusKeyring       *keyring,
+                           int                key_id,
+                           DBusString        *hex_key)
+{
+  DBusKey *key;
+
+  key = find_key_by_id (keyring->keys,
+                        keyring->n_keys,
+                        key_id);
+  if (key == NULL)
+    return TRUE; /* had enough memory, so TRUE */
+
+  return _dbus_string_hex_encode (&key->secret, 0,
+                                  hex_key,
+                                  _dbus_string_get_length (hex_key));
+}
+
 /** @} */ /* end of exposed API */
 
 #ifdef DBUS_BUILD_TESTS
@@ -946,6 +1027,8 @@ _dbus_keyring_test (void)
   _dbus_assert (!_dbus_keyring_validate_context (&context));
   _dbus_string_init_const (&context, "foo\x7f");
   _dbus_assert (_dbus_keyring_validate_context (&context));
+  _dbus_string_init_const (&context, "foo bar");
+  _dbus_assert (!_dbus_keyring_validate_context (&context));
   
   if (!_dbus_string_init (&context, _DBUS_INT_MAX))
     _dbus_assert_not_reached ("no memory");
index 28826ae..2a450ec 100644 (file)
@@ -39,16 +39,11 @@ 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,
+dbus_bool_t  _dbus_keyring_is_for_user      (DBusKeyring       *keyring,
+                                             const DBusString  *username);
+dbus_bool_t  _dbus_keyring_get_hex_key      (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);
-
+                                             DBusString        *hex_key);
 
 DBUS_END_DECLS;
 
index e082311..db1b94b 100644 (file)
@@ -148,7 +148,7 @@ clear_header_padding (DBusMessage *message)
   _dbus_string_shorten (&message->header,
                         message->header_padding);
   message->header_padding = 0;
-}                      
+}              
 
 static dbus_bool_t
 append_header_padding (DBusMessage *message)
@@ -2653,7 +2653,9 @@ _dbus_message_loader_return_buffer (DBusMessageLoader  *loader,
           int i;
           int next_arg;          
 
+#if 0
          _dbus_verbose_bytes_of_string (&loader->data, 0, header_len);
+#endif
          if (!decode_header_data (&loader->data, header_len, byte_order,
                                    fields, &header_padding))
            {
index 2310b00..d096ce3 100644 (file)
@@ -1107,14 +1107,17 @@ get_user_info (const DBusString *username,
                DBusString       *username_out)
 {
   const char *username_c_str;
-  
-  credentials->pid = -1;
-  credentials->uid = -1;
-  credentials->gid = -1;
-
+      
   /* exactly one of username/uid provided */
   _dbus_assert (username != NULL || uid >= 0);
   _dbus_assert (username == NULL || uid < 0);
+
+  if (credentials)
+    {
+      credentials->pid = -1;
+      credentials->uid = -1;
+      credentials->gid = -1;
+    }
   
   if (username != NULL)
     _dbus_string_get_const_data (username, &username_c_str);
index 29ad855..9da24ba 100644 (file)
@@ -66,11 +66,13 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir)
   printf ("%s: running keyring tests\n", "dbus-test");
   if (!_dbus_keyring_test ())
     die ("keyring");
-  
+
+#if 0
   printf ("%s: running md5 tests\n", "dbus-test");
   if (!_dbus_md5_test ())
     die ("md5");
-
+#endif
+  
   printf ("%s: running SHA-1 tests\n", "dbus-test");
   if (!_dbus_sha_test (test_data_dir))
     die ("SHA-1");
index 48cc086..0845d80 100644 (file)
       <para>
         The credentials sent along with the nul byte may be used with the 
         SASL mechanism EXTERNAL.
-
       </para>
     </sect2>
     <sect2 id="auth-command-auth">
        </figure>
       </para>
     </sect2>
+    <sect2 id="auth-mechanisms">
+      <title>Authentication mechanisms</title>
+      <para>
+        This section describes some new authentication mechanisms.
+        D-BUS also allows any standard SASL mechanism of course.
+      </para>
+      <sect3 id="auth-mechanisms-sha">
+        <title>DBUS_COOKIE_SHA1</title>
+        <para>
+          The DBUS_COOKIE_SHA1 mechanism is designed to establish that a client
+          has the ability to read a private file owned by the user being
+          authenticated. If the client can prove that it has access to a secret
+          cookie stored in this file, then the client is authenticated. 
+          Thus the security of DBUS_COOKIE_SHA1 depends on a secure home 
+          directory.
+        </para>
+        <para>
+          Authentication proceeds as follows:
+          <itemizedlist>
+            <listitem>
+              <para>
+                The client sends the username it would like to authenticate 
+                as.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                The server sends the name of its "cookie context" (see below); a
+                space character; the integer ID of the secret cookie the client
+                must demonstrate knowledge of; a space character; then a
+                hex-encoded randomly-generated challenge string.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                The client locates the cookie, and generates its own hex-encoded
+                randomly-generated challenge string.  The client then
+                concatentates the server's hex-encoded challenge, a ":"
+                character, its own hex-encoded challenge, another ":" character,
+                and the hex-encoded cookie.  It computes the SHA-1 hash of this
+                composite string.  It sends back to the server the client's
+                hex-encoded challenge string, a space character, and the SHA-1
+                hash.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                The server generates the same concatenated string used by the
+                client and computes its SHA-1 hash. It compares the hash with
+                the hash received from the client; if the two hashes match, the
+                client is authenticated.
+              </para>
+            </listitem>
+          </itemizedlist>
+        </para>
+        <para>
+          Each server has a "cookie context," which is a name that identifies a
+          set of cookies that apply to that server. A sample context might be
+          "org_freedesktop_session_bus". Context names must be valid ASCII,
+          nonzero length, and may not contain the characters slash ("/"),
+          backslash ("\"), space (" "), newline ("\n"), carriage return ("\r"),
+          tab ("\t"), or period ("."). There is a default context,
+          "org_freedesktop_global" that's used by servers that do not specify
+          otherwise.
+        </para>
+        <para>
+          Cookies are stored in a user's home directory, in the directory
+          <filename>~/.dbus-keyrings/</filename>. This directory must 
+          not be readable or writable by other users. If it is, 
+          clients and servers must ignore it. The directory 
+          contains cookie files named after the cookie context.
+        </para>
+        <para>
+          A cookie file contains one cookie per line. Each line 
+          has three space-separated fields:
+          <itemizedlist>
+            <listitem>
+              <para>
+                The cookie ID number, which must be a non-negative integer and
+                may not be used twice in the same file.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                The cookie's creation time, in UNIX seconds-since-the-epoch
+                format.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                The cookie itself, a hex-encoded random block of bytes.
+              </para>
+            </listitem>
+          </itemizedlist>
+        </para>
+        <para>
+          Only server processes modify the cookie file. 
+          They must do so with this procedure:
+          <itemizedlist>
+            <listitem>
+              <para>
+                Create a lockfile name by appending ".lock" to the name of the
+                cookie file.  The server should attempt to create this file
+                using <literal>O_CREAT | O_EXCL</literal>.  If file creation
+                fails, the lock fails. Servers should retry for a reasonable
+                period of time, then they may choose to delete an existing lock
+                to keep users from having to manually delete a stale
+                lock. <footnote><para>Lockfiles are used instead of real file
+                locking <literal>fcntl()</literal> because real locking
+                implementations are still flaky on network
+                filesystems.</para></footnote>
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                Once the lockfile has been created, the server loads the cookie
+                file. It should then delete any cookies that are old (the
+                timeout can be fairly short), or more than a reasonable
+                time in the future (so that cookies never accidentally 
+                become permanent, if the clock was set far into the future 
+                at some point). If no recent keys remain, the 
+                server may generate a new key.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                The pruned and possibly added-to cookie file 
+                must be resaved atomically (using a temporary 
+                file which is rename()'d).
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                The lock must be dropped by deleting the lockfile.
+              </para>
+            </listitem>
+          </itemizedlist>
+        </para>
+        <para>
+          Clients need not lock the file in order to load it, 
+          because servers are required to save the file atomically.          
+        </para>
+      </sect3>
+    </sect2>
   </sect1>
   <sect1 id="addresses">
     <title>Server Addresses</title>
index f572352..8f0d1aa 100644 (file)
@@ -2,7 +2,7 @@
 
 SERVER
 NO_CREDENTIALS
-SEND 'AUTH EXTERNAL USERNAME_BASE64'
+SEND 'AUTH EXTERNAL USERID_BASE64'
 EXPECT_COMMAND REJECTED
 EXPECT_STATE WAITING_FOR_INPUT
 
index 720b7e5..c126067 100644 (file)
@@ -2,7 +2,7 @@
 
 SERVER
 ROOT_CREDENTIALS
-SEND 'AUTH EXTERNAL USERNAME_BASE64'
+SEND 'AUTH EXTERNAL USERID_BASE64'
 EXPECT_COMMAND OK
 EXPECT_STATE WAITING_FOR_INPUT
 SEND 'BEGIN'
index 3e12c15..cbfef8f 100644 (file)
@@ -2,7 +2,7 @@
 
 SERVER
 SILLY_CREDENTIALS
-SEND 'AUTH EXTERNAL USERNAME_BASE64'
+SEND 'AUTH EXTERNAL USERID_BASE64'
 EXPECT_COMMAND REJECTED
 EXPECT_STATE WAITING_FOR_INPUT
 
index 1b38080..02ecdc0 100644 (file)
@@ -1,7 +1,7 @@
 ## this tests a successful auth of type EXTERNAL
 
 SERVER
-SEND 'AUTH EXTERNAL USERNAME_BASE64'
+SEND 'AUTH EXTERNAL USERID_BASE64'
 EXPECT_COMMAND OK
 EXPECT_STATE WAITING_FOR_INPUT
 SEND 'BEGIN'
index df8df52..0bd705d 100644 (file)
@@ -1,7 +1,7 @@
 ## this tests that we have the expected extra bytes at the end
 
 SERVER
-SEND 'AUTH EXTERNAL USERNAME_BASE64'
+SEND 'AUTH EXTERNAL USERID_BASE64'
 EXPECT_COMMAND OK
 EXPECT_STATE WAITING_FOR_INPUT
 SEND 'BEGIN\r\nHello'
index 75bd380..ae3b907 100644 (file)
@@ -4,30 +4,30 @@ SERVER
 NO_CREDENTIALS
 
 # 1
-SEND 'AUTH EXTERNAL USERNAME_BASE64'
+SEND 'AUTH EXTERNAL USERID_BASE64'
 EXPECT_COMMAND REJECTED
 EXPECT_STATE WAITING_FOR_INPUT
 
 # 2
-SEND 'AUTH EXTERNAL USERNAME_BASE64'
+SEND 'AUTH EXTERNAL USERID_BASE64'
 EXPECT_COMMAND REJECTED
 EXPECT_STATE WAITING_FOR_INPUT
 
 # 3
-SEND 'AUTH EXTERNAL USERNAME_BASE64'
+SEND 'AUTH EXTERNAL USERID_BASE64'
 EXPECT_COMMAND REJECTED
 EXPECT_STATE WAITING_FOR_INPUT
 
 # 4
-SEND 'AUTH EXTERNAL USERNAME_BASE64'
+SEND 'AUTH EXTERNAL USERID_BASE64'
 EXPECT_COMMAND REJECTED
 EXPECT_STATE WAITING_FOR_INPUT
 
 # 5
-SEND 'AUTH EXTERNAL USERNAME_BASE64'
+SEND 'AUTH EXTERNAL USERID_BASE64'
 EXPECT_COMMAND REJECTED
 EXPECT_STATE WAITING_FOR_INPUT
 
 # 6
-SEND 'AUTH EXTERNAL USERNAME_BASE64'
+SEND 'AUTH EXTERNAL USERID_BASE64'
 EXPECT_STATE NEED_DISCONNECT
index 1b8c40f..ebe7ae2 100644 (file)
@@ -5,17 +5,18 @@ CLIENT
 ## Will try EXTERNAL by default first without first calling AUTH alone.
 
 EXPECT_COMMAND AUTH
-SEND 'REJECTED EXTERNAL DBUS_STUPID_TEST_MECH'
+SEND 'REJECTED EXTERNAL DBUS_COOKIE_SHA1 DBUS_TEST_NONEXISTENT_MECH'
 
 ## Will try EXTERNAL again.
 
 EXPECT_COMMAND AUTH
-SEND 'REJECTED EXTERNAL DBUS_STUPID_TEST_MECH'
+SEND 'REJECTED EXTERNAL DBUS_COOKIE_SHA1 DBUS_TEST_NONEXISTENT_MECH'
 
-## And this time we get DBUS_STUPID_TEST_MECH.
+## And this time we get DBUS_COOKIE_SHA1
 
 EXPECT_COMMAND AUTH
+## of course real DBUS_COOKIE_SHA1 would not send this here...
 SEND 'OK'
 
 EXPECT_COMMAND BEGIN
-EXPECT_STATE AUTHENTICATED 
+EXPECT_STATE AUTHENTICATED