#include "dbus-sha.h"
#include "dbus-protocol.h"
#include "dbus-credentials.h"
-#include "dbus-authorization.h"
/**
* @defgroup DBusAuth Authentication
* @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
*/
struct DBusAuth
{
+ int refcount; /**< reference count */
const char *side; /**< Client or server */
DBusString incoming; /**< Incoming data buffer */
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 */
{
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 */
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
};
if (auth == NULL)
return NULL;
+ auth->refcount = 1;
+
auth->keyring = NULL;
auth->cookie_id = -1;
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 ();
_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:
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)
*/
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);
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.
if (auth->keyring == NULL)
{
- dbus_error_init (&error);
auth->keyring = _dbus_keyring_new_for_credentials (auth->desired_identity,
&auth->context,
&error);
_dbus_assert (auth->keyring != NULL);
- dbus_error_init (&error);
auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error);
if (auth->cookie_id < 0)
{
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))
_dbus_string_zero (&tmp2);
_dbus_string_free (&tmp2);
+ if (myself != NULL)
+ _dbus_credentials_unref (myself);
+
return retval;
}
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;
* 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;
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,
if (auth->keyring == NULL)
{
- DBusError error;
-
- dbus_error_init (&error);
auth->keyring = _dbus_keyring_new_for_credentials (NULL,
&context,
&error);
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;
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;
}
/* 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;
"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;
if (!_dbus_string_append (&command,
all_mechanisms[i].mechanism))
goto nomem;
-
- ++i;
}
if (!_dbus_string_append (&command, "\r\n"))
* @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;
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
*/
* mechanism */
if (!send_auth (auth, &all_mechanisms[0]))
{
- _dbus_auth_free (auth);
+ _dbus_auth_unref (auth);
return NULL;
}
}
/**
- * 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);
+ }
}
/**
*
* @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);
{
if (auth->state == &common_state_authenticated)
{
- return auth->authenticated_identity;
+ return auth->authorized_identity;
}
else
{
* 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;
}
}
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 */