Merge "Optional autogen.sh flag --enable-kdbus-transport added allowing to compile...
[platform/upstream/dbus.git] / dbus / dbus-auth.c
index 8125a8e..86e3223 100644 (file)
@@ -1,9 +1,10 @@
-/* -*- mode: C; c-file-style: "gnu" -*- */
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 /* dbus-auth.c Authentication
  *
- * Copyright (C) 2002, 2003 Red Hat Inc.
+ * Copyright (C) 2002, 2003, 2004 Red Hat Inc.
+ * Copyright (C) 2013  Samsung Electronics
  *
- * Licensed under the Academic Free License version 1.2
+ * Licensed under the Academic Free License version 2.1
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * 
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
+
+#include <config.h>
 #include "dbus-auth.h"
 #include "dbus-string.h"
 #include "dbus-list.h"
 #include "dbus-internals.h"
-
-/* See doc/dbus-sasl-profile.txt */
+#include "dbus-keyring.h"
+#include "dbus-sha.h"
+#include "dbus-protocol.h"
+#include "dbus-credentials.h"
 
 /**
  * @defgroup DBusAuth Authentication
  * is first established, and also manage any encryption used over a
  * connection.
  *
- * The file doc/dbus-sasl-profile.txt documents the network protocol
- * used for authentication.
- *
  * @todo some SASL profiles require sending the empty string as a
  * challenge/response, but we don't currently allow that in our
  * protocol.
+ *
+ * @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
  */
 
 /**
  */
 
 /**
- * Processes a command. Returns whether we had enough memory to
- * complete the operation.
- */
-typedef dbus_bool_t (* DBusProcessAuthCommandFunction) (DBusAuth         *auth,
-                                                        const DBusString *command,
-                                                        const DBusString *args);
-
-typedef struct
-{
-  const char *command;
-  DBusProcessAuthCommandFunction func;
-} DBusAuthCommandHandler;
-
-/**
  * This function appends an initial client response to the given string
  */
 typedef dbus_bool_t (* DBusInitialResponseFunction)  (DBusAuth         *auth,
@@ -100,31 +97,69 @@ typedef dbus_bool_t (* DBusAuthDecodeFunction)   (DBusAuth         *auth,
  */
 typedef void        (* DBusAuthShutdownFunction) (DBusAuth       *auth);
 
+/**
+ * Virtual table representing a particular auth mechanism.
+ */
 typedef struct
 {
-  const char *mechanism;
-  DBusAuthDataFunction server_data_func;
-  DBusAuthEncodeFunction server_encode_func;
-  DBusAuthDecodeFunction server_decode_func;
-  DBusAuthShutdownFunction server_shutdown_func;
-  DBusInitialResponseFunction client_initial_response_func;
-  DBusAuthDataFunction client_data_func;
-  DBusAuthEncodeFunction client_encode_func;
-  DBusAuthDecodeFunction client_decode_func;
-  DBusAuthShutdownFunction client_shutdown_func;
+  const char *mechanism; /**< Name of the mechanism */
+  DBusAuthDataFunction server_data_func; /**< Function on server side for DATA */
+  DBusAuthEncodeFunction server_encode_func; /**< Function on server side to encode */
+  DBusAuthDecodeFunction server_decode_func; /**< Function on server side to decode */
+  DBusAuthShutdownFunction server_shutdown_func; /**< Function on server side to shut down */
+  DBusInitialResponseFunction client_initial_response_func; /**< Function on client side to handle initial response */
+  DBusAuthDataFunction client_data_func; /**< Function on client side for DATA */
+  DBusAuthEncodeFunction client_encode_func; /**< Function on client side for encode */
+  DBusAuthDecodeFunction client_decode_func; /**< Function on client side for decode */
+  DBusAuthShutdownFunction client_shutdown_func; /**< Function on client side for shutdown */
 } DBusAuthMechanismHandler;
 
 /**
+ * Enumeration for the known authentication commands.
+ */
+typedef enum {
+  DBUS_AUTH_COMMAND_AUTH,
+  DBUS_AUTH_COMMAND_CANCEL,
+  DBUS_AUTH_COMMAND_DATA,
+  DBUS_AUTH_COMMAND_BEGIN,
+  DBUS_AUTH_COMMAND_REJECTED,
+  DBUS_AUTH_COMMAND_OK,
+  DBUS_AUTH_COMMAND_ERROR,
+  DBUS_AUTH_COMMAND_UNKNOWN,
+  DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD,
+  DBUS_AUTH_COMMAND_AGREE_UNIX_FD
+} DBusAuthCommand;
+
+/**
+ * Auth state function, determines the reaction to incoming events for
+ * a particular state. Returns whether we had enough memory to
+ * complete the operation.
+ */
+typedef dbus_bool_t (* DBusAuthStateFunction) (DBusAuth         *auth,
+                                               DBusAuthCommand   command,
+                                               const DBusString *args);
+
+/**
+ * Information about a auth state.
+ */
+typedef struct
+{
+  const char *name;               /**< Name of the state */
+  DBusAuthStateFunction handler;  /**< State function for this state */
+} DBusAuthStateData;
+
+/**
  * Internal members of DBusAuth.
  */
 struct DBusAuth
 {
   int refcount;           /**< reference count */
+  const char *side;       /**< Client or server */
 
   DBusString incoming;    /**< Incoming data buffer */
   DBusString outgoing;    /**< Outgoing data buffer */
   
-  const DBusAuthCommandHandler *handlers; /**< Handlers for commands */
+  const DBusAuthStateData *state;         /**< Current protocol state */
 
   const DBusAuthMechanismHandler *mech;   /**< Current auth mechanism */
 
@@ -132,99 +167,156 @@ struct DBusAuth
                                           *   as.
                                           */
   
-  DBusCredentials credentials;      /**< Credentials, fields may be -1 */
+  DBusCredentials *credentials;          /**< Credentials read from socket
+                                          */
+
+  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 */
 
-  DBusCredentials authorized_identity; /**< Credentials that are authorized */
+  char **allowed_mechs;             /**< Mechanisms we're allowed to use,
+                                     * or #NULL if we can use any
+                                     */
   
   unsigned int needed_memory : 1;   /**< We needed memory to continue since last
                                      * successful getting something done
                                      */
-  unsigned int need_disconnect : 1; /**< We've given up, time to disconnect */
-  unsigned int authenticated : 1;   /**< We are authenticated */
-  unsigned int authenticated_pending_output : 1; /**< Authenticated once we clear outgoing buffer */
-  unsigned int authenticated_pending_begin : 1;  /**< Authenticated once we get BEGIN */
   unsigned int already_got_mechanisms : 1;       /**< Client already got mech list */
   unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */
+  unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */
+
+  unsigned int unix_fd_possible : 1;  /**< This side could do unix fd passing */
+  unsigned int unix_fd_negotiated : 1; /**< Unix fd was successfully negotiated */
 };
 
+/**
+ * "Subclass" of DBusAuth for client side
+ */
 typedef struct
 {
-  DBusAuth base;
+  DBusAuth base;    /**< Parent class */
 
   DBusList *mechs_to_try; /**< Mechanisms we got from the server that we're going to try using */
+
+  DBusString guid_from_server; /**< GUID received from server */
   
 } DBusAuthClient;
 
+/**
+ * "Subclass" of DBusAuth for server side.
+ */
 typedef struct
 {
-  DBusAuth base;
+  DBusAuth base;    /**< Parent class */
 
   int failures;     /**< Number of times client has been rejected */
   int max_failures; /**< Number of times we reject before disconnect */
+
+  DBusString guid;  /**< Our globally unique ID in hex encoding */
   
 } DBusAuthServer;
 
-static dbus_bool_t process_auth         (DBusAuth         *auth,
-                                         const DBusString *command,
-                                         const DBusString *args);
-static dbus_bool_t process_cancel       (DBusAuth         *auth,
-                                         const DBusString *command,
-                                         const DBusString *args);
-static dbus_bool_t process_begin        (DBusAuth         *auth,
-                                         const DBusString *command,
-                                         const DBusString *args);
-static dbus_bool_t process_data_server  (DBusAuth         *auth,
-                                         const DBusString *command,
-                                         const DBusString *args);
-static dbus_bool_t process_error_server (DBusAuth         *auth,
-                                         const DBusString *command,
-                                         const DBusString *args);
-static dbus_bool_t process_rejected     (DBusAuth         *auth,
-                                         const DBusString *command,
-                                         const DBusString *args);
-static dbus_bool_t process_ok           (DBusAuth         *auth,
-                                         const DBusString *command,
-                                         const DBusString *args);
-static dbus_bool_t process_data_client  (DBusAuth         *auth,
-                                         const DBusString *command,
-                                         const DBusString *args);
-static dbus_bool_t process_error_client (DBusAuth         *auth,
-                                         const DBusString *command,
-                                         const DBusString *args);
-
-
-static dbus_bool_t client_try_next_mechanism (DBusAuth *auth);
+static void        goto_state                (DBusAuth                       *auth,
+                                              const DBusAuthStateData        *new_state);
+static dbus_bool_t send_auth                 (DBusAuth *auth,
+                                              const DBusAuthMechanismHandler *mech);
+static dbus_bool_t send_data                 (DBusAuth *auth,
+                                              DBusString *data);
 static dbus_bool_t send_rejected             (DBusAuth *auth);
+static dbus_bool_t send_error                (DBusAuth *auth,
+                                              const char *message);
+static dbus_bool_t send_ok                   (DBusAuth *auth);
+static dbus_bool_t send_begin                (DBusAuth *auth);
+static dbus_bool_t send_cancel               (DBusAuth *auth);
+static dbus_bool_t send_negotiate_unix_fd    (DBusAuth *auth);
+static dbus_bool_t send_agree_unix_fd        (DBusAuth *auth);
 
-static DBusAuthCommandHandler
-server_handlers[] = {
-  { "AUTH", process_auth },
-  { "CANCEL", process_cancel },
-  { "BEGIN", process_begin },
-  { "DATA", process_data_server },
-  { "ERROR", process_error_server },
-  { NULL, NULL }
+/**
+ * Client states
+ */
+static dbus_bool_t handle_server_state_waiting_for_auth  (DBusAuth         *auth,
+                                                          DBusAuthCommand   command,
+                                                          const DBusString *args);
+static dbus_bool_t handle_server_state_waiting_for_data  (DBusAuth         *auth,
+                                                          DBusAuthCommand   command,
+                                                          const DBusString *args);
+static dbus_bool_t handle_server_state_waiting_for_begin (DBusAuth         *auth,
+                                                          DBusAuthCommand   command,
+                                                          const DBusString *args);
+  
+static const DBusAuthStateData server_state_waiting_for_auth = {
+  "WaitingForAuth", handle_server_state_waiting_for_auth
+};
+static const DBusAuthStateData server_state_waiting_for_data = {
+  "WaitingForData", handle_server_state_waiting_for_data
+};
+static const DBusAuthStateData server_state_waiting_for_begin = {
+  "WaitingForBegin", handle_server_state_waiting_for_begin
+};
+  
+/**
+ * Client states
+ */
+static dbus_bool_t handle_client_state_waiting_for_data   (DBusAuth         *auth,
+                                                           DBusAuthCommand   command,
+                                                           const DBusString *args);
+static dbus_bool_t handle_client_state_waiting_for_ok     (DBusAuth         *auth,
+                                                           DBusAuthCommand   command,
+                                                           const DBusString *args);
+static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth         *auth,
+                                                           DBusAuthCommand   command,
+                                                           const DBusString *args);
+static dbus_bool_t handle_client_state_waiting_for_agree_unix_fd (DBusAuth         *auth,
+                                                           DBusAuthCommand   command,
+                                                           const DBusString *args);
+
+static const DBusAuthStateData client_state_need_send_auth = {
+  "NeedSendAuth", NULL
+};
+static const DBusAuthStateData client_state_waiting_for_data = {
+  "WaitingForData", handle_client_state_waiting_for_data
+};
+static const DBusAuthStateData client_state_waiting_for_ok = {
+  "WaitingForOK", handle_client_state_waiting_for_ok
+};
+static const DBusAuthStateData client_state_waiting_for_reject = {
+  "WaitingForReject", handle_client_state_waiting_for_reject
+};
+static const DBusAuthStateData client_state_waiting_for_agree_unix_fd = {
+  "WaitingForAgreeUnixFD", handle_client_state_waiting_for_agree_unix_fd
 };
 
-static DBusAuthCommandHandler
-client_handlers[] = {
-  { "REJECTED", process_rejected },
-  { "OK", process_ok },
-  { "DATA", process_data_client },
-  { "ERROR", process_error_client },
-  { NULL, NULL }
+/**
+ * Common terminal states.  Terminal states have handler == NULL.
+ */
+
+static const DBusAuthStateData common_state_authenticated = {
+  "Authenticated",  NULL
 };
 
+static const DBusAuthStateData common_state_need_disconnect = {
+  "NeedDisconnect",  NULL
+};
+
+static const char auth_side_client[] = "client";
+static const char auth_side_server[] = "server";
 /**
  * @param auth the auth conversation
  * @returns #TRUE if the conversation is the server side
  */
-#define DBUS_AUTH_IS_SERVER(auth) ((auth)->handlers == server_handlers)
+#define DBUS_AUTH_IS_SERVER(auth) ((auth)->side == auth_side_server)
 /**
  * @param auth the auth conversation
  * @returns #TRUE if the conversation is the client side
  */
-#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->handlers == client_handlers)
+#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->side == auth_side_client)
 /**
  * @param auth the auth conversation
  * @returns auth cast to DBusAuthClient
@@ -236,6 +328,13 @@ client_handlers[] = {
  */
 #define DBUS_AUTH_SERVER(auth)    ((DBusAuthServer*)(auth))
 
+/**
+ * The name of the auth ("client" or "server")
+ * @param auth the auth conversation
+ * @returns a string
+ */
+#define DBUS_AUTH_NAME(auth)      ((auth)->side)
+
 static DBusAuth*
 _dbus_auth_new (int size)
 {
@@ -246,14 +345,9 @@ _dbus_auth_new (int size)
     return NULL;
   
   auth->refcount = 1;
-
-  auth->credentials.pid = -1;
-  auth->credentials.uid = -1;
-  auth->credentials.gid = -1;
-
-  auth->authorized_identity.pid = -1;
-  auth->authorized_identity.uid = -1;
-  auth->authorized_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
@@ -263,46 +357,78 @@ _dbus_auth_new (int size)
    * overlong buffers in _dbus_auth_do_work().
    */
   
-  if (!_dbus_string_init (&auth->incoming, _DBUS_INT_MAX))
-    {
-      dbus_free (auth);
-      return NULL;
-    }
+  if (!_dbus_string_init (&auth->incoming))
+    goto enomem_0;
 
-  if (!_dbus_string_init (&auth->outgoing, _DBUS_INT_MAX))
-    {
-      _dbus_string_free (&auth->incoming);
-      dbus_free (auth);
-      return NULL;
-    }
+  if (!_dbus_string_init (&auth->outgoing))
+    goto enomem_1;
+    
+  if (!_dbus_string_init (&auth->identity))
+    goto enomem_2;
+
+  if (!_dbus_string_init (&auth->context))
+    goto enomem_3;
+
+  if (!_dbus_string_init (&auth->challenge))
+    goto enomem_4;
+
+  /* default context if none is specified */
+  if (!_dbus_string_append (&auth->context, "org_freedesktop_general"))
+    goto enomem_5;
+
+  auth->credentials = _dbus_credentials_new ();
+  if (auth->credentials == NULL)
+    goto enomem_6;
   
-  if (!_dbus_string_init (&auth->identity, _DBUS_INT_MAX))
-    {
-      _dbus_string_free (&auth->incoming);
-      _dbus_string_free (&auth->outgoing);
-      dbus_free (auth);
-      return NULL;
-    }
+  auth->authorized_identity = _dbus_credentials_new ();
+  if (auth->authorized_identity == NULL)
+    goto enomem_7;
+
+  auth->desired_identity = _dbus_credentials_new ();
+  if (auth->desired_identity == NULL)
+    goto enomem_8;
   
   return auth;
+
+#if 0
+ enomem_9:
+  _dbus_credentials_unref (auth->desired_identity);
+#endif
+ enomem_8:
+  _dbus_credentials_unref (auth->authorized_identity);
+ enomem_7:
+  _dbus_credentials_unref (auth->credentials);
+ enomem_6:
+ /* last alloc was an append to context, which is freed already below */ ;
+ 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->outgoing);
+ enomem_1:
+  _dbus_string_free (&auth->incoming);
+ enomem_0:
+  dbus_free (auth);
+  return NULL;
 }
 
 static void
 shutdown_mech (DBusAuth *auth)
 {
   /* Cancel any auth */
-  auth->authenticated_pending_begin = FALSE;
-  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;
+
+  _dbus_credentials_clear (auth->authorized_identity);
+  _dbus_credentials_clear (auth->desired_identity);
   
   if (auth->mech != NULL)
     {
-      _dbus_verbose ("Shutting down mechanism %s\n",
-                     auth->mech->mechanism);
+      _dbus_verbose ("%s: Shutting down mechanism %s\n",
+                     DBUS_AUTH_NAME (auth), auth->mech->mechanism);
       
       if (DBUS_AUTH_IS_CLIENT (auth))
         (* auth->mech->client_shutdown_func) (auth);
@@ -313,84 +439,106 @@ shutdown_mech (DBusAuth *auth)
     }
 }
 
+/*
+ * DBUS_COOKIE_SHA1 mechanism
+ */
+
+/* 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"))
-    return FALSE;
+  DBusString cookie;
+  DBusString to_hash;
+  dbus_bool_t retval;
+  
+  _dbus_assert (auth->keyring != NULL);
 
-  auth->authenticated_pending_begin = TRUE;
+  retval = FALSE;
   
-  return TRUE;
-}
+  if (!_dbus_string_init (&cookie))
+    return FALSE;
 
-static void
-handle_server_shutdown_stupid_test_mech (DBusAuth *auth)
-{
+  if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id,
+                                  &cookie))
+    goto out_0;
 
-}
+  if (_dbus_string_get_length (&cookie) == 0)
+    {
+      retval = TRUE;
+      goto out_0;
+    }
 
-static dbus_bool_t
-handle_client_data_stupid_test_mech (DBusAuth         *auth,
-                                     const DBusString *data)
-{
+  if (!_dbus_string_init (&to_hash))
+    goto out_0;
   
-  return TRUE;
-}
+  if (!_dbus_string_copy (server_challenge, 0,
+                          &to_hash, _dbus_string_get_length (&to_hash)))
+    goto out_1;
 
-static void
-handle_client_shutdown_stupid_test_mech (DBusAuth *auth)
-{
+  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;
 
-/* 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)
-{
-  if (!_dbus_string_base64_encode (plaintext, 0, encoded,
-                                   _dbus_string_get_length (encoded)))
-    return FALSE;
-  
-  return TRUE;
-}
+  if (!_dbus_string_copy (&cookie, 0,
+                          &to_hash, _dbus_string_get_length (&to_hash)))
+    goto out_1;
 
-static dbus_bool_t
-handle_decode_stupid_test_mech (DBusAuth         *auth,
-                                const DBusString *encoded,
-                                DBusString       *plaintext)
-{
-  if (!_dbus_string_base64_decode (encoded, 0, plaintext,
-                                   _dbus_string_get_length (plaintext)))
-    return FALSE;
+  if (!_dbus_sha_compute (&to_hash, hash))
+    goto out_1;
   
-  return TRUE;
+  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;
 }
 
+/** http://www.ietf.org/rfc/rfc2831.txt suggests at least 64 bits of
+ * entropy, we use 128. This is the number of bytes in the random
+ * challenge.
+ */
+#define N_CHALLENGE_BYTES (128/8)
+
 static dbus_bool_t
-handle_server_data_external_mech (DBusAuth         *auth,
-                                  const DBusString *data)
+sha1_handle_first_client_response (DBusAuth         *auth,
+                                   const DBusString *data)
 {
-  DBusCredentials desired_identity;
+  /* We haven't sent a challenge yet, we're expecting a desired
+   * username from the client.
+   */
+  DBusString tmp;
+  DBusString tmp2;
+  dbus_bool_t retval;
+  DBusError error;
+  
+  retval = FALSE;
 
-  if (auth->credentials.uid < 0)
-    {
-      _dbus_verbose ("no credentials, mechanism EXTERNAL can't authenticate\n");
-      return send_rejected (auth);
-    }
+  _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");
+          _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
+                         DBUS_AUTH_NAME (auth));
           return send_rejected (auth);
         }
       else
@@ -400,136 +548,751 @@ handle_server_data_external_mech (DBusAuth         *auth,
             return FALSE;
         }
     }
+      
+  if (!_dbus_credentials_add_from_user (auth->desired_identity, data))
+    {
+      _dbus_verbose ("%s: Did not get a valid username from client\n",
+                     DBUS_AUTH_NAME (auth));
+      return send_rejected (auth);
+    }
+      
+  if (!_dbus_string_init (&tmp))
+    return FALSE;
 
-  /* Poke client for an auth identity, if none given */
-  if (_dbus_string_get_length (&auth->identity) == 0 &&
-      !auth->already_asked_for_initial_response)
+  if (!_dbus_string_init (&tmp2))
     {
-      if (_dbus_string_append (&auth->outgoing,
-                               "DATA\r\n"))
-        {
-          _dbus_verbose ("sending empty challenge asking client for auth identity\n");
-          auth->already_asked_for_initial_response = TRUE;
-          return TRUE;
-        }
-      else
-        return FALSE;
+      _dbus_string_free (&tmp);
+      return FALSE;
     }
 
-  desired_identity.pid = -1;
-  desired_identity.uid = -1;
-  desired_identity.gid = -1;
-  
-  /* If auth->identity is still empty here, then client
-   * responded with an empty string after we poked it for
-   * an initial response. This means to try to auth the
-   * identity provided in the credentials.
+  /* 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 (_dbus_string_get_length (&auth->identity) == 0)
+  if (auth->keyring &&
+      !_dbus_keyring_is_for_credentials (auth->keyring,
+                                         auth->desired_identity))
     {
-      desired_identity.uid = auth->credentials.uid;
+      _dbus_keyring_unref (auth->keyring);
+      auth->keyring = NULL;
     }
-  else
+  
+  if (auth->keyring == NULL)
     {
-      if (!_dbus_credentials_from_uid_string (&auth->identity,
-                                              &desired_identity))
+      dbus_error_init (&error);
+      auth->keyring = _dbus_keyring_new_for_credentials (auth->desired_identity,
+                                                         &auth->context,
+                                                         &error);
+
+      if (auth->keyring == NULL)
         {
-          _dbus_verbose ("could not get credentials from uid string\n");
-          return send_rejected (auth);
+          if (dbus_error_has_name (&error,
+                                   DBUS_ERROR_NO_MEMORY))
+            {
+              dbus_error_free (&error);
+              goto out;
+            }
+          else
+            {
+              _DBUS_ASSERT_ERROR_IS_SET (&error);
+              _dbus_verbose ("%s: Error loading keyring: %s\n",
+                             DBUS_AUTH_NAME (auth), 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));
         }
     }
 
-  if (desired_identity.uid < 0)
-    {
-      _dbus_verbose ("desired UID %d is no good\n", desired_identity.uid);
-      return send_rejected (auth);
-    }
-  
-  if (_dbus_credentials_match (&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);
-      
-      if (!_dbus_string_append (&auth->outgoing,
-                                "OK\r\n"))
-        return FALSE;
+  _dbus_assert (auth->keyring != NULL);
 
-      auth->authorized_identity.uid = desired_identity.uid;
-      
-      auth->authenticated_pending_begin = TRUE;
-      
-      return TRUE;
+  dbus_error_init (&error);
+  auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error);
+  if (auth->cookie_id < 0)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (&error);
+      _dbus_verbose ("%s: Could not get a cookie ID to send to client: %s\n",
+                     DBUS_AUTH_NAME (auth), error.message);
+      if (send_rejected (auth))
+        retval = TRUE;
+      dbus_error_free (&error);
+      goto out;
     }
   else
     {
-      _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);
-      return send_rejected (auth);
+      _dbus_assert (!dbus_error_is_set (&error));
     }
-}
 
-static void
-handle_server_shutdown_external_mech (DBusAuth *auth)
-{
+  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 (!send_data (auth, &tmp2))
+    goto out;
+      
+  goto_state (auth, &server_state_waiting_for_data);
+  retval = TRUE;
+  
+ out:
+  _dbus_string_zero (&tmp);
+  _dbus_string_free (&tmp);
+  _dbus_string_zero (&tmp2);
+  _dbus_string_free (&tmp2);
 
+  return retval;
 }
 
 static dbus_bool_t
-handle_client_initial_response_external_mech (DBusAuth         *auth,
-                                              DBusString       *response)
+sha1_handle_second_client_response (DBusAuth         *auth,
+                                    const DBusString *data)
 {
-  /* We always append our UID as an initial response, so the server
-   * doesn't have to send back an empty challenge to check whether we
-   * want to specify an identity. i.e. this avoids a round trip that
-   * the spec for the EXTERNAL mechanism otherwise requires.
+  /* 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.
    */
-  DBusString plaintext;
+  int i;
+  DBusString client_challenge;
+  DBusString client_hash;
+  dbus_bool_t retval;
+  DBusString correct_hash;
+  
+  retval = FALSE;
+  
+  if (!_dbus_string_find_blank (data, 0, &i))
+    {
+      _dbus_verbose ("%s: no space separator in client response\n",
+                     DBUS_AUTH_NAME (auth));
+      return send_rejected (auth);
+    }
+  
+  if (!_dbus_string_init (&client_challenge))
+    goto out_0;
 
-  if (!_dbus_string_init (&plaintext, _DBUS_INT_MAX))
-    return FALSE;
+  if (!_dbus_string_init (&client_hash))
+    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_append_our_uid (&plaintext))
-    goto failed;
+  if (!_dbus_string_copy_len (data, i,
+                              _dbus_string_get_length (data) - i,
+                              &client_hash,
+                              0))
+    goto out_2;
 
-  if (!_dbus_string_base64_encode (&plaintext, 0,
-                                   response,
-                                   _dbus_string_get_length (response)))
-    goto failed;
+  if (_dbus_string_get_length (&client_challenge) == 0 ||
+      _dbus_string_get_length (&client_hash) == 0)
+    {
+      _dbus_verbose ("%s: zero-length client challenge or hash\n",
+                     DBUS_AUTH_NAME (auth));
+      if (send_rejected (auth))
+        retval = TRUE;
+      goto out_2;
+    }
 
-  _dbus_string_free (&plaintext);
+  if (!_dbus_string_init (&correct_hash))
+    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;
+    }
   
-  return TRUE;
+  if (!_dbus_string_equal (&client_hash, &correct_hash))
+    {
+      if (send_rejected (auth))
+        retval = TRUE;
+      goto out_3;
+    }
 
- failed:
-  _dbus_string_free (&plaintext);
-  return FALSE;  
+  if (!_dbus_credentials_add_credentials (auth->authorized_identity,
+                                          auth->desired_identity))
+    goto out_3;
+
+  /* Copy process ID from the socket credentials if it's there
+   */
+  if (!_dbus_credentials_add_credential (auth->authorized_identity,
+                                         DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+                                         auth->credentials))
+    goto out_3;
+  
+  if (!send_ok (auth))
+    goto out_3;
+
+  _dbus_verbose ("%s: authenticated client using DBUS_COOKIE_SHA1\n",
+                 DBUS_AUTH_NAME (auth));
+  
+  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 dbus_bool_t
-handle_client_data_external_mech (DBusAuth         *auth,
-                                  const DBusString *data)
+handle_server_data_cookie_sha1_mech (DBusAuth         *auth,
+                                     const DBusString *data)
 {
-  
-  return TRUE;
+  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_client_shutdown_external_mech (DBusAuth *auth)
+handle_server_shutdown_cookie_sha1_mech (DBusAuth *auth)
 {
-
+  auth->cookie_id = -1;  
+  _dbus_string_set_length (&auth->challenge, 0);
 }
 
-/* Put mechanisms here in order of preference.
- * What I eventually want to have is:
+static dbus_bool_t
+handle_client_initial_response_cookie_sha1_mech (DBusAuth   *auth,
+                                                 DBusString *response)
+{
+  DBusString username;
+  dbus_bool_t retval;
+
+  retval = FALSE;
+
+  if (!_dbus_string_init (&username))
+    return FALSE;
+  
+  if (!_dbus_append_user_from_current_process (&username))
+    goto out_0;
+
+  if (!_dbus_string_hex_encode (&username, 0,
+                               response,
+                               _dbus_string_get_length (response)))
+    goto out_0;
+
+  retval = TRUE;
+  
+ out_0:
+  _dbus_string_free (&username);
+  
+  return retval;
+}
+
+static dbus_bool_t
+handle_client_data_cookie_sha1_mech (DBusAuth         *auth,
+                                     const DBusString *data)
+{
+  /* 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;
+  
+  retval = FALSE;                 
+  
+  if (!_dbus_string_find_blank (data, 0, &i))
+    {
+      if (send_error (auth,
+                      "Server did not send context/ID/challenge properly"))
+        retval = TRUE;
+      goto out_0;
+    }
+
+  if (!_dbus_string_init (&context))
+    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 (send_error (auth,
+                      "Server did not send context/ID/challenge properly"))
+        retval = TRUE;
+      goto out_1;
+    }
+
+  if (!_dbus_string_init (&cookie_id_str))
+    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))
+    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 (send_error (auth, "Server sent invalid cookie context"))
+        retval = TRUE;
+      goto out_3;
+    }
+
+  if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL))
+    {
+      if (send_error (auth, "Could not parse cookie ID as an integer"))
+        retval = TRUE;
+      goto out_3;
+    }
+
+  if (_dbus_string_get_length (&server_challenge) == 0)
+    {
+      if (send_error (auth, "Empty server challenge string"))
+        retval = TRUE;
+      goto out_3;
+    }
+
+  if (auth->keyring == NULL)
+    {
+      DBusError error;
+
+      dbus_error_init (&error);
+      auth->keyring = _dbus_keyring_new_for_credentials (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_ERROR_IS_SET (&error);
+
+              _dbus_verbose ("%s: Error loading keyring: %s\n",
+                             DBUS_AUTH_NAME (auth), error.message);
+              
+              if (send_error (auth, "Could not load cookie file"))
+                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))
+    goto out_3;
+  
+  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
+    goto out_4;
+
+  if (!_dbus_string_init (&client_challenge))
+    goto out_4;
+
+  if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0))
+    goto out_5;
+
+  if (!_dbus_string_init (&correct_hash))
+    goto out_5;
+  
+  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 (send_error (auth, "Don't have the requested cookie ID"))
+        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;
+
+  if (!send_data (auth, &tmp))
+    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);
+}
+
+/*
+ * EXTERNAL mechanism
+ */
+
+static dbus_bool_t
+handle_server_data_external_mech (DBusAuth         *auth,
+                                  const DBusString *data)
+{
+  if (_dbus_credentials_are_anonymous (auth->credentials))
+    {
+      _dbus_verbose ("%s: no credentials, mechanism EXTERNAL can't authenticate\n",
+                     DBUS_AUTH_NAME (auth));
+      return send_rejected (auth);
+    }
+  
+  if (_dbus_string_get_length (data) > 0)
+    {
+      if (_dbus_string_get_length (&auth->identity) > 0)
+        {
+          /* Tried to send two auth identities, wtf */
+          _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
+                         DBUS_AUTH_NAME (auth));
+          return send_rejected (auth);
+        }
+      else
+        {
+          /* this is our auth identity */
+          if (!_dbus_string_copy (data, 0, &auth->identity, 0))
+            return FALSE;
+        }
+    }
+
+  /* Poke client for an auth identity, if none given */
+  if (_dbus_string_get_length (&auth->identity) == 0 &&
+      !auth->already_asked_for_initial_response)
+    {
+      if (send_data (auth, NULL))
+        {
+          _dbus_verbose ("%s: sending empty challenge asking client for auth identity\n",
+                         DBUS_AUTH_NAME (auth));
+          auth->already_asked_for_initial_response = TRUE;
+          goto_state (auth, &server_state_waiting_for_data);
+          return TRUE;
+        }
+      else
+        return FALSE;
+    }
+
+  _dbus_credentials_clear (auth->desired_identity);
+  
+  /* If auth->identity is still empty here, then client
+   * responded with an empty string after we poked it for
+   * an initial response. This means to try to auth the
+   * identity provided in the credentials.
+   */
+  if (_dbus_string_get_length (&auth->identity) == 0)
+    {
+      if (!_dbus_credentials_add_credentials (auth->desired_identity,
+                                              auth->credentials))
+        {
+          return FALSE; /* OOM */
+        }
+    }
+  else
+    {
+      if (!_dbus_credentials_add_from_user (auth->desired_identity,
+                                            &auth->identity))
+        {
+          _dbus_verbose ("%s: could not get credentials from uid string\n",
+                         DBUS_AUTH_NAME (auth));
+          return send_rejected (auth);
+        }
+    }
+
+  if (_dbus_credentials_are_anonymous (auth->desired_identity))
+    {
+      _dbus_verbose ("%s: desired user %s is no good\n",
+                     DBUS_AUTH_NAME (auth),
+                     _dbus_string_get_const_data (&auth->identity));
+      return send_rejected (auth);
+    }
+  
+  if (_dbus_credentials_are_superset (auth->credentials,
+                                      auth->desired_identity))
+    {
+      /* client has authenticated */
+      if (!_dbus_credentials_add_credentials (auth->authorized_identity,
+                                              auth->desired_identity))
+        return FALSE;
+
+      /* also copy process ID from the socket credentials
+       */
+      if (!_dbus_credentials_add_credential (auth->authorized_identity,
+                                             DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+                                             auth->credentials))
+        return FALSE;
+
+      /* also copy audit data from the socket credentials
+       */
+      if (!_dbus_credentials_add_credential (auth->authorized_identity,
+                                             DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
+                                             auth->credentials))
+        return FALSE;
+      
+      if (!send_ok (auth))
+        return FALSE;
+
+      _dbus_verbose ("%s: authenticated client based on socket credentials\n",
+                     DBUS_AUTH_NAME (auth));
+
+      return TRUE;
+    }
+  else
+    {
+      _dbus_verbose ("%s: desired identity not found in socket credentials\n",
+                     DBUS_AUTH_NAME (auth));
+      return send_rejected (auth);
+    }
+}
+
+static void
+handle_server_shutdown_external_mech (DBusAuth *auth)
+{
+
+}
+
+static dbus_bool_t
+handle_client_initial_response_external_mech (DBusAuth         *auth,
+                                              DBusString       *response)
+{
+  /* We always append our UID as an initial response, so the server
+   * doesn't have to send back an empty challenge to check whether we
+   * want to specify an identity. i.e. this avoids a round trip that
+   * the spec for the EXTERNAL mechanism otherwise requires.
+   */
+  DBusString plaintext;
+
+  if (!_dbus_string_init (&plaintext))
+    return FALSE;
+
+  if (!_dbus_append_user_from_current_process (&plaintext))
+    goto failed;
+
+  if (!_dbus_string_hex_encode (&plaintext, 0,
+                               response,
+                               _dbus_string_get_length (response)))
+    goto failed;
+
+  _dbus_string_free (&plaintext);
+  
+  return TRUE;
+
+ failed:
+  _dbus_string_free (&plaintext);
+  return FALSE;  
+}
+
+static dbus_bool_t
+handle_client_data_external_mech (DBusAuth         *auth,
+                                  const DBusString *data)
+{
+  
+  return TRUE;
+}
+
+static void
+handle_client_shutdown_external_mech (DBusAuth *auth)
+{
+
+}
+
+/*
+ * ANONYMOUS mechanism
+ */
+
+static dbus_bool_t
+handle_server_data_anonymous_mech (DBusAuth         *auth,
+                                   const DBusString *data)
+{  
+  if (_dbus_string_get_length (data) > 0)
+    {
+      /* Client is allowed to send "trace" data, the only defined
+       * meaning is that if it contains '@' it is an email address,
+       * and otherwise it is anything else, and it's supposed to be
+       * UTF-8
+       */
+      if (!_dbus_string_validate_utf8 (data, 0, _dbus_string_get_length (data)))
+        {
+          _dbus_verbose ("%s: Received invalid UTF-8 trace data from ANONYMOUS client\n",
+                         DBUS_AUTH_NAME (auth));
+          return send_rejected (auth);
+        }
+      
+      _dbus_verbose ("%s: ANONYMOUS client sent trace string: '%s'\n",
+                     DBUS_AUTH_NAME (auth),
+                     _dbus_string_get_const_data (data));
+    }
+
+  /* We want to be anonymous (clear in case some other protocol got midway through I guess) */
+  _dbus_credentials_clear (auth->desired_identity);
+
+  /* Copy process ID from the socket credentials
+   */
+  if (!_dbus_credentials_add_credential (auth->authorized_identity,
+                                         DBUS_CREDENTIAL_UNIX_PROCESS_ID,
+                                         auth->credentials))
+    return FALSE;
+  
+  /* Anonymous is always allowed */
+  if (!send_ok (auth))
+    return FALSE;
+
+  _dbus_verbose ("%s: authenticated client as anonymous\n",
+                 DBUS_AUTH_NAME (auth));
+
+  return TRUE;
+}
+
+static void
+handle_server_shutdown_anonymous_mech (DBusAuth *auth)
+{
+  
+}
+
+static dbus_bool_t
+handle_client_initial_response_anonymous_mech (DBusAuth         *auth,
+                                               DBusString       *response)
+{
+  /* Our initial response is a "trace" string which must be valid UTF-8
+   * and must be an email address if it contains '@'.
+   * We just send the dbus implementation info, like a user-agent or
+   * something, because... why not. There's nothing guaranteed here
+   * though, we could change it later.
+   */
+  DBusString plaintext;
+
+  if (!_dbus_string_init (&plaintext))
+    return FALSE;
+
+  if (!_dbus_string_append (&plaintext,
+                            "libdbus " DBUS_VERSION_STRING))
+    goto failed;
+
+  if (!_dbus_string_hex_encode (&plaintext, 0,
+                               response,
+                               _dbus_string_get_length (response)))
+    goto failed;
+
+  _dbus_string_free (&plaintext);
+  
+  return TRUE;
+
+ failed:
+  _dbus_string_free (&plaintext);
+  return FALSE;  
+}
+
+static dbus_bool_t
+handle_client_data_anonymous_mech (DBusAuth         *auth,
+                                  const DBusString *data)
+{
+  
+  return TRUE;
+}
+
+static void
+handle_client_shutdown_anonymous_mech (DBusAuth *auth)
+{
+  
+}
+
+/* Put mechanisms here in order of preference.
+ * Right now we have:
+ *
+ * - EXTERNAL checks socket credentials (or in the future, other info from the OS)
+ * - DBUS_COOKIE_SHA1 uses a cookie in the home directory, like xauth or ICE
+ * - ANONYMOUS checks nothing but doesn't auth the person as a user
  *
- *  - a mechanism that checks UNIX domain socket credentials
- *  - a simple magic cookie mechanism like X11 or ICE
- *  - mechanisms that chain to Cyrus SASL, so we can use anything it
- *    offers such as Kerberos, X509, whatever.
+ * We might ideally add a mechanism to chain to Cyrus SASL so we can
+ * use its mechanisms as well.
  * 
  */
 static const DBusAuthMechanismHandler
@@ -542,28 +1305,39 @@ 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 },
+  { "ANONYMOUS",
+    handle_server_data_anonymous_mech,
+    NULL, NULL,
+    handle_server_shutdown_anonymous_mech,
+    handle_client_initial_response_anonymous_mech,
+    handle_client_data_anonymous_mech,
+    NULL, NULL,
+    handle_client_shutdown_anonymous_mech },  
   { NULL, NULL }
 };
 
 static const DBusAuthMechanismHandler*
-find_mech (const DBusString *name)
+find_mech (const DBusString  *name,
+           char             **allowed_mechs)
 {
   int i;
   
+  if (allowed_mechs != NULL &&
+      !_dbus_string_array_contains ((const char**) allowed_mechs,
+                                    _dbus_string_get_const_data (name)))
+    return NULL;
+  
   i = 0;
   while (all_mechanisms[i].mechanism != NULL)
-    {
+    {      
       if (_dbus_string_equal_c_str (name,
                                     all_mechanisms[i].mechanism))
 
@@ -571,8 +1345,97 @@ find_mech (const DBusString *name)
       
       ++i;
     }
-  
-  return NULL;
+  
+  return NULL;
+}
+
+static dbus_bool_t
+send_auth (DBusAuth *auth, const DBusAuthMechanismHandler *mech)
+{
+  DBusString auth_command;
+
+  if (!_dbus_string_init (&auth_command))
+    return FALSE;
+      
+  if (!_dbus_string_append (&auth_command,
+                            "AUTH "))
+    {
+      _dbus_string_free (&auth_command);
+      return FALSE;
+    }  
+  
+  if (!_dbus_string_append (&auth_command,
+                            mech->mechanism))
+    {
+      _dbus_string_free (&auth_command);
+      return FALSE;
+    }
+
+  if (mech->client_initial_response_func != NULL)
+    {
+      if (!_dbus_string_append (&auth_command, " "))
+        {
+          _dbus_string_free (&auth_command);
+          return FALSE;
+        }
+      
+      if (!(* mech->client_initial_response_func) (auth, &auth_command))
+        {
+          _dbus_string_free (&auth_command);
+          return FALSE;
+        }
+    }
+  
+  if (!_dbus_string_append (&auth_command,
+                            "\r\n"))
+    {
+      _dbus_string_free (&auth_command);
+      return FALSE;
+    }
+
+  if (!_dbus_string_copy (&auth_command, 0,
+                          &auth->outgoing,
+                          _dbus_string_get_length (&auth->outgoing)))
+    {
+      _dbus_string_free (&auth_command);
+      return FALSE;
+    }
+
+  _dbus_string_free (&auth_command);
+  shutdown_mech (auth);
+  auth->mech = mech;      
+  goto_state (auth, &client_state_waiting_for_data);
+
+  return TRUE;
+}
+
+static dbus_bool_t
+send_data (DBusAuth *auth, DBusString *data)
+{
+  int old_len;
+
+  if (data == NULL || _dbus_string_get_length (data) == 0)
+    return _dbus_string_append (&auth->outgoing, "DATA\r\n");
+  else
+    {
+      old_len = _dbus_string_get_length (&auth->outgoing);
+      if (!_dbus_string_append (&auth->outgoing, "DATA "))
+        goto out;
+
+      if (!_dbus_string_hex_encode (data, 0, &auth->outgoing,
+                                    _dbus_string_get_length (&auth->outgoing)))
+        goto out;
+
+      if (!_dbus_string_append (&auth->outgoing, "\r\n"))
+        goto out;
+
+      return TRUE;
+
+    out:
+      _dbus_string_set_length (&auth->outgoing, old_len);
+
+      return FALSE;
+    }
 }
 
 static dbus_bool_t
@@ -582,7 +1445,7 @@ send_rejected (DBusAuth *auth)
   DBusAuthServer *server_auth;
   int i;
   
-  if (!_dbus_string_init (&command, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&command))
     return FALSE;
   
   if (!_dbus_string_append (&command,
@@ -615,6 +1478,13 @@ send_rejected (DBusAuth *auth)
   _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
   server_auth = DBUS_AUTH_SERVER (auth);
   server_auth->failures += 1;
+
+  if (server_auth->failures >= server_auth->max_failures)
+    goto_state (auth, &common_state_need_disconnect);
+  else
+    goto_state (auth, &server_state_waiting_for_auth);
+
+  _dbus_string_free (&command);
   
   return TRUE;
 
@@ -624,20 +1494,178 @@ send_rejected (DBusAuth *auth)
 }
 
 static dbus_bool_t
-process_auth (DBusAuth         *auth,
-              const DBusString *command,
-              const DBusString *args)
+send_error (DBusAuth *auth, const char *message)
+{
+  return _dbus_string_append_printf (&auth->outgoing,
+                                     "ERROR \"%s\"\r\n", message);
+}
+
+static dbus_bool_t
+send_ok (DBusAuth *auth)
+{
+  int orig_len;
+
+  orig_len = _dbus_string_get_length (&auth->outgoing);
+  
+  if (_dbus_string_append (&auth->outgoing, "OK ") &&
+      _dbus_string_copy (& DBUS_AUTH_SERVER (auth)->guid,
+                         0,
+                         &auth->outgoing,
+                         _dbus_string_get_length (&auth->outgoing)) &&
+      _dbus_string_append (&auth->outgoing, "\r\n"))
+    {
+      goto_state (auth, &server_state_waiting_for_begin);
+      return TRUE;
+    }
+  else
+    {
+      _dbus_string_set_length (&auth->outgoing, orig_len);
+      return FALSE;
+    }
+}
+
+static dbus_bool_t
+send_begin (DBusAuth         *auth)
+{
+
+  if (!_dbus_string_append (&auth->outgoing,
+                            "BEGIN\r\n"))
+    return FALSE;
+
+  goto_state (auth, &common_state_authenticated);
+  return TRUE;
+}
+
+static dbus_bool_t
+process_ok(DBusAuth *auth,
+          const DBusString *args_from_ok) {
+
+  int end_of_hex;
+  
+  /* "args_from_ok" should be the GUID, whitespace already pulled off the front */
+  _dbus_assert (_dbus_string_get_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server) == 0);
+
+  /* We decode the hex string to binary, using guid_from_server as scratch... */
+  
+  end_of_hex = 0;
+  if (!_dbus_string_hex_decode (args_from_ok, 0, &end_of_hex,
+                                & DBUS_AUTH_CLIENT (auth)->guid_from_server, 0))
+    return FALSE;
+
+  /* now clear out the scratch */
+  _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
+  
+  if (end_of_hex != _dbus_string_get_length (args_from_ok) ||
+      end_of_hex == 0)
+    {
+      _dbus_verbose ("Bad GUID from server, parsed %d bytes and had %d bytes from server\n",
+                     end_of_hex, _dbus_string_get_length (args_from_ok));
+      goto_state (auth, &common_state_need_disconnect);
+      return TRUE;
+    }
+
+  if (!_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0)) {
+      _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
+      return FALSE;
+  }
+
+  _dbus_verbose ("Got GUID '%s' from the server\n",
+                 _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server));
+
+  if (auth->unix_fd_possible)
+    return send_negotiate_unix_fd(auth);
+
+  _dbus_verbose("Not negotiating unix fd passing, since not possible\n");
+  return send_begin (auth);
+}
+
+static dbus_bool_t
+send_cancel (DBusAuth *auth)
+{
+  if (_dbus_string_append (&auth->outgoing, "CANCEL\r\n"))
+    {
+      goto_state (auth, &client_state_waiting_for_reject);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static dbus_bool_t
+process_data (DBusAuth             *auth,
+              const DBusString     *args,
+              DBusAuthDataFunction  data_func)
 {
-  if (auth->mech)
+  int end;
+  DBusString decoded;
+
+  if (!_dbus_string_init (&decoded))
+    return FALSE;
+
+  if (!_dbus_string_hex_decode (args, 0, &end, &decoded, 0))
     {
-      /* We are already using a mechanism, client is on crack */
-      if (!_dbus_string_append (&auth->outgoing,
-                                "ERROR \"Sent AUTH while another AUTH in progress\"\r\n"))
+      _dbus_string_free (&decoded);
+      return FALSE;
+    }
+
+  if (_dbus_string_get_length (args) != end)
+    {
+      _dbus_string_free (&decoded);
+      if (!send_error (auth, "Invalid hex encoding"))
         return FALSE;
 
       return TRUE;
     }
-  else if (_dbus_string_get_length (args) == 0)
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+  if (_dbus_string_validate_ascii (&decoded, 0,
+                                   _dbus_string_get_length (&decoded)))
+    _dbus_verbose ("%s: data: '%s'\n",
+                   DBUS_AUTH_NAME (auth),
+                   _dbus_string_get_const_data (&decoded));
+#endif
+      
+  if (!(* data_func) (auth, &decoded))
+    {
+      _dbus_string_free (&decoded);
+      return FALSE;
+    }
+
+  _dbus_string_free (&decoded);
+  return TRUE;
+}
+
+static dbus_bool_t
+send_negotiate_unix_fd (DBusAuth *auth)
+{
+  if (!_dbus_string_append (&auth->outgoing,
+                            "NEGOTIATE_UNIX_FD\r\n"))
+    return FALSE;
+
+  goto_state (auth, &client_state_waiting_for_agree_unix_fd);
+  return TRUE;
+}
+
+static dbus_bool_t
+send_agree_unix_fd (DBusAuth *auth)
+{
+  _dbus_assert(auth->unix_fd_possible);
+
+  auth->unix_fd_negotiated = TRUE;
+  _dbus_verbose("Agreed to UNIX FD passing\n");
+
+  if (!_dbus_string_append (&auth->outgoing,
+                            "AGREE_UNIX_FD\r\n"))
+    return FALSE;
+
+  goto_state (auth, &server_state_waiting_for_begin);
+  return TRUE;
+}
+
+static dbus_bool_t
+handle_auth (DBusAuth *auth, const DBusString *args)
+{
+  if (_dbus_string_get_length (args) == 0)
     {
       /* No args to the auth, send mechanisms */
       if (!send_rejected (auth))
@@ -649,141 +1677,161 @@ process_auth (DBusAuth         *auth,
     {
       int i;
       DBusString mech;
-      DBusString base64_response;
-      DBusString decoded_response;
+      DBusString hex_response;
       
       _dbus_string_find_blank (args, 0, &i);
 
-      if (!_dbus_string_init (&mech, _DBUS_INT_MAX))
+      if (!_dbus_string_init (&mech))
         return FALSE;
 
-      if (!_dbus_string_init (&base64_response, _DBUS_INT_MAX))
-        {
-          _dbus_string_free (&mech);
-          return FALSE;
-        }
-
-      if (!_dbus_string_init (&decoded_response, _DBUS_INT_MAX))
+      if (!_dbus_string_init (&hex_response))
         {
           _dbus_string_free (&mech);
-          _dbus_string_free (&base64_response);
           return FALSE;
         }
       
       if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
         goto failed;
 
-      if (!_dbus_string_copy (args, i, &base64_response, 0))
-        goto failed;
-
-      if (!_dbus_string_base64_decode (&base64_response, 0,
-                                       &decoded_response, 0))
+      _dbus_string_skip_blank (args, i, &i);
+      if (!_dbus_string_copy (args, i, &hex_response, 0))
         goto failed;
-
-      auth->mech = find_mech (&mech);
+     
+      auth->mech = find_mech (&mech, auth->allowed_mechs);
       if (auth->mech != NULL)
         {
-          _dbus_verbose ("Trying mechanism %s with initial response of %d bytes\n",
-                         auth->mech->mechanism,
-                         _dbus_string_get_length (&decoded_response));
+          _dbus_verbose ("%s: Trying mechanism %s\n",
+                         DBUS_AUTH_NAME (auth),
+                         auth->mech->mechanism);
           
-          if (!(* auth->mech->server_data_func) (auth,
-                                                 &decoded_response))
+          if (!process_data (auth, &hex_response,
+                             auth->mech->server_data_func))
             goto failed;
         }
       else
         {
           /* Unsupported mechanism */
+          _dbus_verbose ("%s: Unsupported mechanism %s\n",
+                         DBUS_AUTH_NAME (auth),
+                         _dbus_string_get_const_data (&mech));
+          
           if (!send_rejected (auth))
-            return FALSE;
+            goto failed;
         }
 
       _dbus_string_free (&mech);      
-      _dbus_string_free (&base64_response);
-      _dbus_string_free (&decoded_response);
+      _dbus_string_free (&hex_response);
 
       return TRUE;
       
     failed:
       auth->mech = NULL;
       _dbus_string_free (&mech);
-      _dbus_string_free (&base64_response);
-      _dbus_string_free (&decoded_response);
+      _dbus_string_free (&hex_response);
       return FALSE;
     }
 }
 
 static dbus_bool_t
-process_cancel (DBusAuth         *auth,
-                const DBusString *command,
-                const DBusString *args)
-{
-  shutdown_mech (auth);
-  
-  return TRUE;
-}
-
-static dbus_bool_t
-process_begin (DBusAuth         *auth,
-               const DBusString *command,
-               const DBusString *args)
+handle_server_state_waiting_for_auth  (DBusAuth         *auth,
+                                       DBusAuthCommand   command,
+                                       const DBusString *args)
 {
-  if (auth->authenticated_pending_begin)
-    auth->authenticated = TRUE;
-  else
+  switch (command)
     {
-      auth->need_disconnect = TRUE; /* client trying to send data before auth,
-                                     * kick it
-                                     */
-      shutdown_mech (auth);
+    case DBUS_AUTH_COMMAND_AUTH:
+      return handle_auth (auth, args);
+
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_DATA:
+      return send_error (auth, "Not currently in an auth conversation");
+
+    case DBUS_AUTH_COMMAND_BEGIN:
+      goto_state (auth, &common_state_need_disconnect);
+      return TRUE;
+
+    case DBUS_AUTH_COMMAND_ERROR:
+      return send_rejected (auth);
+
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+      return send_error (auth, "Need to authenticate first");
+
+    case DBUS_AUTH_COMMAND_REJECTED:
+    case DBUS_AUTH_COMMAND_OK:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+    default:
+      return send_error (auth, "Unknown command");
     }
-  
-  return TRUE;
 }
 
 static dbus_bool_t
-process_data_server (DBusAuth         *auth,
-                     const DBusString *command,
-                     const DBusString *args)
+handle_server_state_waiting_for_data  (DBusAuth         *auth,
+                                       DBusAuthCommand   command,
+                                       const DBusString *args)
 {
-  if (auth->mech != NULL)
+  switch (command)
     {
-      DBusString decoded;
+    case DBUS_AUTH_COMMAND_AUTH:
+      return send_error (auth, "Sent AUTH while another AUTH in progress");
 
-      if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
-        return FALSE;
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_ERROR:
+      return send_rejected (auth);
 
-      if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
-        {
-          _dbus_string_free (&decoded);
-          return FALSE;
-        }
-      
-      if (!(* auth->mech->server_data_func) (auth, &decoded))
-        {
-          _dbus_string_free (&decoded);
-          return FALSE;
-        }
+    case DBUS_AUTH_COMMAND_DATA:
+      return process_data (auth, args, auth->mech->server_data_func);
 
-      _dbus_string_free (&decoded);
-    }
-  else
-    {
-      if (!_dbus_string_append (&auth->outgoing,
-                                "ERROR \"Not currently in an auth conversation\"\r\n"))
-        return FALSE;
+    case DBUS_AUTH_COMMAND_BEGIN:
+      goto_state (auth, &common_state_need_disconnect);
+      return TRUE;
+
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+      return send_error (auth, "Need to authenticate first");
+
+    case DBUS_AUTH_COMMAND_REJECTED:
+    case DBUS_AUTH_COMMAND_OK:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+    default:
+      return send_error (auth, "Unknown command");
     }
-  
-  return TRUE;
 }
 
 static dbus_bool_t
-process_error_server (DBusAuth         *auth,
-                      const DBusString *command,
-                      const DBusString *args)
+handle_server_state_waiting_for_begin (DBusAuth         *auth,
+                                       DBusAuthCommand   command,
+                                       const DBusString *args)
 {
-  
-  return TRUE;
+  switch (command)
+    {
+    case DBUS_AUTH_COMMAND_AUTH:
+      return send_error (auth, "Sent AUTH while expecting BEGIN");
+
+    case DBUS_AUTH_COMMAND_DATA:
+      return send_error (auth, "Sent DATA while expecting BEGIN");
+
+    case DBUS_AUTH_COMMAND_BEGIN:
+      goto_state (auth, &common_state_authenticated);
+      return TRUE;
+
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+      if (auth->unix_fd_possible)
+        return send_agree_unix_fd(auth);
+      else
+        return send_error(auth, "Unix FD passing not supported, not authenticated or otherwise not possible");
+
+    case DBUS_AUTH_COMMAND_REJECTED:
+    case DBUS_AUTH_COMMAND_OK:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+    default:
+      return send_error (auth, "Unknown command");
+
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_ERROR:
+      return send_rejected (auth);
+    }
 }
 
 /* return FALSE if no memory, TRUE if all OK */
@@ -799,7 +1847,7 @@ get_word (const DBusString *str,
   
   if (i > *start)
     {
-      if (!_dbus_string_copy_len (str, *start, i, word, 0))
+      if (!_dbus_string_copy_len (str, *start, i - *start, word, 0))
         return FALSE;
       
       *start = i;
@@ -810,7 +1858,6 @@ get_word (const DBusString *str,
 
 static dbus_bool_t
 record_mechanisms (DBusAuth         *auth,
-                   const DBusString *command,
                    const DBusString *args)
 {
   int next;
@@ -827,13 +1874,16 @@ record_mechanisms (DBusAuth         *auth,
       DBusString m;
       const DBusAuthMechanismHandler *mech;
       
-      if (!_dbus_string_init (&m, _DBUS_INT_MAX))
+      if (!_dbus_string_init (&m))
         goto nomem;
       
       if (!get_word (args, &next, &m))
-        goto nomem;
+        {
+          _dbus_string_free (&m);
+          goto nomem;
+        }
 
-      mech = find_mech (&m);
+      mech = find_mech (&m, auth->allowed_mechs);
 
       if (mech != NULL)
         {
@@ -846,20 +1896,29 @@ record_mechanisms (DBusAuth         *auth,
            * it lists things in that order anyhow.
            */
 
-          _dbus_verbose ("Adding mechanism %s to list we will try\n",
-                         mech->mechanism);
+          if (mech != &all_mechanisms[0])
+            {
+              _dbus_verbose ("%s: Adding mechanism %s to list we will try\n",
+                             DBUS_AUTH_NAME (auth), mech->mechanism);
           
-          if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
-                                  (void*) mech))
-            goto nomem;
+              if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
+                                      (void*) mech))
+                {
+                  _dbus_string_free (&m);
+                  goto nomem;
+                }
+            }
+          else
+            {
+              _dbus_verbose ("%s: Already tried mechanism %s; not adding to list we will try\n",
+                             DBUS_AUTH_NAME (auth), mech->mechanism);
+            }
         }
       else
         {
-          const char *s;
-
-          _dbus_string_get_const_data (&m, &s);
-          _dbus_verbose ("Server offered mechanism \"%s\" that we don't know how to use\n",
-                         s);
+          _dbus_verbose ("%s: Server offered mechanism \"%s\" that we don't know how to use\n",
+                         DBUS_AUTH_NAME (auth),
+                         _dbus_string_get_const_data (&m));
         }
 
       _dbus_string_free (&m);
@@ -876,180 +1935,220 @@ record_mechanisms (DBusAuth         *auth,
 }
 
 static dbus_bool_t
-client_try_next_mechanism (DBusAuth *auth)
+process_rejected (DBusAuth *auth, const DBusString *args)
 {
   const DBusAuthMechanismHandler *mech;
-  DBusString auth_command;
-
-  if (DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
-    return FALSE;
-
-  mech = DBUS_AUTH_CLIENT (auth)->mechs_to_try->data;
-
-  if (!_dbus_string_init (&auth_command, _DBUS_INT_MAX))
-    return FALSE;
-      
-  if (!_dbus_string_append (&auth_command,
-                            "AUTH "))
-    {
-      _dbus_string_free (&auth_command);
-      return FALSE;
-    }  
-  
-  if (!_dbus_string_append (&auth_command,
-                            mech->mechanism))
-    {
-      _dbus_string_free (&auth_command);
-      return FALSE;
-    }
-
-  if (mech->client_initial_response_func != NULL)
-    {
-      if (!_dbus_string_append (&auth_command, " "))
-        {
-          _dbus_string_free (&auth_command);
-          return FALSE;
-        }
-      
-      if (!(* mech->client_initial_response_func) (auth, &auth_command))
-        {
-          _dbus_string_free (&auth_command);
-          return FALSE;
-        }
-    }
-  
-  if (!_dbus_string_append (&auth_command,
-                            "\r\n"))
-    {
-      _dbus_string_free (&auth_command);
-      return FALSE;
-    }
+  DBusAuthClient *client;
 
-  if (!_dbus_string_copy (&auth_command, 0,
-                          &auth->outgoing,
-                          _dbus_string_get_length (&auth->outgoing)))
-    {
-      _dbus_string_free (&auth_command);
-      return FALSE;
-    }
-
-  auth->mech = mech;      
-  _dbus_list_pop_first (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
-
-  _dbus_verbose ("Trying mechanism %s\n",
-                 auth->mech->mechanism);
-
-  return TRUE;
-}
+  client = DBUS_AUTH_CLIENT (auth);
 
-static dbus_bool_t
-process_rejected (DBusAuth         *auth,
-                  const DBusString *command,
-                  const DBusString *args)
-{
-  shutdown_mech (auth);
-  
   if (!auth->already_got_mechanisms)
     {
-      if (!record_mechanisms (auth, command, args))
+      if (!record_mechanisms (auth, args))
         return FALSE;
     }
   
   if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
     {
-      client_try_next_mechanism (auth);
+      mech = client->mechs_to_try->data;
+
+      if (!send_auth (auth, mech))
+        return FALSE;
+
+      _dbus_list_pop_first (&client->mechs_to_try);
+
+      _dbus_verbose ("%s: Trying mechanism %s\n",
+                     DBUS_AUTH_NAME (auth),
+                     mech->mechanism);
     }
   else
     {
       /* Give up */
-      auth->need_disconnect = TRUE;
+      _dbus_verbose ("%s: Disconnecting because we are out of mechanisms to try using\n",
+                     DBUS_AUTH_NAME (auth));
+      goto_state (auth, &common_state_need_disconnect);
     }
   
   return TRUE;
 }
 
+
 static dbus_bool_t
-process_ok (DBusAuth         *auth,
-            const DBusString *command,
-            const DBusString *args)
+handle_client_state_waiting_for_data (DBusAuth         *auth,
+                                      DBusAuthCommand   command,
+                                      const DBusString *args)
 {
-  if (!_dbus_string_append (&auth->outgoing,
-                            "BEGIN\r\n"))
-    return FALSE;
-  
-  auth->authenticated_pending_output = TRUE;
-  
-  return TRUE;
+  _dbus_assert (auth->mech != NULL);
+  switch (command)
+    {
+    case DBUS_AUTH_COMMAND_DATA:
+      return process_data (auth, args, auth->mech->client_data_func);
+
+    case DBUS_AUTH_COMMAND_REJECTED:
+      return process_rejected (auth, args);
+
+    case DBUS_AUTH_COMMAND_OK:
+      return process_ok(auth, args);
+
+    case DBUS_AUTH_COMMAND_ERROR:
+      return send_cancel (auth);
+
+    case DBUS_AUTH_COMMAND_AUTH:
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_BEGIN:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+    default:
+      return send_error (auth, "Unknown command");
+    }
 }
 
-
 static dbus_bool_t
-process_data_client (DBusAuth         *auth,
-                     const DBusString *command,
-                     const DBusString *args)
+handle_client_state_waiting_for_ok (DBusAuth         *auth,
+                                    DBusAuthCommand   command,
+                                    const DBusString *args)
 {
-  if (auth->mech != NULL)
+  switch (command)
     {
-      DBusString decoded;
-
-      if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
-        return FALSE;
+    case DBUS_AUTH_COMMAND_REJECTED:
+      return process_rejected (auth, args);
+
+    case DBUS_AUTH_COMMAND_OK:
+      return process_ok(auth, args);
+
+    case DBUS_AUTH_COMMAND_DATA:
+    case DBUS_AUTH_COMMAND_ERROR:
+      return send_cancel (auth);
+
+    case DBUS_AUTH_COMMAND_AUTH:
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_BEGIN:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+    default:
+      return send_error (auth, "Unknown command");
+    }
+}
 
-      if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
-        {
-          _dbus_string_free (&decoded);
-          return FALSE;
-        }
+static dbus_bool_t
+handle_client_state_waiting_for_reject (DBusAuth         *auth,
+                                        DBusAuthCommand   command,
+                                        const DBusString *args)
+{
+  switch (command)
+    {
+    case DBUS_AUTH_COMMAND_REJECTED:
+      return process_rejected (auth, args);
       
-      if (!(* auth->mech->client_data_func) (auth, &decoded))
-        {
-          _dbus_string_free (&decoded);
-          return FALSE;
-        }
-
-      _dbus_string_free (&decoded);
+    case DBUS_AUTH_COMMAND_AUTH:
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_DATA:
+    case DBUS_AUTH_COMMAND_BEGIN:
+    case DBUS_AUTH_COMMAND_OK:
+    case DBUS_AUTH_COMMAND_ERROR:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+    default:
+      goto_state (auth, &common_state_need_disconnect);
+      return TRUE;
     }
-  else
+}
+
+static dbus_bool_t
+handle_client_state_waiting_for_agree_unix_fd(DBusAuth         *auth,
+                                              DBusAuthCommand   command,
+                                              const DBusString *args)
+{
+  switch (command)
     {
-      if (!_dbus_string_append (&auth->outgoing,
-                                "ERROR \"Got DATA when not in an auth exchange\"\r\n"))
-        return FALSE;
+    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
+      _dbus_assert(auth->unix_fd_possible);
+      auth->unix_fd_negotiated = TRUE;
+      _dbus_verbose("Successfully negotiated UNIX FD passing\n");
+      return send_begin (auth);
+
+    case DBUS_AUTH_COMMAND_ERROR:
+      _dbus_assert(auth->unix_fd_possible);
+      auth->unix_fd_negotiated = FALSE;
+      _dbus_verbose("Failed to negotiate UNIX FD passing\n");
+      return send_begin (auth);
+
+    case DBUS_AUTH_COMMAND_OK:
+    case DBUS_AUTH_COMMAND_DATA:
+    case DBUS_AUTH_COMMAND_REJECTED:
+    case DBUS_AUTH_COMMAND_AUTH:
+    case DBUS_AUTH_COMMAND_CANCEL:
+    case DBUS_AUTH_COMMAND_BEGIN:
+    case DBUS_AUTH_COMMAND_UNKNOWN:
+    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
+    default:
+      return send_error (auth, "Unknown command");
     }
-  
-  return TRUE;
 }
 
-static dbus_bool_t
-process_error_client (DBusAuth         *auth,
-                      const DBusString *command,
-                      const DBusString *args)
+/**
+ * Mapping from command name to enum
+ */
+typedef struct {
+  const char *name;        /**< Name of the command */
+  DBusAuthCommand command; /**< Corresponding enum */
+} DBusAuthCommandName;
+
+static const DBusAuthCommandName auth_command_names[] = {
+  { "AUTH",              DBUS_AUTH_COMMAND_AUTH },
+  { "CANCEL",            DBUS_AUTH_COMMAND_CANCEL },
+  { "DATA",              DBUS_AUTH_COMMAND_DATA },
+  { "BEGIN",             DBUS_AUTH_COMMAND_BEGIN },
+  { "REJECTED",          DBUS_AUTH_COMMAND_REJECTED },
+  { "OK",                DBUS_AUTH_COMMAND_OK },
+  { "ERROR",             DBUS_AUTH_COMMAND_ERROR },
+  { "NEGOTIATE_UNIX_FD", DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD },
+  { "AGREE_UNIX_FD",     DBUS_AUTH_COMMAND_AGREE_UNIX_FD }
+};
+
+static DBusAuthCommand
+lookup_command_from_name (DBusString *command)
 {
-  return TRUE;
+  int i;
+
+  for (i = 0; i < _DBUS_N_ELEMENTS (auth_command_names); i++)
+    {
+      if (_dbus_string_equal_c_str (command,
+                                    auth_command_names[i].name))
+        return auth_command_names[i].command;
+    }
+
+  return DBUS_AUTH_COMMAND_UNKNOWN;
 }
 
-static dbus_bool_t
-process_unknown (DBusAuth         *auth,
-                 const DBusString *command,
-                 const DBusString *args)
+static void
+goto_state (DBusAuth *auth,
+            const DBusAuthStateData *state)
 {
-  if (!_dbus_string_append (&auth->outgoing,
-                            "ERROR \"Unknown command\"\r\n"))
-    return FALSE;
+  _dbus_verbose ("%s: going from state %s to state %s\n",
+                 DBUS_AUTH_NAME (auth),
+                 auth->state->name,
+                 state->name);
 
-  return TRUE;
+  auth->state = state;
 }
 
 /* returns whether to call it again right away */
 static dbus_bool_t
 process_command (DBusAuth *auth)
 {
-  DBusString command;
+  DBusAuthCommand command;
+  DBusString line;
   DBusString args;
   int eol;
   int i, j;
   dbus_bool_t retval;
 
-  /* _dbus_verbose ("  trying process_command()\n"); */
+  /* _dbus_verbose ("%s:   trying process_command()\n"); */
   
   retval = FALSE;
   
@@ -1057,77 +2156,53 @@ process_command (DBusAuth *auth)
   if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
     return FALSE;
   
-  if (!_dbus_string_init (&command, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&line))
     {
       auth->needed_memory = TRUE;
       return FALSE;
     }
 
-  if (!_dbus_string_init (&args, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&args))
     {
+      _dbus_string_free (&line);
       auth->needed_memory = TRUE;
       return FALSE;
     }
   
-  if (eol > _DBUS_ONE_MEGABYTE)
-    {
-      /* This is a giant line, someone is trying to hose us. */
-      if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command too long\"\r\n"))
-        goto out;
-      else
-        goto next_command;
-    }
-
-  if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0))
+  if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &line, 0))
     goto out;
 
-  if (!_dbus_string_validate_ascii (&command, 0,
-                                    _dbus_string_get_length (&command)))
+  if (!_dbus_string_validate_ascii (&line, 0,
+                                    _dbus_string_get_length (&line)))
     {
-      _dbus_verbose ("Command contained non-ASCII chars or embedded nul\n");
-      if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command contained non-ASCII\"\r\n"))
+      _dbus_verbose ("%s: Command contained non-ASCII chars or embedded nul\n",
+                     DBUS_AUTH_NAME (auth));
+      if (!send_error (auth, "Command contained non-ASCII"))
         goto out;
       else
         goto next_command;
     }
   
-  {
-    const char *q;
-    _dbus_string_get_const_data (&command, &q);
-    _dbus_verbose ("got command \"%s\"\n", q);
-  }
+  _dbus_verbose ("%s: got command \"%s\"\n",
+                 DBUS_AUTH_NAME (auth),
+                 _dbus_string_get_const_data (&line));
   
-  _dbus_string_find_blank (&command, 0, &i);
-  _dbus_string_skip_blank (&command, i, &j);
+  _dbus_string_find_blank (&line, 0, &i);
+  _dbus_string_skip_blank (&line, i, &j);
 
   if (j > i)
-    _dbus_string_delete (&command, i, j - i);
+    _dbus_string_delete (&line, i, j - i);
   
-  if (!_dbus_string_move (&command, i, &args, 0))
+  if (!_dbus_string_move (&line, i, &args, 0))
     goto out;
-  
-  i = 0;
-  while (auth->handlers[i].command != NULL)
-    {
-      if (_dbus_string_equal_c_str (&command,
-                                    auth->handlers[i].command))
-        {
-          _dbus_verbose ("Processing auth command %s\n",
-                         auth->handlers[i].command);
-          
-          if (!(* auth->handlers[i].func) (auth, &command, &args))
-            goto out;
-
-          break;
-        }
-      ++i;
-    }
 
-  if (auth->handlers[i].command == NULL)
-    {
-      if (!process_unknown (auth, &command, &args))
-        goto out;
-    }
+  /* FIXME 1.0 we should probably validate that only the allowed
+   * chars are in the command name
+   */
+  
+  command = lookup_command_from_name (&line);
+  if (!(* auth->state->handler) (auth, command, &args))
+    goto out;
 
  next_command:
   
@@ -1144,7 +2219,7 @@ process_command (DBusAuth *auth)
   
  out:
   _dbus_string_free (&args);
-  _dbus_string_free (&command);
+  _dbus_string_free (&line);
 
   if (!retval)
     auth->needed_memory = TRUE;
@@ -1164,25 +2239,41 @@ process_command (DBusAuth *auth)
 
 /**
  * Creates a new auth conversation object for the server side.
- * See doc/dbus-sasl-profile.txt for full details on what
- * this object does.
+ * See http://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol
+ * for full details on what this object does.
  *
  * @returns the new object or #NULL if no memory
  */
 DBusAuth*
-_dbus_auth_server_new (void)
+_dbus_auth_server_new (const DBusString *guid)
 {
   DBusAuth *auth;
   DBusAuthServer *server_auth;
+  DBusString guid_copy;
 
-  auth = _dbus_auth_new (sizeof (DBusAuthServer));
-  if (auth == NULL)
+  if (!_dbus_string_init (&guid_copy))
     return NULL;
 
-  auth->handlers = server_handlers;
+  if (!_dbus_string_copy (guid, 0, &guid_copy, 0))
+    {
+      _dbus_string_free (&guid_copy);
+      return NULL;
+    }
+
+  auth = _dbus_auth_new (sizeof (DBusAuthServer));
+  if (auth == NULL)
+    {
+      _dbus_string_free (&guid_copy);
+      return NULL;
+    }
+  
+  auth->side = auth_side_server;
+  auth->state = &server_state_waiting_for_auth;
 
   server_auth = DBUS_AUTH_SERVER (auth);
 
+  server_auth->guid = guid_copy;
+  
   /* perhaps this should be per-mechanism with a lower
    * max
    */
@@ -1194,8 +2285,8 @@ _dbus_auth_server_new (void)
 
 /**
  * Creates a new auth conversation object for the client side.
- * See doc/dbus-sasl-profile.txt for full details on what
- * this object does.
+ * See http://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol
+ * for full details on what this object does.
  *
  * @returns the new object or #NULL if no memory
  */
@@ -1203,42 +2294,91 @@ DBusAuth*
 _dbus_auth_client_new (void)
 {
   DBusAuth *auth;
+  DBusString guid_str;
+
+  if (!_dbus_string_init (&guid_str))
+    return NULL;
 
   auth = _dbus_auth_new (sizeof (DBusAuthClient));
   if (auth == NULL)
-    return NULL;
+    {
+      _dbus_string_free (&guid_str);
+      return NULL;
+    }
 
-  auth->handlers = client_handlers;
+  DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str;
 
-  /* Add a default mechanism to try */
-  if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
-                          (void*) &all_mechanisms[0]))
+  auth->side = auth_side_client;
+  auth->state = &client_state_need_send_auth;
+
+  /* Start the auth conversation by sending AUTH for our default
+   * mechanism */
+  if (!send_auth (auth, &all_mechanisms[0]))
     {
       _dbus_auth_unref (auth);
       return NULL;
     }
+  
+  return auth;
+}
+
+#ifdef ENABLE_KDBUS_TRANSPORT
+/**
+ * Creates a new auth conversation object for the client side of kdbus.
+ * In fact it only initialize structures and sets authenticated state
+ * because of different authentication-like mechanism in kdbus - policies
+ * TODO Probably to be checked and modified when kdbus will be documented
+ *
+ * @returns the new object or #NULL if no memory
+ */
+DBusAuth*
+_dbus_auth_client_new_kdbus (void)
+{
+  DBusAuth *auth;
+  DBusString guid_str;
+
+  if (!_dbus_string_init (&guid_str))
+    return NULL;
+
+  auth = _dbus_auth_new (sizeof (DBusAuthClient));
+  if (auth == NULL)
+    {
+      _dbus_string_free (&guid_str);
+      return NULL;
+    }
 
-  /* Now try the mechanism we just added */
-  if (!client_try_next_mechanism (auth))
+  DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str;
+
+  auth->side = auth_side_client;
+  auth->state = &common_state_authenticated;
+  auth->unix_fd_negotiated = TRUE;
+
+  /* Start the auth conversation by sending AUTH for our default
+   * mechanism */
+/*  if (!send_auth (auth, &all_mechanisms[0]))
     {
       _dbus_auth_unref (auth);
       return NULL;
-    }
-  
+    }*/
+
   return auth;
 }
+#endif
 
 /**
  * Increments the refcount of an auth object.
  *
  * @param auth the auth conversation
+ * @returns the auth conversation
  */
-void
+DBusAuth *
 _dbus_auth_ref (DBusAuth *auth)
 {
   _dbus_assert (auth != NULL);
   
   auth->refcount += 1;
+  
+  return auth;
 }
 
 /**
@@ -1259,21 +2399,70 @@ _dbus_auth_unref (DBusAuth *auth)
 
       if (DBUS_AUTH_IS_CLIENT (auth))
         {
+          _dbus_string_free (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
           _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
         }
+      else
+        {
+          _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
+
+          _dbus_string_free (& DBUS_AUTH_SERVER (auth)->guid);
+        }
 
+      if (auth->keyring)
+        _dbus_keyring_unref (auth->keyring);
+
+      _dbus_string_free (&auth->context);
+      _dbus_string_free (&auth->challenge);
       _dbus_string_free (&auth->identity);
       _dbus_string_free (&auth->incoming);
       _dbus_string_free (&auth->outgoing);
+
+      dbus_free_string_array (auth->allowed_mechs);
+
+      _dbus_credentials_unref (auth->credentials);
+      _dbus_credentials_unref (auth->authorized_identity);
+      _dbus_credentials_unref (auth->desired_identity);
+      
       dbus_free (auth);
     }
 }
 
 /**
+ * Sets an array of authentication mechanism names
+ * that we are willing to use.
+ *
+ * @param auth the auth conversation
+ * @param mechanisms #NULL-terminated array of mechanism names
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_auth_set_mechanisms (DBusAuth    *auth,
+                           const char **mechanisms)
+{
+  char **copy;
+
+  if (mechanisms != NULL)
+    {
+      copy = _dbus_dup_string_array (mechanisms);
+      if (copy == NULL)
+        return FALSE;
+    }
+  else
+    copy = NULL;
+  
+  dbus_free_string_array (auth->allowed_mechs);
+
+  auth->allowed_mechs = copy;
+
+  return TRUE;
+}
+
+/**
  * @param auth the auth conversation object
  * @returns #TRUE if we're in a final state
  */
-#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->need_disconnect || (auth)->authenticated)
+#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->state->handler == NULL)
 
 /**
  * Analyzes buffered input and moves the auth conversation forward,
@@ -1298,42 +2487,23 @@ _dbus_auth_do_work (DBusAuth *auth)
       if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
           _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
         {
-          auth->need_disconnect = TRUE;
-          _dbus_verbose ("Disconnecting due to excessive data buffered in auth phase\n");
-          break;
-        }
-
-      if (auth->mech == NULL &&
-          auth->already_got_mechanisms &&
-          DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
-        {
-          auth->need_disconnect = TRUE;
-          _dbus_verbose ("Disconnecting because we are out of mechanisms to try using\n");
+          goto_state (auth, &common_state_need_disconnect);
+          _dbus_verbose ("%s: Disconnecting due to excessive data buffered in auth phase\n",
+                         DBUS_AUTH_NAME (auth));
           break;
         }
     }
   while (process_command (auth));
 
-  if (DBUS_AUTH_IS_SERVER (auth) &&
-      DBUS_AUTH_SERVER (auth)->failures >=
-      DBUS_AUTH_SERVER (auth)->max_failures)
-    auth->need_disconnect = TRUE;
-
-  if (auth->need_disconnect)
-    return DBUS_AUTH_STATE_NEED_DISCONNECT;
-  else if (auth->authenticated)
-    {
-      if (_dbus_string_get_length (&auth->incoming) > 0)
-        return DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES;
-      else
-        return DBUS_AUTH_STATE_AUTHENTICATED;
-    }
-  else if (auth->needed_memory)
+  if (auth->needed_memory)
     return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
   else if (_dbus_string_get_length (&auth->outgoing) > 0)
     return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
-  else
-    return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
+  else if (auth->state == &common_state_need_disconnect)
+    return DBUS_AUTH_STATE_NEED_DISCONNECT;
+  else if (auth->state == &common_state_authenticated)
+    return DBUS_AUTH_STATE_AUTHENTICATED;
+  else return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
 }
 
 /**
@@ -1354,9 +2524,6 @@ _dbus_auth_get_bytes_to_send (DBusAuth          *auth,
 
   *str = NULL;
   
-  if (DBUS_AUTH_IN_END_STATE (auth))
-    return FALSE;
-
   if (_dbus_string_get_length (&auth->outgoing) == 0)
     return FALSE;
 
@@ -1377,44 +2544,50 @@ void
 _dbus_auth_bytes_sent (DBusAuth *auth,
                        int       bytes_sent)
 {
+  _dbus_verbose ("%s: Sent %d bytes of: %s\n",
+                 DBUS_AUTH_NAME (auth),
+                 bytes_sent,
+                 _dbus_string_get_const_data (&auth->outgoing));
+  
   _dbus_string_delete (&auth->outgoing,
                        0, bytes_sent);
-  
-  if (auth->authenticated_pending_output &&
-      _dbus_string_get_length (&auth->outgoing) == 0)
-    auth->authenticated = TRUE;
 }
 
 /**
- * Stores bytes received from the peer we're conversing with.
+ * Get a buffer to be used for reading bytes from the peer we're conversing
+ * with. Bytes should be appended to this buffer.
  *
  * @param auth the auth conversation
- * @param str the received bytes.
- * @returns #FALSE if not enough memory to store the bytes or we were already authenticated.
+ * @param buffer return location for buffer to append bytes to
  */
-dbus_bool_t
-_dbus_auth_bytes_received (DBusAuth   *auth,
-                           const DBusString *str)
+void
+_dbus_auth_get_buffer (DBusAuth     *auth,
+                       DBusString **buffer)
 {
   _dbus_assert (auth != NULL);
-  _dbus_assert (str != NULL);
+  _dbus_assert (!auth->buffer_outstanding);
   
-  if (DBUS_AUTH_IN_END_STATE (auth))
-    return FALSE;
+  *buffer = &auth->incoming;
 
-  auth->needed_memory = FALSE;
-  
-  if (!_dbus_string_copy (str, 0,
-                          &auth->incoming,
-                          _dbus_string_get_length (&auth->incoming)))
-    {
-      auth->needed_memory = TRUE;
-      return FALSE;
-    }
+  auth->buffer_outstanding = TRUE;
+}
 
-  _dbus_auth_do_work (auth);
-  
-  return TRUE;
+/**
+ * Returns a buffer with new data read into it.
+ *
+ * @param auth the auth conversation
+ * @param buffer the buffer being returned
+ * @param bytes_read number of new bytes added
+ */
+void
+_dbus_auth_return_buffer (DBusAuth               *auth,
+                          DBusString             *buffer,
+                          int                     bytes_read)
+{
+  _dbus_assert (buffer == &auth->incoming);
+  _dbus_assert (auth->buffer_outstanding);
+
+  auth->buffer_outstanding = FALSE;
 }
 
 /**
@@ -1424,22 +2597,32 @@ _dbus_auth_bytes_received (DBusAuth   *auth,
  * succeeded.
  *
  * @param auth the auth conversation
- * @param str string to append the unused bytes to
- * @returns #FALSE if not enough memory to return the bytes
+ * @param str return location for pointer to string of unused bytes
  */
-dbus_bool_t
-_dbus_auth_get_unused_bytes (DBusAuth   *auth,
-                             DBusString *str)
+void
+_dbus_auth_get_unused_bytes (DBusAuth           *auth,
+                             const DBusString **str)
 {
   if (!DBUS_AUTH_IN_END_STATE (auth))
-    return FALSE;
-  
-  if (!_dbus_string_move (&auth->incoming,
-                          0, str,
-                          _dbus_string_get_length (str)))
-    return FALSE;
+    return;
 
-  return TRUE;
+  *str = &auth->incoming;
+}
+
+
+/**
+ * Gets rid of unused bytes returned by _dbus_auth_get_unused_bytes()
+ * after we've gotten them and successfully moved them elsewhere.
+ *
+ * @param auth the auth conversation
+ */
+void
+_dbus_auth_delete_unused_bytes (DBusAuth *auth)
+{
+  if (!DBUS_AUTH_IN_END_STATE (auth))
+    return;
+
+  _dbus_string_set_length (&auth->incoming, 0);
 }
 
 /**
@@ -1453,7 +2636,7 @@ _dbus_auth_get_unused_bytes (DBusAuth   *auth,
 dbus_bool_t
 _dbus_auth_needs_encoding (DBusAuth *auth)
 {
-  if (!auth->authenticated)
+  if (auth->state != &common_state_authenticated)
     return FALSE;
   
   if (auth->mech != NULL)
@@ -1484,7 +2667,7 @@ _dbus_auth_encode_data (DBusAuth         *auth,
 {
   _dbus_assert (plaintext != encoded);
   
-  if (!auth->authenticated)
+  if (auth->state != &common_state_authenticated)
     return FALSE;
   
   if (_dbus_auth_needs_encoding (auth))
@@ -1512,7 +2695,7 @@ _dbus_auth_encode_data (DBusAuth         *auth,
 dbus_bool_t
 _dbus_auth_needs_decoding (DBusAuth *auth)
 {
-  if (!auth->authenticated)
+  if (auth->state != &common_state_authenticated)
     return FALSE;
     
   if (auth->mech != NULL)
@@ -1532,7 +2715,7 @@ _dbus_auth_needs_decoding (DBusAuth *auth)
  * the peer. If no encoding was negotiated, just copies the bytes (you
  * can avoid this by checking _dbus_auth_needs_decoding()).
  *
- * @todo We need to be able to distinguish "out of memory" error
+ * @todo 1.0? We need to be able to distinguish "out of memory" error
  * from "the data is hosed" error.
  *
  * @param auth the auth conversation
@@ -1547,7 +2730,7 @@ _dbus_auth_decode_data (DBusAuth         *auth,
 {
   _dbus_assert (plaintext != encoded);
   
-  if (!auth->authenticated)
+  if (auth->state != &common_state_authenticated)
     return FALSE;
   
   if (_dbus_auth_needs_decoding (auth))
@@ -1570,178 +2753,102 @@ _dbus_auth_decode_data (DBusAuth         *auth,
  *
  * @param auth the auth conversation
  * @param credentials the credentials received
+ * @returns #FALSE on OOM
  */
-void
+dbus_bool_t
 _dbus_auth_set_credentials (DBusAuth               *auth,
-                            const DBusCredentials  *credentials)
+                            DBusCredentials        *credentials)
 {
-  auth->credentials = *credentials;
+  _dbus_credentials_clear (auth->credentials);
+  return _dbus_credentials_add_credentials (auth->credentials,
+                                            credentials);
 }
 
 /**
  * Gets the identity we authorized the client as.  Apps may have
  * different policies as to what identities they allow.
  *
+ * Returned credentials are not a copy and should not be modified
+ *
  * @param auth the auth conversation
- * @param credentials the credentials we've authorized
+ * @returns the credentials we've authorized BY REFERENCE do not modify
  */
-void
-_dbus_auth_get_identity (DBusAuth               *auth,
-                         DBusCredentials        *credentials)
+DBusCredentials*
+_dbus_auth_get_identity (DBusAuth               *auth)
 {
-  if (auth->authenticated)
+  if (auth->state == &common_state_authenticated)
     {
-      *credentials = auth->authorized_identity;
+      return auth->authorized_identity;
     }
   else
     {
-      credentials->pid = -1;
-      credentials->uid = -1;
-      credentials->gid = -1;
+      /* FIXME instead of this, keep an empty credential around that
+       * doesn't require allocation or something
+       */
+      /* return empty credentials */
+      _dbus_assert (_dbus_credentials_are_empty (auth->authorized_identity));
+      return auth->authorized_identity;
     }
 }
 
-/** @} */
-
-#ifdef DBUS_BUILD_TESTS
-#include "dbus-test.h"
-#include "dbus-auth-script.h"
-#include <stdio.h>
-
-static dbus_bool_t
-process_test_subdir (const DBusString          *test_base_dir,
-                     const char                *subdir)
+/**
+ * Gets the GUID from the server if we've authenticated; gets
+ * #NULL otherwise.
+ * @param auth the auth object
+ * @returns the GUID in ASCII hex format
+ */
+const char*
+_dbus_auth_get_guid_from_server (DBusAuth *auth)
 {
-  DBusString test_directory;
-  DBusString filename;
-  DBusDirIter *dir;
-  dbus_bool_t retval;
-  DBusResultCode result;
-
-  retval = FALSE;
-  dir = NULL;
-  
-  if (!_dbus_string_init (&test_directory, _DBUS_INT_MAX))
-    _dbus_assert_not_reached ("didn't allocate test_directory\n");
-
-  _dbus_string_init_const (&filename, subdir);
-  
-  if (!_dbus_string_copy (test_base_dir, 0,
-                          &test_directory, 0))
-    _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
-  
-  if (!_dbus_concat_dir_and_file (&test_directory, &filename))    
-    _dbus_assert_not_reached ("couldn't allocate full path");
-
-  _dbus_string_free (&filename);
-  if (!_dbus_string_init (&filename, _DBUS_INT_MAX))
-    _dbus_assert_not_reached ("didn't allocate filename string\n");
-  
-  dir = _dbus_directory_open (&test_directory, &result);
-  if (dir == NULL)
-    {
-      const char *s;
-      _dbus_string_get_const_data (&test_directory, &s);
-      _dbus_warn ("Could not open %s: %s\n", s,
-                  dbus_result_to_string (result));
-      goto failed;
-    }
-
-  printf ("Testing:\n");
-  
-  result = DBUS_RESULT_SUCCESS;
- next:
-  while (_dbus_directory_get_next_file (dir, &filename, &result))
-    {
-      DBusString full_path;
-      
-      if (!_dbus_string_init (&full_path, _DBUS_INT_MAX))
-        _dbus_assert_not_reached ("couldn't init string");
-
-      if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
-        _dbus_assert_not_reached ("couldn't copy dir to full_path");
-
-      if (!_dbus_concat_dir_and_file (&full_path, &filename))
-        _dbus_assert_not_reached ("couldn't concat file to dir");
-
-      if (!_dbus_string_ends_with_c_str (&filename, ".auth-script"))
-        {
-          const char *filename_c;
-          _dbus_string_get_const_data (&filename, &filename_c);
-          _dbus_verbose ("Skipping non-.auth-script file %s\n",
-                         filename_c);
-          goto next;
-        }
-
-      {
-        const char *s;
-        _dbus_string_get_const_data (&filename, &s);
-        printf ("    %s\n", s);
-      }
-      
-      if (!_dbus_auth_script_run (&full_path))
-        {
-          _dbus_string_free (&full_path);
-          goto failed;
-        }
-      else
-        _dbus_string_free (&full_path);
-    }
-
-  if (result != DBUS_RESULT_SUCCESS)
-    {
-      const char *s;
-      _dbus_string_get_const_data (&test_directory, &s);
-      _dbus_warn ("Could not get next file in %s: %s\n",
-                  s, dbus_result_to_string (result));
-      goto failed;
-    }
-    
-  retval = TRUE;
+  _dbus_assert (DBUS_AUTH_IS_CLIENT (auth));
   
- failed:
-
-  if (dir)
-    _dbus_directory_close (dir);
-  _dbus_string_free (&test_directory);
-  _dbus_string_free (&filename);
-
-  return retval;
+  if (auth->state == &common_state_authenticated)
+    return _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
+  else
+    return NULL;
 }
 
-static dbus_bool_t
-process_test_dirs (const char *test_data_dir)
+/**
+ * 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)
 {
-  DBusString test_directory;
-  dbus_bool_t retval;
-
-  retval = FALSE;
-  
-  _dbus_string_init_const (&test_directory, test_data_dir);
-
-  if (!process_test_subdir (&test_directory, "auth"))
-    goto failed;
-
-  retval = TRUE;
-  
- failed:
+  return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context),
+                                   &auth->context, 0, _dbus_string_get_length (context));
+}
 
-  _dbus_string_free (&test_directory);
-  
-  return retval;
+/**
+ * Sets whether unix fd passing is potentially on the transport and
+ * hence shall be negotiated.
+ *
+ * @param auth the auth conversation
+ * @param b TRUE when unix fd passing shall be negotiated, otherwise FALSE
+ */
+void
+_dbus_auth_set_unix_fd_possible(DBusAuth *auth, dbus_bool_t b)
+{
+  auth->unix_fd_possible = b;
 }
 
+/**
+ * Queries whether unix fd passing was successfully negotiated.
+ *
+ * @param auth the auth conversion
+ * @returns #TRUE when unix fd passing was negotiated.
+ */
 dbus_bool_t
-_dbus_auth_test (const char *test_data_dir)
+_dbus_auth_get_unix_fd_negotiated(DBusAuth *auth)
 {
-  
-  if (test_data_dir == NULL)
-    return TRUE;
-  
-  if (!process_test_dirs (test_data_dir))
-    return FALSE;
-
-  return TRUE;
+  return auth->unix_fd_negotiated;
 }
 
-#endif /* DBUS_BUILD_TESTS */
+/** @} */
+
+/* tests in dbus-auth-util.c */