Clarify license declaration
[platform/upstream/dbus.git] / dbus / dbus-auth.c
index 6215f9e..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
@@ -170,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 */
   
@@ -214,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 */
 
@@ -285,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
 };
@@ -382,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 ();
@@ -397,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:
@@ -424,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)
@@ -527,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);
   
@@ -567,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.
@@ -581,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);
@@ -613,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)
     {
@@ -643,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))
@@ -666,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;
 }
 
@@ -745,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;
@@ -829,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;
@@ -838,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,
@@ -906,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);
@@ -945,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;
@@ -1101,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->authorized_identity))
-        {
-          if (!send_ok (auth))
-            return FALSE;
-        }
-      else
-        {
-          _dbus_verbose ("%s: desired identity does not match server identity: "
-              "not authorized\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;
+
+      if (!send_ok (auth))
+        return FALSE;
 
-      _dbus_verbose ("%s: authenticated and authorized client based on "
-          "socket credentials\n", DBUS_AUTH_NAME (auth));
+      _dbus_verbose ("%s: authenticated client based on socket credentials\n",
+                     DBUS_AUTH_NAME (auth));
 
       return TRUE;
     }
@@ -1232,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;
@@ -1468,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;
@@ -1478,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"))
@@ -2261,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;
@@ -2290,8 +2341,7 @@ _dbus_auth_server_new (const DBusString *guid,
   server_auth = DBUS_AUTH_SERVER (auth);
 
   server_auth->guid = guid_copy;
-  server_auth->authorization = _dbus_authorization_ref (authorization);
-
+  
   /* perhaps this should be per-mechanism with a lower
    * max
    */
@@ -2341,6 +2391,39 @@ _dbus_auth_client_new (void)
 }
 
 /**
+ * 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.
+ *
+ * @returns the new object or #NULL if no memory
+ */
+DBusAuth*
+_dbus_auth_client_new_authenticated (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;
+
+  return auth;
+}
+
+/**
  * Increments the refcount of an auth object.
  *
  * @param auth the auth conversation
@@ -2382,7 +2465,6 @@ _dbus_auth_unref (DBusAuth *auth)
           _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
 
           _dbus_string_free (& DBUS_AUTH_SERVER (auth)->guid);
-          _dbus_authorization_unref (DBUS_AUTH_SERVER (auth)->authorization);
         }
 
       if (auth->keyring)
@@ -2397,7 +2479,7 @@ _dbus_auth_unref (DBusAuth *auth)
       dbus_free_string_array (auth->allowed_mechs);
 
       _dbus_credentials_unref (auth->credentials);
-      _dbus_credentials_unref (auth->authenticated_identity);
+      _dbus_credentials_unref (auth->authorized_identity);
       _dbus_credentials_unref (auth->desired_identity);
       
       dbus_free (auth);
@@ -2553,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);
@@ -2754,7 +2834,7 @@ _dbus_auth_get_identity (DBusAuth               *auth)
 {
   if (auth->state == &common_state_authenticated)
     {
-      return auth->authenticated_identity;
+      return auth->authorized_identity;
     }
   else
     {
@@ -2762,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;
     }
 }
 
@@ -2825,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 */