Merge "Optional autogen.sh flag --enable-kdbus-transport added allowing to compile...
[platform/upstream/dbus.git] / dbus / dbus-auth.c
index 83dfc8a..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, 2004 Red Hat Inc.
+ * Copyright (C) 2013  Samsung Electronics
  *
- * Licensed under the Academic Free License version 2.0
+ * 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"
 #include "dbus-keyring.h"
 #include "dbus-sha.h"
-#include "dbus-userdb.h"
 #include "dbus-protocol.h"
+#include "dbus-credentials.h"
 
 /**
  * @defgroup DBusAuth Authentication
  * challenge/response, but we don't currently allow that in our
  * protocol.
  *
- * @todo DBusAuth really needs to be rewritten as an explicit state
- * machine. Right now it's too hard to prove to yourself by inspection
- * that it works.
- *
  * @todo right now sometimes both ends will block waiting for input
  * from the other end, e.g. if there's an error during
  * DBUS_COOKIE_SHA1.
@@ -126,7 +125,9 @@ typedef enum {
   DBUS_AUTH_COMMAND_REJECTED,
   DBUS_AUTH_COMMAND_OK,
   DBUS_AUTH_COMMAND_ERROR,
-  DBUS_AUTH_COMMAND_UNKNOWN
+  DBUS_AUTH_COMMAND_UNKNOWN,
+  DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD,
+  DBUS_AUTH_COMMAND_AGREE_UNIX_FD
 } DBusAuthCommand;
 
 /**
@@ -166,13 +167,12 @@ struct DBusAuth
                                           *   as.
                                           */
   
-  DBusCredentials credentials;      /**< Credentials read from socket,
-                                     * fields may be -1
-                                     */
+  DBusCredentials *credentials;          /**< Credentials read from socket
+                                          */
 
-  DBusCredentials authorized_identity; /**< Credentials that are authorized */
+  DBusCredentials *authorized_identity; /**< Credentials that are authorized */
 
-  DBusCredentials desired_identity;    /**< Identity client has requested */
+  DBusCredentials *desired_identity;    /**< Identity client has requested */
   
   DBusString context;               /**< Cookie scope */
   DBusKeyring *keyring;             /**< Keyring for cookie mechanism. */
@@ -189,6 +189,9 @@ struct DBusAuth
   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 */
 };
 
 /**
@@ -199,6 +202,8 @@ typedef struct
   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;
 
@@ -211,6 +216,8 @@ typedef struct
 
   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;
 
@@ -226,6 +233,8 @@ static dbus_bool_t send_error                (DBusAuth *auth,
 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);
 
 /**
  * Client states
@@ -264,6 +273,9 @@ static dbus_bool_t handle_client_state_waiting_for_ok     (DBusAuth         *aut
 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
@@ -277,7 +289,10 @@ static const DBusAuthStateData 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
+};
+
 /**
  * Common terminal states.  Terminal states have handler == NULL.
  */
@@ -330,10 +345,6 @@ _dbus_auth_new (int size)
     return NULL;
   
   auth->refcount = 1;
-
-  _dbus_credentials_clear (&auth->credentials);
-  _dbus_credentials_clear (&auth->authorized_identity);
-  _dbus_credentials_clear (&auth->desired_identity);
   
   auth->keyring = NULL;
   auth->cookie_id = -1;
@@ -364,9 +375,31 @@ _dbus_auth_new (int size)
   /* 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;
+  
+  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:
@@ -389,8 +422,8 @@ shutdown_mech (DBusAuth *auth)
   auth->already_asked_for_initial_response = FALSE;
   _dbus_string_set_length (&auth->identity, 0);
 
-  _dbus_credentials_clear (&auth->authorized_identity);
-  _dbus_credentials_clear (&auth->desired_identity);
+  _dbus_credentials_clear (auth->authorized_identity);
+  _dbus_credentials_clear (auth->desired_identity);
   
   if (auth->mech != NULL)
     {
@@ -406,6 +439,10 @@ 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.
@@ -512,7 +549,7 @@ sha1_handle_first_client_response (DBusAuth         *auth,
         }
     }
       
-  if (!_dbus_credentials_from_username (data, &auth->desired_identity))
+  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));
@@ -533,8 +570,8 @@ sha1_handle_first_client_response (DBusAuth         *auth,
    * a different DBusAuth for every connection.
    */
   if (auth->keyring &&
-      !_dbus_keyring_is_for_user (auth->keyring,
-                                  data))
+      !_dbus_keyring_is_for_credentials (auth->keyring,
+                                         auth->desired_identity))
     {
       _dbus_keyring_unref (auth->keyring);
       auth->keyring = NULL;
@@ -542,12 +579,10 @@ sha1_handle_first_client_response (DBusAuth         *auth,
   
   if (auth->keyring == NULL)
     {
-      DBusError error;
-
       dbus_error_init (&error);
-      auth->keyring = _dbus_keyring_new_homedir (data,
-                                                 &auth->context,
-                                                 &error);
+      auth->keyring = _dbus_keyring_new_for_credentials (auth->desired_identity,
+                                                         &auth->context,
+                                                         &error);
 
       if (auth->keyring == NULL)
         {
@@ -707,14 +742,24 @@ sha1_handle_second_client_response (DBusAuth         *auth,
         retval = TRUE;
       goto out_3;
     }
-      
+
+  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 with UID "DBUS_UID_FORMAT" using DBUS_COOKIE_SHA1\n",
-                 DBUS_AUTH_NAME (auth), auth->desired_identity.uid);
+  _dbus_verbose ("%s: authenticated client using DBUS_COOKIE_SHA1\n",
+                 DBUS_AUTH_NAME (auth));
   
-  auth->authorized_identity = auth->desired_identity;
   retval = TRUE;
   
  out_3:
@@ -750,15 +795,18 @@ static dbus_bool_t
 handle_client_initial_response_cookie_sha1_mech (DBusAuth   *auth,
                                                  DBusString *response)
 {
-  const DBusString *username;
+  DBusString username;
   dbus_bool_t retval;
 
   retval = FALSE;
 
-  if (!_dbus_username_from_current_process (&username))
+  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,
+  if (!_dbus_string_hex_encode (&username, 0,
                                response,
                                _dbus_string_get_length (response)))
     goto out_0;
@@ -766,6 +814,8 @@ handle_client_initial_response_cookie_sha1_mech (DBusAuth   *auth,
   retval = TRUE;
   
  out_0:
+  _dbus_string_free (&username);
+  
   return retval;
 }
 
@@ -857,9 +907,9 @@ handle_client_data_cookie_sha1_mech (DBusAuth         *auth,
       DBusError error;
 
       dbus_error_init (&error);
-      auth->keyring = _dbus_keyring_new_homedir (NULL,
-                                                 &context,
-                                                 &error);
+      auth->keyring = _dbus_keyring_new_for_credentials (NULL,
+                                                         &context,
+                                                         &error);
 
       if (auth->keyring == NULL)
         {
@@ -963,11 +1013,15 @@ handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth)
   _dbus_string_set_length (&auth->challenge, 0);
 }
 
+/*
+ * EXTERNAL mechanism
+ */
+
 static dbus_bool_t
 handle_server_data_external_mech (DBusAuth         *auth,
                                   const DBusString *data)
 {
-  if (auth->credentials.uid == DBUS_UID_UNSET)
+  if (_dbus_credentials_are_anonymous (auth->credentials))
     {
       _dbus_verbose ("%s: no credentials, mechanism EXTERNAL can't authenticate\n",
                      DBUS_AUTH_NAME (auth));
@@ -1000,13 +1054,14 @@ handle_server_data_external_mech (DBusAuth         *auth,
           _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);
+  _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
@@ -1015,12 +1070,16 @@ handle_server_data_external_mech (DBusAuth         *auth,
    */
   if (_dbus_string_get_length (&auth->identity) == 0)
     {
-      auth->desired_identity.uid = auth->credentials.uid;
+      if (!_dbus_credentials_add_credentials (auth->desired_identity,
+                                              auth->credentials))
+        {
+          return FALSE; /* OOM */
+        }
     }
   else
     {
-      if (!_dbus_uid_from_string (&auth->identity,
-                                  &auth->desired_identity.uid))
+      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));
@@ -1028,7 +1087,7 @@ handle_server_data_external_mech (DBusAuth         *auth,
         }
     }
 
-  if (auth->desired_identity.uid == DBUS_UID_UNSET)
+  if (_dbus_credentials_are_anonymous (auth->desired_identity))
     {
       _dbus_verbose ("%s: desired user %s is no good\n",
                      DBUS_AUTH_NAME (auth),
@@ -1036,32 +1095,40 @@ handle_server_data_external_mech (DBusAuth         *auth,
       return send_rejected (auth);
     }
   
-  if (_dbus_credentials_match (&auth->desired_identity,
-                               &auth->credentials))
+  if (_dbus_credentials_are_superset (auth->credentials,
+                                      auth->desired_identity))
     {
-      /* client has authenticated */      
-      if (!send_ok (auth))
+      /* client has authenticated */
+      if (!_dbus_credentials_add_credentials (auth->authorized_identity,
+                                              auth->desired_identity))
         return FALSE;
 
-      _dbus_verbose ("%s: authenticated client with UID "DBUS_UID_FORMAT
-                     " matching socket credentials UID "DBUS_UID_FORMAT"\n",
-                     DBUS_AUTH_NAME (auth),
-                     auth->desired_identity.uid,
-                     auth->credentials.uid);
-      
-      auth->authorized_identity.uid = auth->desired_identity.uid;
+      /* 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: credentials uid="DBUS_UID_FORMAT
-                     " gid="DBUS_GID_FORMAT
-                     " do not allow uid="DBUS_UID_FORMAT
-                     " gid="DBUS_GID_FORMAT"\n",
-                     DBUS_AUTH_NAME (auth),
-                     auth->credentials.uid, auth->credentials.gid,
-                     auth->desired_identity.uid, auth->desired_identity.gid);
+      _dbus_verbose ("%s: desired identity not found in socket credentials\n",
+                     DBUS_AUTH_NAME (auth));
       return send_rejected (auth);
     }
 }
@@ -1085,9 +1152,8 @@ handle_client_initial_response_external_mech (DBusAuth         *auth,
 
   if (!_dbus_string_init (&plaintext))
     return FALSE;
-  
-  if (!_dbus_string_append_uint (&plaintext,
-                                 _dbus_getuid ()))
+
+  if (!_dbus_append_user_from_current_process (&plaintext))
     goto failed;
 
   if (!_dbus_string_hex_encode (&plaintext, 0,
@@ -1118,13 +1184,115 @@ 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.
- * What I eventually want to have is:
+ * 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
@@ -1145,6 +1313,14 @@ all_mechanisms[] = {
     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 }
 };
 
@@ -1327,25 +1503,80 @@ send_error (DBusAuth *auth, const char *message)
 static dbus_bool_t
 send_ok (DBusAuth *auth)
 {
-  if (_dbus_string_append (&auth->outgoing, "OK\r\n"))
+  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
-    return FALSE;
+    {
+      _dbus_string_set_length (&auth->outgoing, orig_len);
+      return FALSE;
+    }
 }
 
 static dbus_bool_t
-send_begin (DBusAuth *auth)
+send_begin (DBusAuth         *auth)
 {
-  if (_dbus_string_append (&auth->outgoing, "BEGIN\r\n"))
+
+  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)
     {
-      goto_state (auth, &common_state_authenticated);
+      _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;
     }
-  else
-    return FALSE;
+
+  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
@@ -1362,8 +1593,8 @@ send_cancel (DBusAuth *auth)
 
 static dbus_bool_t
 process_data (DBusAuth             *auth,
-             const DBusString     *args,
-             DBusAuthDataFunction  data_func)
+              const DBusString     *args,
+              DBusAuthDataFunction  data_func)
 {
   int end;
   DBusString decoded;
@@ -1405,6 +1636,33 @@ process_data (DBusAuth             *auth,
 }
 
 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)
@@ -1495,9 +1753,13 @@ handle_server_state_waiting_for_auth  (DBusAuth         *auth,
     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");
     }
@@ -1524,9 +1786,13 @@ handle_server_state_waiting_for_data  (DBusAuth         *auth,
       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");
     }
@@ -1549,9 +1815,16 @@ handle_server_state_waiting_for_begin (DBusAuth         *auth,
       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");
 
@@ -1623,14 +1896,22 @@ record_mechanisms (DBusAuth         *auth,
            * it lists things in that order anyhow.
            */
 
-          _dbus_verbose ("%s: Adding mechanism %s to list we will try\n",
-                         DBUS_AUTH_NAME (auth), 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))
+              if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
+                                      (void*) mech))
+                {
+                  _dbus_string_free (&m);
+                  goto nomem;
+                }
+            }
+          else
             {
-              _dbus_string_free (&m);
-              goto nomem;
+              _dbus_verbose ("%s: Already tried mechanism %s; not adding to list we will try\n",
+                             DBUS_AUTH_NAME (auth), mech->mechanism);
             }
         }
       else
@@ -1708,7 +1989,7 @@ handle_client_state_waiting_for_data (DBusAuth         *auth,
       return process_rejected (auth, args);
 
     case DBUS_AUTH_COMMAND_OK:
-      return send_begin (auth);
+      return process_ok(auth, args);
 
     case DBUS_AUTH_COMMAND_ERROR:
       return send_cancel (auth);
@@ -1717,6 +1998,8 @@ handle_client_state_waiting_for_data (DBusAuth         *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");
     }
@@ -1733,7 +2016,7 @@ handle_client_state_waiting_for_ok (DBusAuth         *auth,
       return process_rejected (auth, args);
 
     case DBUS_AUTH_COMMAND_OK:
-      return send_begin (auth);
+      return process_ok(auth, args);
 
     case DBUS_AUTH_COMMAND_DATA:
     case DBUS_AUTH_COMMAND_ERROR:
@@ -1743,6 +2026,8 @@ handle_client_state_waiting_for_ok (DBusAuth         *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");
     }
@@ -1765,12 +2050,46 @@ handle_client_state_waiting_for_reject (DBusAuth         *auth,
     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;
     }
 }
 
+static dbus_bool_t
+handle_client_state_waiting_for_agree_unix_fd(DBusAuth         *auth,
+                                              DBusAuthCommand   command,
+                                              const DBusString *args)
+{
+  switch (command)
+    {
+    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");
+    }
+}
+
 /**
  * Mapping from command name to enum
  */
@@ -1779,14 +2098,16 @@ typedef struct {
   DBusAuthCommand command; /**< Corresponding enum */
 } DBusAuthCommandName;
 
-static 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 }
+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
@@ -1805,7 +2126,8 @@ lookup_command_from_name (DBusString *command)
 }
 
 static void
-goto_state (DBusAuth *auth, const DBusAuthStateData *state)
+goto_state (DBusAuth *auth,
+            const DBusAuthStateData *state)
 {
   _dbus_verbose ("%s: going from state %s to state %s\n",
                  DBUS_AUTH_NAME (auth),
@@ -1873,6 +2195,10 @@ process_command (DBusAuth *auth)
   
   if (!_dbus_string_move (&line, i, &args, 0))
     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))
@@ -1913,26 +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;
 
+  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
    */
@@ -1944,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
  */
@@ -1953,10 +2294,19 @@ 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;
+    }
+
+  DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str;
 
   auth->side = auth_side_client;
   auth->state = &client_state_need_send_auth;
@@ -1972,6 +2322,49 @@ _dbus_auth_client_new (void)
   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;
+    }
+
+  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.
  *
@@ -2006,8 +2399,15 @@ _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);
@@ -2019,6 +2419,10 @@ _dbus_auth_unref (DBusAuth *auth)
       _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);
     }
@@ -2311,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
@@ -2349,29 +2753,59 @@ _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->state == &common_state_authenticated)
-    *credentials = auth->authorized_identity;
+    {
+      return auth->authorized_identity;
+    }
   else
-    _dbus_credentials_clear (credentials);
+    {
+      /* 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;
+    }
+}
+
+/**
+ * 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)
+{
+  _dbus_assert (DBUS_AUTH_IS_CLIENT (auth));
+  
+  if (auth->state == &common_state_authenticated)
+    return _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
+  else
+    return NULL;
 }
 
 /**
@@ -2390,141 +2824,31 @@ _dbus_auth_set_context (DBusAuth               *auth,
                                    &auth->context, 0, _dbus_string_get_length (context));
 }
 
-/** @} */
-
-#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)
-{
-  DBusString test_directory;
-  DBusString filename;
-  DBusDirIter *dir;
-  dbus_bool_t retval;
-  DBusError error;
-
-  retval = FALSE;
-  dir = NULL;
-  
-  if (!_dbus_string_init (&test_directory))
-    _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_assert_not_reached ("didn't allocate filename string\n");
-
-  dbus_error_init (&error);
-  dir = _dbus_directory_open (&test_directory, &error);
-  if (dir == NULL)
-    {
-      _dbus_warn ("Could not open %s: %s\n",
-                  _dbus_string_get_const_data (&test_directory),
-                  error.message);
-      dbus_error_free (&error);
-      goto failed;
-    }
-
-  printf ("Testing %s:\n", subdir);
-  
- next:
-  while (_dbus_directory_get_next_file (dir, &filename, &error))
-    {
-      DBusString full_path;
-      
-      if (!_dbus_string_init (&full_path))
-        _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"))
-        {
-          _dbus_verbose ("Skipping non-.auth-script file %s\n",
-                         _dbus_string_get_const_data (&filename));
-         _dbus_string_free (&full_path);
-          goto next;
-        }
-
-      printf ("    %s\n", _dbus_string_get_const_data (&filename));
-      
-      if (!_dbus_auth_script_run (&full_path))
-        {
-          _dbus_string_free (&full_path);
-          goto failed;
-        }
-      else
-        _dbus_string_free (&full_path);
-    }
-
-  if (dbus_error_is_set (&error))
-    {
-      _dbus_warn ("Could not get next file in %s: %s\n",
-                  _dbus_string_get_const_data (&test_directory), error.message);
-      dbus_error_free (&error);
-      goto failed;
-    }
-    
-  retval = TRUE;
-  
- failed:
-
-  if (dir)
-    _dbus_directory_close (dir);
-  _dbus_string_free (&test_directory);
-  _dbus_string_free (&filename);
-
-  return retval;
-}
-
-static dbus_bool_t
-process_test_dirs (const char *test_data_dir)
+/**
+ * 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)
 {
-  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:
-
-  _dbus_string_free (&test_directory);
-  
-  return retval;
+  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 */