Clarify license declaration
[platform/upstream/dbus.git] / dbus / dbus-auth.c
index fabe8c5..330fb8f 100644 (file)
@@ -30,7 +30,6 @@
 #include "dbus-sha.h"
 #include "dbus-protocol.h"
 #include "dbus-credentials.h"
-#include "dbus-authorization.h"
 
 /**
  * @defgroup DBusAuth Authentication
@@ -38,7 +37,7 @@
  * @brief DBusAuth object
  *
  * DBusAuth manages the authentication negotiation when a connection
- * is first established, and also manage any encryption used over a
+ * is first established, and also manages any encryption used over a
  * connection.
  *
  * @todo some SASL profiles require sending the empty string as a
@@ -153,6 +152,7 @@ typedef struct
  */
 struct DBusAuth
 {
+  int refcount;           /**< reference count */
   const char *side;       /**< Client or server */
 
   DBusString incoming;    /**< Incoming data buffer */
@@ -169,7 +169,7 @@ struct DBusAuth
   DBusCredentials *credentials;          /**< Credentials read from socket
                                           */
 
-  DBusCredentials *authenticated_identity; /**< Credentials that are authorized */
+  DBusCredentials *authorized_identity; /**< Credentials that are authorized */
 
   DBusCredentials *desired_identity;    /**< Identity client has requested */
   
@@ -213,8 +213,6 @@ typedef struct
 {
   DBusAuth base;    /**< Parent class */
 
-  DBusAuthorization *authorization;             /* DBus Authorization callbacks */
-
   int failures;     /**< Number of times client has been rejected */
   int max_failures; /**< Number of times we reject before disconnect */
 
@@ -284,6 +282,9 @@ static const DBusAuthStateData client_state_need_send_auth = {
 static const DBusAuthStateData client_state_waiting_for_data = {
   "WaitingForData", handle_client_state_waiting_for_data
 };
+/* The WaitingForOK state doesn't appear to be used.
+ * See https://bugs.freedesktop.org/show_bug.cgi?id=97298 */
+_DBUS_GNUC_UNUSED
 static const DBusAuthStateData client_state_waiting_for_ok = {
   "WaitingForOK", handle_client_state_waiting_for_ok
 };
@@ -345,6 +346,8 @@ _dbus_auth_new (int size)
   if (auth == NULL)
     return NULL;
   
+  auth->refcount = 1;
+  
   auth->keyring = NULL;
   auth->cookie_id = -1;
   
@@ -379,8 +382,8 @@ _dbus_auth_new (int size)
   if (auth->credentials == NULL)
     goto enomem_6;
   
-  auth->authenticated_identity = _dbus_credentials_new ();
-  if (auth->authenticated_identity == NULL)
+  auth->authorized_identity = _dbus_credentials_new ();
+  if (auth->authorized_identity == NULL)
     goto enomem_7;
 
   auth->desired_identity = _dbus_credentials_new ();
@@ -394,7 +397,7 @@ _dbus_auth_new (int size)
   _dbus_credentials_unref (auth->desired_identity);
 #endif
  enomem_8:
-  _dbus_credentials_unref (auth->authenticated_identity);
+  _dbus_credentials_unref (auth->authorized_identity);
  enomem_7:
   _dbus_credentials_unref (auth->credentials);
  enomem_6:
@@ -421,7 +424,7 @@ shutdown_mech (DBusAuth *auth)
   auth->already_asked_for_initial_response = FALSE;
   _dbus_string_set_length (&auth->identity, 0);
 
-  _dbus_credentials_clear (auth->authenticated_identity);
+  _dbus_credentials_clear (auth->authorized_identity);
   _dbus_credentials_clear (auth->desired_identity);
   
   if (auth->mech != NULL)
@@ -524,10 +527,9 @@ sha1_handle_first_client_response (DBusAuth         *auth,
    */
   DBusString tmp;
   DBusString tmp2;
-  dbus_bool_t retval;
-  DBusError error;
-  
-  retval = FALSE;
+  dbus_bool_t retval = FALSE;
+  DBusError error = DBUS_ERROR_INIT;
+  DBusCredentials *myself = NULL;
 
   _dbus_string_set_length (&auth->challenge, 0);
   
@@ -564,6 +566,34 @@ sha1_handle_first_client_response (DBusAuth         *auth,
       return FALSE;
     }
 
+  myself = _dbus_credentials_new_from_current_process ();
+
+  if (myself == NULL)
+    goto out;
+
+  if (!_dbus_credentials_same_user (myself, auth->desired_identity))
+    {
+      /*
+       * DBUS_COOKIE_SHA1 is not suitable for authenticating that the
+       * client is anyone other than the user owning the process
+       * containing the DBusServer: we probably aren't allowed to write
+       * to other users' home directories. Even if we can (for example
+       * uid 0 on traditional Unix or CAP_DAC_OVERRIDE on Linux), we
+       * must not, because the other user controls their home directory,
+       * and could carry out symlink attacks to make us read from or
+       * write to unintended locations. It's difficult to avoid symlink
+       * attacks in a portable way, so we just don't try. This isn't a
+       * regression, because DBUS_COOKIE_SHA1 never worked for other
+       * users anyway.
+       */
+      _dbus_verbose ("%s: client tried to authenticate as \"%s\", "
+                     "but that doesn't match this process",
+                     DBUS_AUTH_NAME (auth),
+                     _dbus_string_get_const_data (data));
+      retval = send_rejected (auth);
+      goto out;
+    }
+
   /* 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.
@@ -578,7 +608,6 @@ sha1_handle_first_client_response (DBusAuth         *auth,
   
   if (auth->keyring == NULL)
     {
-      dbus_error_init (&error);
       auth->keyring = _dbus_keyring_new_for_credentials (auth->desired_identity,
                                                          &auth->context,
                                                          &error);
@@ -610,7 +639,6 @@ sha1_handle_first_client_response (DBusAuth         *auth,
 
   _dbus_assert (auth->keyring != NULL);
 
-  dbus_error_init (&error);
   auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error);
   if (auth->cookie_id < 0)
     {
@@ -640,8 +668,25 @@ sha1_handle_first_client_response (DBusAuth         *auth,
   if (!_dbus_string_append (&tmp2, " "))
     goto out;  
   
-  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
-    goto out;
+  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES, &error))
+    {
+      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 generating challenge: %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;
+        }
+    }
 
   _dbus_string_set_length (&auth->challenge, 0);
   if (!_dbus_string_hex_encode (&tmp, 0, &auth->challenge, 0))
@@ -663,6 +708,9 @@ sha1_handle_first_client_response (DBusAuth         *auth,
   _dbus_string_zero (&tmp2);
   _dbus_string_free (&tmp2);
 
+  if (myself != NULL)
+    _dbus_credentials_unref (myself);
+
   return retval;
 }
 
@@ -742,13 +790,13 @@ sha1_handle_second_client_response (DBusAuth         *auth,
       goto out_3;
     }
 
-  if (!_dbus_credentials_add_credentials (auth->authenticated_identity,
+  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->authenticated_identity,
+  if (!_dbus_credentials_add_credential (auth->authorized_identity,
                                          DBUS_CREDENTIAL_UNIX_PROCESS_ID,
                                          auth->credentials))
     goto out_3;
@@ -826,7 +874,7 @@ handle_client_data_cookie_sha1_mech (DBusAuth         *auth,
    * 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;
+  dbus_bool_t retval = FALSE;
   DBusString context;
   DBusString cookie_id_str;
   DBusString server_challenge;
@@ -835,9 +883,8 @@ handle_client_data_cookie_sha1_mech (DBusAuth         *auth,
   DBusString tmp;
   int i, j;
   long val;
-  
-  retval = FALSE;                 
-  
+  DBusError error = DBUS_ERROR_INIT;
+
   if (!_dbus_string_find_blank (data, 0, &i))
     {
       if (send_error (auth,
@@ -903,9 +950,6 @@ handle_client_data_cookie_sha1_mech (DBusAuth         *auth,
 
   if (auth->keyring == NULL)
     {
-      DBusError error;
-
-      dbus_error_init (&error);
       auth->keyring = _dbus_keyring_new_for_credentials (NULL,
                                                          &context,
                                                          &error);
@@ -942,9 +986,28 @@ handle_client_data_cookie_sha1_mech (DBusAuth         *auth,
   
   if (!_dbus_string_init (&tmp))
     goto out_3;
-  
-  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
-    goto out_4;
+
+  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES, &error))
+    {
+      if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+        {
+          dbus_error_free (&error);
+          goto out_4;
+        }
+      else
+        {
+          _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+          _dbus_verbose ("%s: Failed to generate challenge: %s\n",
+                         DBUS_AUTH_NAME (auth), error.message);
+
+          if (send_error (auth, "Failed to generate challenge"))
+            retval = TRUE; /* retval is only about mem */
+
+          dbus_error_free (&error);
+          goto out_4;
+        }
+    }
 
   if (!_dbus_string_init (&client_challenge))
     goto out_4;
@@ -1098,43 +1161,32 @@ handle_server_data_external_mech (DBusAuth         *auth,
                                       auth->desired_identity))
     {
       /* client has authenticated */
-      if (!_dbus_credentials_add_credentials (auth->authenticated_identity,
+      if (!_dbus_credentials_add_credentials (auth->authorized_identity,
                                               auth->desired_identity))
         return FALSE;
 
-      /* also copy process ID from the socket credentials
+      /* also copy misc process info from the socket credentials
        */
-      if (!_dbus_credentials_add_credential (auth->authenticated_identity,
+      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->authenticated_identity,
+      if (!_dbus_credentials_add_credential (auth->authorized_identity,
                                              DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
                                              auth->credentials))
         return FALSE;
 
-      /* Do a first authorization of the transport, in order to REJECT
-       * immediately connection if needed (FDO#39720), transport will
-       * re-authorize later, but it will close the connection on fail,
-       * we want to REJECT now if possible */
-      if (_dbus_authorization_do_authorization (DBUS_AUTH_SERVER (auth)->authorization,
-                                                auth->authenticated_identity))
-        {
-          if (!send_ok (auth))
-            return FALSE;
-        }
-      else
-        {
-          _dbus_verbose ("%s: authenticated identity not authorized by server\n",
-                         DBUS_AUTH_NAME (auth));
-          return send_rejected (auth);
-        }
+      if (!_dbus_credentials_add_credential (auth->authorized_identity,
+                                             DBUS_CREDENTIAL_LINUX_SECURITY_LABEL,
+                                             auth->credentials))
+        return FALSE;
 
-      _dbus_verbose ("%s: authenticated and authorized client based on "
-          "socket credentials\n", DBUS_AUTH_NAME (auth));
+      if (!send_ok (auth))
+        return FALSE;
+
+      _dbus_verbose ("%s: authenticated client based on socket credentials\n",
+                     DBUS_AUTH_NAME (auth));
 
       return TRUE;
     }
@@ -1229,7 +1281,7 @@ handle_server_data_anonymous_mech (DBusAuth         *auth,
 
   /* Copy process ID from the socket credentials
    */
-  if (!_dbus_credentials_add_credential (auth->authenticated_identity,
+  if (!_dbus_credentials_add_credential (auth->authorized_identity,
                                          DBUS_CREDENTIAL_UNIX_PROCESS_ID,
                                          auth->credentials))
     return FALSE;
@@ -1465,9 +1517,14 @@ send_rejected (DBusAuth *auth)
                             "REJECTED"))
     goto nomem;
 
-  i = 0;
-  while (all_mechanisms[i].mechanism != NULL)
+  for (i = 0; all_mechanisms[i].mechanism != NULL; i++)
     {
+      /* skip mechanisms that aren't allowed */
+      if (auth->allowed_mechs != NULL &&
+          !_dbus_string_array_contains ((const char**)auth->allowed_mechs,
+                                        all_mechanisms[i].mechanism))
+        continue;
+
       if (!_dbus_string_append (&command,
                                 " "))
         goto nomem;
@@ -1475,8 +1532,6 @@ send_rejected (DBusAuth *auth)
       if (!_dbus_string_append (&command,
                                 all_mechanisms[i].mechanism))
         goto nomem;
-      
-      ++i;
     }
   
   if (!_dbus_string_append (&command, "\r\n"))
@@ -2258,8 +2313,7 @@ process_command (DBusAuth *auth)
  * @returns the new object or #NULL if no memory
  */
 DBusAuth*
-_dbus_auth_server_new (const DBusString *guid,
-                       DBusAuthorization *authorization)
+_dbus_auth_server_new (const DBusString *guid)
 {
   DBusAuth *auth;
   DBusAuthServer *server_auth;
@@ -2287,8 +2341,7 @@ _dbus_auth_server_new (const DBusString *guid,
   server_auth = DBUS_AUTH_SERVER (auth);
 
   server_auth->guid = guid_copy;
-  server_auth->authorization = authorization;
-
+  
   /* perhaps this should be per-mechanism with a lower
    * max
    */
@@ -2330,7 +2383,7 @@ _dbus_auth_client_new (void)
    * mechanism */
   if (!send_auth (auth, &all_mechanisms[0]))
     {
-      _dbus_auth_free (auth);
+      _dbus_auth_unref (auth);
       return NULL;
     }
   
@@ -2338,45 +2391,99 @@ _dbus_auth_client_new (void)
 }
 
 /**
- * Free memory allocated for an auth object.
+ * Creates a new auth conversation object for the client side.
+ * In fact it only initialize structures and sets authenticated state
+ * and leaves authentication to different transport-dependent
+ * mechanisms.
  *
- * @param auth the auth conversation
+ * @returns the new object or #NULL if no memory
  */
-void
-_dbus_auth_free (DBusAuth *auth)
+DBusAuth*
+_dbus_auth_client_new_authenticated (void)
 {
-  _dbus_assert (auth != NULL);
+  DBusAuth *auth;
+  DBusString guid_str;
 
-  shutdown_mech (auth);
+  if (!_dbus_string_init (&guid_str))
+    return NULL;
 
-  if (DBUS_AUTH_IS_CLIENT (auth))
+  auth = _dbus_auth_new (sizeof (DBusAuthClient));
+  if (auth == NULL)
     {
-      _dbus_string_free (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
-      _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
+      _dbus_string_free (&guid_str);
+      return NULL;
     }
-  else
+
+  DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str;
+
+  auth->side = auth_side_client;
+  auth->state = &common_state_authenticated;
+  auth->unix_fd_negotiated = TRUE;
+
+  return auth;
+}
+
+/**
+ * Increments the refcount of an auth object.
+ *
+ * @param auth the auth conversation
+ * @returns the auth conversation
+ */
+DBusAuth *
+_dbus_auth_ref (DBusAuth *auth)
+{
+  _dbus_assert (auth != NULL);
+  
+  auth->refcount += 1;
+  
+  return auth;
+}
+
+/**
+ * Decrements the refcount of an auth object.
+ *
+ * @param auth the auth conversation
+ */
+void
+_dbus_auth_unref (DBusAuth *auth)
+{
+  _dbus_assert (auth != NULL);
+  _dbus_assert (auth->refcount > 0);
+
+  auth->refcount -= 1;
+  if (auth->refcount == 0)
     {
-      _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
+      shutdown_mech (auth);
 
-      _dbus_string_free (& DBUS_AUTH_SERVER (auth)->guid);
-    }
+      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));
 
-  if (auth->keyring)
-    _dbus_keyring_unref (auth->keyring);
+          _dbus_string_free (& DBUS_AUTH_SERVER (auth)->guid);
+        }
 
-  _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);
+      if (auth->keyring)
+        _dbus_keyring_unref (auth->keyring);
 
-  dbus_free_string_array (auth->allowed_mechs);
+      _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_credentials_unref (auth->credentials);
-  _dbus_credentials_unref (auth->authenticated_identity);
-  _dbus_credentials_unref (auth->desired_identity);
+      dbus_free_string_array (auth->allowed_mechs);
 
-  dbus_free (auth);
+      _dbus_credentials_unref (auth->credentials);
+      _dbus_credentials_unref (auth->authorized_identity);
+      _dbus_credentials_unref (auth->desired_identity);
+      
+      dbus_free (auth);
+    }
 }
 
 /**
@@ -2528,12 +2635,10 @@ _dbus_auth_get_buffer (DBusAuth     *auth,
  *
  * @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)
+                          DBusString             *buffer)
 {
   _dbus_assert (buffer == &auth->incoming);
   _dbus_assert (auth->buffer_outstanding);
@@ -2729,7 +2834,7 @@ _dbus_auth_get_identity (DBusAuth               *auth)
 {
   if (auth->state == &common_state_authenticated)
     {
-      return auth->authenticated_identity;
+      return auth->authorized_identity;
     }
   else
     {
@@ -2737,8 +2842,8 @@ _dbus_auth_get_identity (DBusAuth               *auth)
        * doesn't require allocation or something
        */
       /* return empty credentials */
-      _dbus_assert (_dbus_credentials_are_empty (auth->authenticated_identity));
-      return auth->authenticated_identity;
+      _dbus_assert (_dbus_credentials_are_empty (auth->authorized_identity));
+      return auth->authorized_identity;
     }
 }
 
@@ -2800,6 +2905,45 @@ _dbus_auth_get_unix_fd_negotiated(DBusAuth *auth)
   return auth->unix_fd_negotiated;
 }
 
+/**
+ * Queries whether the given auth mechanism is supported.
+ *
+ * @param auth the auth mechanism to query for
+ * @returns #TRUE when auth mechanism is supported
+ */
+dbus_bool_t
+_dbus_auth_is_supported_mechanism (DBusString *name)
+{
+  _dbus_assert (name != NULL);
+
+  return find_mech (name, NULL) != NULL;
+}
+
+/**
+ * Return a human-readable string containing all supported auth mechanisms.
+ *
+ * @param string to hold the supported auth mechanisms
+ * @returns #FALSE on oom
+ */
+dbus_bool_t
+_dbus_auth_dump_supported_mechanisms (DBusString *buffer)
+{
+  unsigned int i;
+  _dbus_assert (buffer != NULL);
+
+  for (i = 0; all_mechanisms[i].mechanism != NULL; i++)
+    {
+      if (i > 0)
+        {
+          if (!_dbus_string_append (buffer, ", "))
+            return FALSE;
+        }
+      if (!_dbus_string_append (buffer, all_mechanisms[i].mechanism))
+        return FALSE;
+    }
+  return TRUE;
+}
+
 /** @} */
 
 /* tests in dbus-auth-util.c */