Commit message: auth: Reject DBUS_COOKIE_SHA1 for users other than the server owner

The DBUS_COOKIE_SHA1 authentication mechanism aims to prove ownership
of a shared home directory by having the server write a secret "cookie"
into a .dbus-keyrings subdirectory of the desired identity's home
directory with 0700 permissions, and having the client prove that it can
read the cookie. This never actually worked for non-malicious clients in
the case where server uid != client uid (unless the server and client
both have privileges, such as Linux CAP_DAC_OVERRIDE or traditional
Unix uid 0) because an unprivileged server would fail to write out the
cookie, and an unprivileged client would be unable to read the resulting
file owned by the server.

Additionally, since dbus 1.7.10 we have checked that ~/.dbus-keyrings
is owned by the uid of the server (a side-effect of a check added to
harden our use of XDG_RUNTIME_DIR), further ruling out successful use
by a non-malicious client with a uid differing from the server's.

Joe Vennix of Apple Information Security discovered that the
implementation of DBUS_COOKIE_SHA1 was susceptible to a symbolic link
attack: a malicious client with write access to its own home directory
could manipulate a ~/.dbus-keyrings symlink to cause the DBusServer to
read and write in unintended locations. In the worst case this could
result in the DBusServer reusing a cookie that is known to the
malicious client, and treating that cookie as evidence that a subsequent
client connection came from an attacker-chosen uid, allowing
authentication bypass.

This is mitigated by the fact that by default, the well-known system
dbus-daemon (since 2003) and the well-known session dbus-daemon (in
stable releases since dbus 1.10.0 in 2015) only accept the EXTERNAL
authentication mechanism, and as a result will reject DBUS_COOKIE_SHA1
at an early stage, before manipulating cookies. As a result, this
vulnerability only applies to:

* system or session dbus-daemons with non-standard configuration
* third-party dbus-daemon invocations such as at-spi2-core (although
  in practice at-spi2-core also only accepts EXTERNAL by default)
* third-party uses of DBusServer such as the one in Upstart

Avoiding symlink attacks in a portable way is difficult, because APIs
like openat() and Linux /proc/self/fd are not universally available.
However, because DBUS_COOKIE_SHA1 already doesn't work in practice for
a non-matching uid, we can solve this vulnerability in an easier way
without regressions, by rejecting it early (before looking at
~/.dbus-keyrings) whenever the requested identity doesn't match the
identity of the process hosting the DBusServer.

Signed-off-by: Simon McVittie <smcv@collabora.com>
Closes: https://gitlab.freedesktop.org/dbus/dbus/issues/269
Closes: CVE-2019-12749
CVE: CVE-2019-12749
CWE: CWE-59
Vulnerability description: dbus before 1.10.28, 1.12.x before 1.12.16, and 1.13.x before 1.13.12, as used in DBusServer in Canonical Upstart in Ubuntu 14.04 (and in some, less common, uses of dbus-daemon), allows cookie spoofing because of symlink mishandling in the reference implementation of DBUS_COOKIE_SHA1 in the libdbus library. (This only affects the DBUS_COOKIE_SHA1 authentication mechanism.) A malicious client with write access to its own home directory could manipulate a ~/.dbus-keyrings symlink to cause a DBusServer with a different uid to read and write in unintended locations. In the worst case, this could result in the DBusServer reusing a cookie that is known to the malicious client, and treating that cookie as evidence that a subsequent client connection came from an attacker-chosen uid, allowing authentication bypass.

Project: dbus
File: dbus-auth.c

handle_server_state_waiting_for_begin (DBusAuth         *auth,
                                       DBusAuthCommand   command,
                                       const DBusString *args)
{
  switch (command)
    {
    case DBUS_AUTH_COMMAND_AUTH:
      return send_error (auth, "Sent AUTH while expecting BEGIN");

    case DBUS_AUTH_COMMAND_DATA:
      return send_error (auth, "Sent DATA while expecting BEGIN");

    case DBUS_AUTH_COMMAND_BEGIN:
      goto_state (auth, &common_state_authenticated);
      return TRUE;

    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
      if (auth->unix_fd_possible)
        return send_agree_unix_fd(auth);
      else
        return send_error(auth, "Unix FD passing not supported, not authenticated or otherwise not possible");

    case DBUS_AUTH_COMMAND_REJECTED:
    case DBUS_AUTH_COMMAND_OK:
    case DBUS_AUTH_COMMAND_UNKNOWN:
    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
    default:
      return send_error (auth, "Unknown command");

    case DBUS_AUTH_COMMAND_CANCEL:
    case DBUS_AUTH_COMMAND_ERROR:
      return send_rejected (auth);
    }
}

Project: dbus
File: dbus-auth.c

send_cancel (DBusAuth *auth)
{
  if (_dbus_string_append (&auth->outgoing, "CANCEL\r\n"))
    {
      goto_state (auth, &client_state_waiting_for_reject);
      return TRUE;
    }
  else
    return FALSE;
}

Project: dbus
File: dbus-auth.c

sha1_handle_second_client_response (DBusAuth         *auth,
                                    const DBusString *data)
{
  /* We are expecting a response which is the hex-encoded client
   * challenge, space, then SHA-1 hash of the concatenation of our
   * challenge, ":", client challenge, ":", secret key, all
   * hex-encoded.
   */
  int i;
  DBusString client_challenge;
  DBusString client_hash;
  dbus_bool_t retval;
  DBusString correct_hash;
  
  retval = FALSE;
  
  if (!_dbus_string_find_blank (data, 0, &i))
    {
      _dbus_verbose ("%s: no space separator in client response\n",
                     DBUS_AUTH_NAME (auth));
      return send_rejected (auth);
    }
  
  if (!_dbus_string_init (&client_challenge))
    goto out_0;

  if (!_dbus_string_init (&client_hash))
    goto out_1;  

  if (!_dbus_string_copy_len (data, 0, i, &client_challenge,
                              0))
    goto out_2;

  _dbus_string_skip_blank (data, i, &i);
  
  if (!_dbus_string_copy_len (data, i,
                              _dbus_string_get_length (data) - i,
                              &client_hash,
                              0))
    goto out_2;

  if (_dbus_string_get_length (&client_challenge) == 0 ||
      _dbus_string_get_length (&client_hash) == 0)
    {
      _dbus_verbose ("%s: zero-length client challenge or hash\n",
                     DBUS_AUTH_NAME (auth));
      if (send_rejected (auth))
        retval = TRUE;
      goto out_2;
    }

  if (!_dbus_string_init (&correct_hash))
    goto out_2;

  if (!sha1_compute_hash (auth, auth->cookie_id,
                          &auth->challenge, 
                          &client_challenge,
                          &correct_hash))
    goto out_3;

  /* if cookie_id was invalid, then we get an empty hash */
  if (_dbus_string_get_length (&correct_hash) == 0)
    {
      if (send_rejected (auth))
        retval = TRUE;
      goto out_3;
    }
  
  if (!_dbus_string_equal (&client_hash, &correct_hash))
    {
      if (send_rejected (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 using DBUS_COOKIE_SHA1\n",
                 DBUS_AUTH_NAME (auth));
  
  retval = TRUE;
  
 out_3:
  _dbus_string_zero (&correct_hash);
  _dbus_string_free (&correct_hash);
 out_2:
  _dbus_string_zero (&client_hash);
  _dbus_string_free (&client_hash);
 out_1:
  _dbus_string_free (&client_challenge);
 out_0:
  return retval;
}

Project: dbus
File: dbus-auth.c

handle_client_initial_response_external_mech (DBusAuth         *auth,
                                              DBusString       *response)
{
  /* We always append our UID as an initial response, so the server
   * doesn't have to send back an empty challenge to check whether we
   * want to specify an identity. i.e. this avoids a round trip that
   * the spec for the EXTERNAL mechanism otherwise requires.
   */
  DBusString plaintext;

  if (!_dbus_string_init (&plaintext))
    return FALSE;

  if (!_dbus_append_user_from_current_process (&plaintext))
    goto failed;

  if (!_dbus_string_hex_encode (&plaintext, 0,
				response,
				_dbus_string_get_length (response)))
    goto failed;

  _dbus_string_free (&plaintext);
  
  return TRUE;

 failed:
  _dbus_string_free (&plaintext);
  return FALSE;  
}

Project: dbus
File: dbus-auth.c

_dbus_auth_get_unix_fd_negotiated(DBusAuth *auth)
{
  return auth->unix_fd_negotiated;
}

Project: dbus
File: dbus-auth.c

handle_auth (DBusAuth *auth, const DBusString *args)
{
  if (_dbus_string_get_length (args) == 0)
    {
      /* No args to the auth, send mechanisms */
      if (!send_rejected (auth))
        return FALSE;

      return TRUE;
    }
  else
    {
      int i;
      DBusString mech;
      DBusString hex_response;
      
      _dbus_string_find_blank (args, 0, &i);

      if (!_dbus_string_init (&mech))
        return FALSE;

      if (!_dbus_string_init (&hex_response))
        {
          _dbus_string_free (&mech);
          return FALSE;
        }
      
      if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
        goto failed;

      _dbus_string_skip_blank (args, i, &i);
      if (!_dbus_string_copy (args, i, &hex_response, 0))
        goto failed;
     
      auth->mech = find_mech (&mech, auth->allowed_mechs);
      if (auth->mech != NULL)
        {
          _dbus_verbose ("%s: Trying mechanism %s\n",
                         DBUS_AUTH_NAME (auth),
                         auth->mech->mechanism);
          
          if (!process_data (auth, &hex_response,
                             auth->mech->server_data_func))
            goto failed;
        }
      else
        {
          /* Unsupported mechanism */
          _dbus_verbose ("%s: Unsupported mechanism %s\n",
                         DBUS_AUTH_NAME (auth),
                         _dbus_string_get_const_data (&mech));
          
          if (!send_rejected (auth))
            goto failed;
        }

      _dbus_string_free (&mech);      
      _dbus_string_free (&hex_response);

      return TRUE;
      
    failed:
      auth->mech = NULL;
      _dbus_string_free (&mech);
      _dbus_string_free (&hex_response);
      return FALSE;
    }
}

Project: dbus
File: dbus-auth.c

lookup_command_from_name (DBusString *command)
{
  int i;

  for (i = 0; i < _DBUS_N_ELEMENTS (auth_command_names); i++)
    {
      if (_dbus_string_equal_c_str (command,
                                    auth_command_names[i].name))
        return auth_command_names[i].command;
    }

  return DBUS_AUTH_COMMAND_UNKNOWN;
}

Project: dbus
File: dbus-auth.c

process_rejected (DBusAuth *auth, const DBusString *args)
{
  const DBusAuthMechanismHandler *mech;
  DBusAuthClient *client;

  client = DBUS_AUTH_CLIENT (auth);

  if (!auth->already_got_mechanisms)
    {
      if (!record_mechanisms (auth, args))
        return FALSE;
    }
  
  if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
    {
      mech = client->mechs_to_try->data;

      if (!send_auth (auth, mech))
        return FALSE;

      _dbus_list_pop_first (&client->mechs_to_try);

      _dbus_verbose ("%s: Trying mechanism %s\n",
                     DBUS_AUTH_NAME (auth),
                     mech->mechanism);
    }
  else
    {
      /* Give up */
      _dbus_verbose ("%s: Disconnecting because we are out of mechanisms to try using\n",
                     DBUS_AUTH_NAME (auth));
      goto_state (auth, &common_state_need_disconnect);
    }
  
  return TRUE;
}

Project: dbus
File: dbus-auth.c

handle_client_data_external_mech (DBusAuth         *auth,
                                  const DBusString *data)
{
  
  return TRUE;
}

Project: dbus
File: dbus-auth.c

record_mechanisms (DBusAuth         *auth,
                   const DBusString *args)
{
  int next;
  int len;

  if (auth->already_got_mechanisms)
    return TRUE;
  
  len = _dbus_string_get_length (args);
  
  next = 0;
  while (next < len)
    {
      DBusString m;
      const DBusAuthMechanismHandler *mech;
      
      if (!_dbus_string_init (&m))
        goto nomem;
      
      if (!get_word (args, &next, &m))
        {
          _dbus_string_free (&m);
          goto nomem;
        }

      mech = find_mech (&m, auth->allowed_mechs);

      if (mech != NULL)
        {
          /* FIXME right now we try mechanisms in the order
           * the server lists them; should we do them in
           * some more deterministic order?
           *
           * Probably in all_mechanisms order, our order of
           * preference. Of course when the server is us,
           * it lists things in that order anyhow.
           */

          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))
                {
                  _dbus_string_free (&m);
                  goto nomem;
                }
            }
          else
            {
              _dbus_verbose ("%s: Already tried mechanism %s; not adding to list we will try\n",
                             DBUS_AUTH_NAME (auth), mech->mechanism);
            }
        }
      else
        {
          _dbus_verbose ("%s: Server offered mechanism \"%s\" that we don't know how to use\n",
                         DBUS_AUTH_NAME (auth),
                         _dbus_string_get_const_data (&m));
        }

      _dbus_string_free (&m);
    }
  
  auth->already_got_mechanisms = TRUE;
  
  return TRUE;

 nomem:
  _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
  
  return FALSE;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_set_mechanisms (DBusAuth    *auth,
                           const char **mechanisms)
{
  char **copy;

  if (mechanisms != NULL)
    {
      copy = _dbus_dup_string_array (mechanisms);
      if (copy == NULL)
        return FALSE;
    }
  else
    copy = NULL;
  
  dbus_free_string_array (auth->allowed_mechs);

  auth->allowed_mechs = copy;

  return TRUE;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_get_buffer (DBusAuth     *auth,
                       DBusString **buffer)
{
  _dbus_assert (auth != NULL);
  _dbus_assert (!auth->buffer_outstanding);
  
  *buffer = &auth->incoming;

  auth->buffer_outstanding = TRUE;
}

Project: dbus
File: dbus-auth.c

send_rejected (DBusAuth *auth)
{
  DBusString command;
  DBusAuthServer *server_auth;
  int i;
  
  if (!_dbus_string_init (&command))
    return FALSE;
  
  if (!_dbus_string_append (&command,
                            "REJECTED"))
    goto nomem;

  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;
    }
  
  if (!_dbus_string_append (&command, "\r\n"))
    goto nomem;

  if (!_dbus_string_copy (&command, 0, &auth->outgoing,
                          _dbus_string_get_length (&auth->outgoing)))
    goto nomem;

  shutdown_mech (auth);
  
  _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
  server_auth = DBUS_AUTH_SERVER (auth);
  server_auth->failures += 1;

  if (server_auth->failures >= server_auth->max_failures)
    goto_state (auth, &common_state_need_disconnect);
  else
    goto_state (auth, &server_state_waiting_for_auth);

  _dbus_string_free (&command);
  
  return TRUE;

 nomem:
  _dbus_string_free (&command);
  return FALSE;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_get_bytes_to_send (DBusAuth          *auth,
                              const DBusString **str)
{
  _dbus_assert (auth != NULL);
  _dbus_assert (str != NULL);

  *str = NULL;
  
  if (_dbus_string_get_length (&auth->outgoing) == 0)
    return FALSE;

  *str = &auth->outgoing;

  return TRUE;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_return_buffer (DBusAuth               *auth,
                          DBusString             *buffer)
{
  _dbus_assert (buffer == &auth->incoming);
  _dbus_assert (auth->buffer_outstanding);

  auth->buffer_outstanding = FALSE;
}

Project: dbus
File: dbus-auth.c

send_data (DBusAuth *auth, DBusString *data)
{
  int old_len;

  if (data == NULL || _dbus_string_get_length (data) == 0)
    return _dbus_string_append (&auth->outgoing, "DATA\r\n");
  else
    {
      old_len = _dbus_string_get_length (&auth->outgoing);
      if (!_dbus_string_append (&auth->outgoing, "DATA "))
        goto out;

      if (!_dbus_string_hex_encode (data, 0, &auth->outgoing,
                                    _dbus_string_get_length (&auth->outgoing)))
        goto out;

      if (!_dbus_string_append (&auth->outgoing, "\r\n"))
        goto out;

      return TRUE;

    out:
      _dbus_string_set_length (&auth->outgoing, old_len);

      return FALSE;
    }
}

Project: dbus
File: dbus-auth.c

handle_server_state_waiting_for_data  (DBusAuth         *auth,
                                       DBusAuthCommand   command,
                                       const DBusString *args)
{
  switch (command)
    {
    case DBUS_AUTH_COMMAND_AUTH:
      return send_error (auth, "Sent AUTH while another AUTH in progress");

    case DBUS_AUTH_COMMAND_CANCEL:
    case DBUS_AUTH_COMMAND_ERROR:
      return send_rejected (auth);

    case DBUS_AUTH_COMMAND_DATA:
      return process_data (auth, args, auth->mech->server_data_func);

    case DBUS_AUTH_COMMAND_BEGIN:
      goto_state (auth, &common_state_need_disconnect);
      return TRUE;

    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
      return send_error (auth, "Need to authenticate first");

    case DBUS_AUTH_COMMAND_REJECTED:
    case DBUS_AUTH_COMMAND_OK:
    case DBUS_AUTH_COMMAND_UNKNOWN:
    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
    default:
      return send_error (auth, "Unknown command");
    }
}

Project: dbus
File: dbus-auth.c

handle_client_state_waiting_for_reject (DBusAuth         *auth,
                                        DBusAuthCommand   command,
                                        const DBusString *args)
{
  switch (command)
    {
    case DBUS_AUTH_COMMAND_REJECTED:
      return process_rejected (auth, args);
      
    case DBUS_AUTH_COMMAND_AUTH:
    case DBUS_AUTH_COMMAND_CANCEL:
    case DBUS_AUTH_COMMAND_DATA:
    case DBUS_AUTH_COMMAND_BEGIN:
    case DBUS_AUTH_COMMAND_OK:
    case DBUS_AUTH_COMMAND_ERROR:
    case DBUS_AUTH_COMMAND_UNKNOWN:
    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
    default:
      goto_state (auth, &common_state_need_disconnect);
      return TRUE;
    }
}

Project: dbus
File: dbus-auth.c

send_ok (DBusAuth *auth)
{
  int orig_len;

  orig_len = _dbus_string_get_length (&auth->outgoing);
  
  if (_dbus_string_append (&auth->outgoing, "OK ") &&
      _dbus_string_copy (& DBUS_AUTH_SERVER (auth)->guid,
                         0,
                         &auth->outgoing,
                         _dbus_string_get_length (&auth->outgoing)) &&
      _dbus_string_append (&auth->outgoing, "\r\n"))
    {
      goto_state (auth, &server_state_waiting_for_begin);
      return TRUE;
    }
  else
    {
      _dbus_string_set_length (&auth->outgoing, orig_len);
      return FALSE;
    }
}

Project: dbus
File: dbus-auth.c

_dbus_auth_new (int size)
{
  DBusAuth *auth;
  
  auth = dbus_malloc0 (size);
  if (auth == NULL)
    return NULL;
  
  auth->refcount = 1;
  
  auth->keyring = NULL;
  auth->cookie_id = -1;
  
  /* note that we don't use the max string length feature,
   * because you can't use that feature if you're going to
   * try to recover from out-of-memory (it creates
   * what looks like unrecoverable inability to alloc
   * more space in the string). But we do handle
   * overlong buffers in _dbus_auth_do_work().
   */
  
  if (!_dbus_string_init (&auth->incoming))
    goto enomem_0;

  if (!_dbus_string_init (&auth->outgoing))
    goto enomem_1;
    
  if (!_dbus_string_init (&auth->identity))
    goto enomem_2;

  if (!_dbus_string_init (&auth->context))
    goto enomem_3;

  if (!_dbus_string_init (&auth->challenge))
    goto enomem_4;

  /* default context if none is specified */
  if (!_dbus_string_append (&auth->context, "org_freedesktop_general"))
    goto enomem_5;

  auth->credentials = _dbus_credentials_new ();
  if (auth->credentials == NULL)
    goto enomem_6;
  
  auth->authorized_identity = _dbus_credentials_new ();
  if (auth->authorized_identity == NULL)
    goto enomem_7;

  auth->desired_identity = _dbus_credentials_new ();
  if (auth->desired_identity == NULL)
    goto enomem_8;
  
  return auth;

#if 0
 enomem_9:
  _dbus_credentials_unref (auth->desired_identity);
#endif
 enomem_8:
  _dbus_credentials_unref (auth->authorized_identity);
 enomem_7:
  _dbus_credentials_unref (auth->credentials);
 enomem_6:
 /* last alloc was an append to context, which is freed already below */ ;
 enomem_5:
  _dbus_string_free (&auth->challenge);
 enomem_4:
  _dbus_string_free (&auth->context);
 enomem_3:
  _dbus_string_free (&auth->identity);
 enomem_2:
  _dbus_string_free (&auth->outgoing);
 enomem_1:
  _dbus_string_free (&auth->incoming);
 enomem_0:
  dbus_free (auth);
  return NULL;
}

Project: dbus
File: dbus-auth.c

handle_client_shutdown_external_mech (DBusAuth *auth)
{

}

Project: dbus
File: dbus-auth.c

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");
    }
}

Project: dbus
File: dbus-auth.c

shutdown_mech (DBusAuth *auth)
{
  /* Cancel any 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);
  
  if (auth->mech != NULL)
    {
      _dbus_verbose ("%s: Shutting down mechanism %s\n",
                     DBUS_AUTH_NAME (auth), auth->mech->mechanism);
      
      if (DBUS_AUTH_IS_CLIENT (auth))
        (* auth->mech->client_shutdown_func) (auth);
      else
        (* auth->mech->server_shutdown_func) (auth);
      
      auth->mech = NULL;
    }
}

Project: dbus
File: dbus-auth.c

goto_state (DBusAuth *auth,
            const DBusAuthStateData *state)
{
  _dbus_verbose ("%s: going from state %s to state %s\n",
                 DBUS_AUTH_NAME (auth),
                 auth->state->name,
                 state->name);

  auth->state = state;
}

Project: dbus
File: dbus-auth.c

get_word (const DBusString *str,
          int              *start,
          DBusString       *word)
{
  int i;

  _dbus_string_skip_blank (str, *start, start);
  _dbus_string_find_blank (str, *start, &i);
  
  if (i > *start)
    {
      if (!_dbus_string_copy_len (str, *start, i - *start, word, 0))
        return FALSE;
      
      *start = i;
    }

  return TRUE;
}

Project: dbus
File: dbus-auth.c

handle_client_data_anonymous_mech (DBusAuth         *auth,
                                  const DBusString *data)
{
  
  return TRUE;
}

Project: dbus
File: dbus-auth.c

handle_server_data_cookie_sha1_mech (DBusAuth         *auth,
                                     const DBusString *data)
{
  if (auth->cookie_id < 0)
    return sha1_handle_first_client_response (auth, data);
  else
    return sha1_handle_second_client_response (auth, data);
}

Project: dbus
File: dbus-auth.c

_dbus_auth_unref (DBusAuth *auth)
{
  _dbus_assert (auth != NULL);
  _dbus_assert (auth->refcount > 0);

  auth->refcount -= 1;
  if (auth->refcount == 0)
    {
      shutdown_mech (auth);

      if (DBUS_AUTH_IS_CLIENT (auth))
        {
          _dbus_string_free (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
          _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
        }
      else
        {
          _dbus_assert (DBUS_AUTH_IS_SERVER (auth));

          _dbus_string_free (& DBUS_AUTH_SERVER (auth)->guid);
        }

      if (auth->keyring)
        _dbus_keyring_unref (auth->keyring);

      _dbus_string_free (&auth->context);
      _dbus_string_free (&auth->challenge);
      _dbus_string_free (&auth->identity);
      _dbus_string_free (&auth->incoming);
      _dbus_string_free (&auth->outgoing);

      dbus_free_string_array (auth->allowed_mechs);

      _dbus_credentials_unref (auth->credentials);
      _dbus_credentials_unref (auth->authorized_identity);
      _dbus_credentials_unref (auth->desired_identity);
      
      dbus_free (auth);
    }
}

Project: dbus
File: dbus-auth.c

handle_client_initial_response_cookie_sha1_mech (DBusAuth   *auth,
                                                 DBusString *response)
{
  DBusString username;
  dbus_bool_t retval;

  retval = FALSE;

  if (!_dbus_string_init (&username))
    return FALSE;
  
  if (!_dbus_append_user_from_current_process (&username))
    goto out_0;

  if (!_dbus_string_hex_encode (&username, 0,
				response,
				_dbus_string_get_length (response)))
    goto out_0;

  retval = TRUE;
  
 out_0:
  _dbus_string_free (&username);
  
  return retval;
}

Project: dbus
File: dbus-auth.c

handle_server_state_waiting_for_auth  (DBusAuth         *auth,
                                       DBusAuthCommand   command,
                                       const DBusString *args)
{
  switch (command)
    {
    case DBUS_AUTH_COMMAND_AUTH:
      return handle_auth (auth, args);

    case DBUS_AUTH_COMMAND_CANCEL:
    case DBUS_AUTH_COMMAND_DATA:
      return send_error (auth, "Not currently in an auth conversation");

    case DBUS_AUTH_COMMAND_BEGIN:
      goto_state (auth, &common_state_need_disconnect);
      return TRUE;

    case DBUS_AUTH_COMMAND_ERROR:
      return send_rejected (auth);

    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
      return send_error (auth, "Need to authenticate first");

    case DBUS_AUTH_COMMAND_REJECTED:
    case DBUS_AUTH_COMMAND_OK:
    case DBUS_AUTH_COMMAND_UNKNOWN:
    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
    default:
      return send_error (auth, "Unknown command");
    }
}

Project: dbus
File: dbus-auth.c

sha1_handle_first_client_response (DBusAuth         *auth,
                                   const DBusString *data)
{
  /* We haven't sent a challenge yet, we're expecting a desired
   * username from the client.
   */
  DBusString tmp;
  DBusString tmp2;
  dbus_bool_t retval = FALSE;
  DBusError error = DBUS_ERROR_INIT;
  DBusCredentials *myself = NULL;

  _dbus_string_set_length (&auth->challenge, 0);
  
  if (_dbus_string_get_length (data) > 0)
    {
      if (_dbus_string_get_length (&auth->identity) > 0)
        {
          /* Tried to send two auth identities, wtf */
          _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
                         DBUS_AUTH_NAME (auth));
          return send_rejected (auth);
        }
      else
        {
          /* this is our auth identity */
          if (!_dbus_string_copy (data, 0, &auth->identity, 0))
            return FALSE;
        }
    }
      
  if (!_dbus_credentials_add_from_user (auth->desired_identity, data))
    {
      _dbus_verbose ("%s: Did not get a valid username from client\n",
                     DBUS_AUTH_NAME (auth));
      return send_rejected (auth);
    }
      
  if (!_dbus_string_init (&tmp))
    return FALSE;

  if (!_dbus_string_init (&tmp2))
    {
      _dbus_string_free (&tmp);
      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 &&
      !_dbus_keyring_is_for_credentials (auth->keyring,
                                         auth->desired_identity))
    {
      _dbus_keyring_unref (auth->keyring);
      auth->keyring = NULL;
    }
  
  if (auth->keyring == NULL)
    {
      auth->keyring = _dbus_keyring_new_for_credentials (auth->desired_identity,
                                                         &auth->context,
                                                         &error);

      if (auth->keyring == NULL)
        {
          if (dbus_error_has_name (&error,
                                   DBUS_ERROR_NO_MEMORY))
            {
              dbus_error_free (&error);
              goto out;
            }
          else
            {
              _DBUS_ASSERT_ERROR_IS_SET (&error);
              _dbus_verbose ("%s: Error loading keyring: %s\n",
                             DBUS_AUTH_NAME (auth), error.message);
              if (send_rejected (auth))
                retval = TRUE; /* retval is only about mem */
              dbus_error_free (&error);
              goto out;
            }
        }
      else
        {
          _dbus_assert (!dbus_error_is_set (&error));
        }
    }

  _dbus_assert (auth->keyring != NULL);

  auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error);
  if (auth->cookie_id < 0)
    {
      _DBUS_ASSERT_ERROR_IS_SET (&error);
      _dbus_verbose ("%s: Could not get a cookie ID to send to client: %s\n",
                     DBUS_AUTH_NAME (auth), error.message);
      if (send_rejected (auth))
        retval = TRUE;
      dbus_error_free (&error);
      goto out;
    }
  else
    {
      _dbus_assert (!dbus_error_is_set (&error));
    }

  if (!_dbus_string_copy (&auth->context, 0,
                          &tmp2, _dbus_string_get_length (&tmp2)))
    goto out;

  if (!_dbus_string_append (&tmp2, " "))
    goto out;

  if (!_dbus_string_append_int (&tmp2, auth->cookie_id))
    goto out;

  if (!_dbus_string_append (&tmp2, " "))
    goto out;  
  
  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES, &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))
    goto out;
  
  if (!_dbus_string_hex_encode (&tmp, 0, &tmp2,
                                _dbus_string_get_length (&tmp2)))
    goto out;

  if (!send_data (auth, &tmp2))
    goto out;
      
  goto_state (auth, &server_state_waiting_for_data);
  retval = TRUE;
  
 out:
  _dbus_string_zero (&tmp);
  _dbus_string_free (&tmp);
  _dbus_string_zero (&tmp2);
  _dbus_string_free (&tmp2);

  if (myself != NULL)
    _dbus_credentials_unref (myself);

  return retval;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_needs_encoding (DBusAuth *auth)
{
  if (auth->state != &common_state_authenticated)
    return FALSE;
  
  if (auth->mech != NULL)
    {
      if (DBUS_AUTH_IS_CLIENT (auth))
        return auth->mech->client_encode_func != NULL;
      else
        return auth->mech->server_encode_func != NULL;
    }
  else
    return FALSE;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_needs_decoding (DBusAuth *auth)
{
  if (auth->state != &common_state_authenticated)
    return FALSE;
    
  if (auth->mech != NULL)
    {
      if (DBUS_AUTH_IS_CLIENT (auth))
        return auth->mech->client_decode_func != NULL;
      else
        return auth->mech->server_decode_func != NULL;
    }
  else
    return FALSE;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_set_credentials (DBusAuth               *auth,
                            DBusCredentials        *credentials)
{
  _dbus_credentials_clear (auth->credentials);
  return _dbus_credentials_add_credentials (auth->credentials,
                                            credentials);
}

Project: dbus
File: dbus-auth.c

_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;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_set_context (DBusAuth               *auth,
                        const DBusString       *context)
{
  return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context),
                                   &auth->context, 0, _dbus_string_get_length (context));
}

Project: dbus
File: dbus-auth.c

handle_server_shutdown_external_mech (DBusAuth *auth)
{

}

Project: dbus
File: dbus-auth.c

_dbus_auth_encode_data (DBusAuth         *auth,
                        const DBusString *plaintext,
                        DBusString       *encoded)
{
  _dbus_assert (plaintext != encoded);
  
  if (auth->state != &common_state_authenticated)
    return FALSE;
  
  if (_dbus_auth_needs_encoding (auth))
    {
      if (DBUS_AUTH_IS_CLIENT (auth))
        return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
      else
        return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
    }
  else
    {
      return _dbus_string_copy (plaintext, 0, encoded,
                                _dbus_string_get_length (encoded));
    }
}

Project: dbus
File: dbus-auth.c

handle_server_data_external_mech (DBusAuth         *auth,
                                  const DBusString *data)
{
  if (_dbus_credentials_are_anonymous (auth->credentials))
    {
      _dbus_verbose ("%s: no credentials, mechanism EXTERNAL can't authenticate\n",
                     DBUS_AUTH_NAME (auth));
      return send_rejected (auth);
    }
  
  if (_dbus_string_get_length (data) > 0)
    {
      if (_dbus_string_get_length (&auth->identity) > 0)
        {
          /* Tried to send two auth identities, wtf */
          _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
                         DBUS_AUTH_NAME (auth));
          return send_rejected (auth);
        }
      else
        {
          /* this is our auth identity */
          if (!_dbus_string_copy (data, 0, &auth->identity, 0))
            return FALSE;
        }
    }

  /* Poke client for an auth identity, if none given */
  if (_dbus_string_get_length (&auth->identity) == 0 &&
      !auth->already_asked_for_initial_response)
    {
      if (send_data (auth, NULL))
        {
          _dbus_verbose ("%s: sending empty challenge asking client for auth identity\n",
                         DBUS_AUTH_NAME (auth));
          auth->already_asked_for_initial_response = TRUE;
          goto_state (auth, &server_state_waiting_for_data);
          return TRUE;
        }
      else
        return FALSE;
    }

  _dbus_credentials_clear (auth->desired_identity);
  
  /* If auth->identity is still empty here, then client
   * responded with an empty string after we poked it for
   * an initial response. This means to try to auth the
   * identity provided in the credentials.
   */
  if (_dbus_string_get_length (&auth->identity) == 0)
    {
      if (!_dbus_credentials_add_credentials (auth->desired_identity,
                                              auth->credentials))
        {
          return FALSE; /* OOM */
        }
    }
  else
    {
      if (!_dbus_credentials_add_from_user (auth->desired_identity,
                                            &auth->identity))
        {
          _dbus_verbose ("%s: could not get credentials from uid string\n",
                         DBUS_AUTH_NAME (auth));
          return send_rejected (auth);
        }
    }

  if (_dbus_credentials_are_anonymous (auth->desired_identity))
    {
      _dbus_verbose ("%s: desired user %s is no good\n",
                     DBUS_AUTH_NAME (auth),
                     _dbus_string_get_const_data (&auth->identity));
      return send_rejected (auth);
    }
  
  if (_dbus_credentials_are_superset (auth->credentials,
                                      auth->desired_identity))
    {
      /* client has authenticated */
      if (!_dbus_credentials_add_credentials (auth->authorized_identity,
                                              auth->desired_identity))
        return FALSE;

      /* also copy misc process info from the socket credentials
       */
      if (!_dbus_credentials_add_credential (auth->authorized_identity,
                                             DBUS_CREDENTIAL_UNIX_PROCESS_ID,
                                             auth->credentials))
        return FALSE;

      if (!_dbus_credentials_add_credential (auth->authorized_identity,
                                             DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
                                             auth->credentials))
        return FALSE;

      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 client based on socket credentials\n",
                     DBUS_AUTH_NAME (auth));

      return TRUE;
    }
  else
    {
      _dbus_verbose ("%s: desired identity not found in socket credentials\n",
                     DBUS_AUTH_NAME (auth));
      return send_rejected (auth);
    }
}

Project: dbus
File: dbus-auth.c

handle_client_shutdown_anonymous_mech (DBusAuth *auth)
{
  
}

Project: dbus
File: dbus-auth.c

_dbus_auth_do_work (DBusAuth *auth)
{
  auth->needed_memory = FALSE;

  /* Max amount we'll buffer up before deciding someone's on crack */
#define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE)

  do
    {
      if (DBUS_AUTH_IN_END_STATE (auth))
        break;
      
      if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
          _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
        {
          goto_state (auth, &common_state_need_disconnect);
          _dbus_verbose ("%s: Disconnecting due to excessive data buffered in auth phase\n",
                         DBUS_AUTH_NAME (auth));
          break;
        }
    }
  while (process_command (auth));

  if (auth->needed_memory)
    return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
  else if (_dbus_string_get_length (&auth->outgoing) > 0)
    return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
  else if (auth->state == &common_state_need_disconnect)
    return DBUS_AUTH_STATE_NEED_DISCONNECT;
  else if (auth->state == &common_state_authenticated)
    return DBUS_AUTH_STATE_AUTHENTICATED;
  else return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
}

Project: dbus
File: dbus-auth.c

handle_client_state_waiting_for_data (DBusAuth         *auth,
                                      DBusAuthCommand   command,
                                      const DBusString *args)
{
  _dbus_assert (auth->mech != NULL);
 
  switch (command)
    {
    case DBUS_AUTH_COMMAND_DATA:
      return process_data (auth, args, auth->mech->client_data_func);

    case DBUS_AUTH_COMMAND_REJECTED:
      return process_rejected (auth, args);

    case DBUS_AUTH_COMMAND_OK:
      return process_ok(auth, args);

    case DBUS_AUTH_COMMAND_ERROR:
      return send_cancel (auth);

    case DBUS_AUTH_COMMAND_AUTH:
    case DBUS_AUTH_COMMAND_CANCEL:
    case DBUS_AUTH_COMMAND_BEGIN:
    case DBUS_AUTH_COMMAND_UNKNOWN:
    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
    default:
      return send_error (auth, "Unknown command");
    }
}

Project: dbus
File: dbus-auth.c

_dbus_auth_ref (DBusAuth *auth)
{
  _dbus_assert (auth != NULL);
  
  auth->refcount += 1;
  
  return auth;
}

Project: dbus
File: dbus-auth.c

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;  
}

Project: dbus
File: dbus-auth.c

process_command (DBusAuth *auth)
{
  DBusAuthCommand command;
  DBusString line;
  DBusString args;
  int eol;
  int i, j;
  dbus_bool_t retval;

  /* _dbus_verbose ("%s:   trying process_command()\n"); */
  
  retval = FALSE;
  
  eol = 0;
  if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
    return FALSE;
  
  if (!_dbus_string_init (&line))
    {
      auth->needed_memory = TRUE;
      return FALSE;
    }

  if (!_dbus_string_init (&args))
    {
      _dbus_string_free (&line);
      auth->needed_memory = TRUE;
      return FALSE;
    }
  
  if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &line, 0))
    goto out;

  if (!_dbus_string_validate_ascii (&line, 0,
                                    _dbus_string_get_length (&line)))
    {
      _dbus_verbose ("%s: Command contained non-ASCII chars or embedded nul\n",
                     DBUS_AUTH_NAME (auth));
      if (!send_error (auth, "Command contained non-ASCII"))
        goto out;
      else
        goto next_command;
    }
  
  _dbus_verbose ("%s: got command \"%s\"\n",
                 DBUS_AUTH_NAME (auth),
                 _dbus_string_get_const_data (&line));
  
  _dbus_string_find_blank (&line, 0, &i);
  _dbus_string_skip_blank (&line, i, &j);

  if (j > i)
    _dbus_string_delete (&line, i, j - i);
  
  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))
    goto out;

 next_command:
  
  /* We've succeeded in processing the whole command so drop it out
   * of the incoming buffer and return TRUE to try another command.
   */

  _dbus_string_delete (&auth->incoming, 0, eol);
  
  /* kill the \r\n */
  _dbus_string_delete (&auth->incoming, 0, 2);

  retval = TRUE;
  
 out:
  _dbus_string_free (&args);
  _dbus_string_free (&line);

  if (!retval)
    auth->needed_memory = TRUE;
  else
    auth->needed_memory = FALSE;
  
  return retval;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_get_identity (DBusAuth               *auth)
{
  if (auth->state == &common_state_authenticated)
    {
      return auth->authorized_identity;
    }
  else
    {
      /* 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;
    }
}

Project: dbus
File: dbus-auth.c

send_auth (DBusAuth *auth, const DBusAuthMechanismHandler *mech)
{
  DBusString auth_command;

  if (!_dbus_string_init (&auth_command))
    return FALSE;
      
  if (!_dbus_string_append (&auth_command,
                            "AUTH "))
    {
      _dbus_string_free (&auth_command);
      return FALSE;
    }  
  
  if (!_dbus_string_append (&auth_command,
                            mech->mechanism))
    {
      _dbus_string_free (&auth_command);
      return FALSE;
    }

  if (mech->client_initial_response_func != NULL)
    {
      if (!_dbus_string_append (&auth_command, " "))
        {
          _dbus_string_free (&auth_command);
          return FALSE;
        }
      
      if (!(* mech->client_initial_response_func) (auth, &auth_command))
        {
          _dbus_string_free (&auth_command);
          return FALSE;
        }
    }
  
  if (!_dbus_string_append (&auth_command,
                            "\r\n"))
    {
      _dbus_string_free (&auth_command);
      return FALSE;
    }

  if (!_dbus_string_copy (&auth_command, 0,
                          &auth->outgoing,
                          _dbus_string_get_length (&auth->outgoing)))
    {
      _dbus_string_free (&auth_command);
      return FALSE;
    }

  _dbus_string_free (&auth_command);
  shutdown_mech (auth);
  auth->mech = mech;      
  goto_state (auth, &client_state_waiting_for_data);

  return TRUE;
}

Project: dbus
File: dbus-auth.c

process_data (DBusAuth             *auth,
              const DBusString     *args,
              DBusAuthDataFunction  data_func)
{
  int end;
  DBusString decoded;

  if (!_dbus_string_init (&decoded))
    return FALSE;

  if (!_dbus_string_hex_decode (args, 0, &end, &decoded, 0))
    {
      _dbus_string_free (&decoded);
      return FALSE;
    }

  if (_dbus_string_get_length (args) != end)
    {
      _dbus_string_free (&decoded);
      if (!send_error (auth, "Invalid hex encoding"))
        return FALSE;

      return TRUE;
    }

#ifdef DBUS_ENABLE_VERBOSE_MODE
  if (_dbus_string_validate_ascii (&decoded, 0,
                                   _dbus_string_get_length (&decoded)))
    _dbus_verbose ("%s: data: '%s'\n",
                   DBUS_AUTH_NAME (auth),
                   _dbus_string_get_const_data (&decoded));
#endif
      
  if (!(* data_func) (auth, &decoded))
    {
      _dbus_string_free (&decoded);
      return FALSE;
    }

  _dbus_string_free (&decoded);
  return TRUE;
}

Project: dbus
File: dbus-auth.c

find_mech (const DBusString  *name,
           char             **allowed_mechs)
{
  int i;
  
  if (allowed_mechs != NULL &&
      !_dbus_string_array_contains ((const char**) allowed_mechs,
                                    _dbus_string_get_const_data (name)))
    return NULL;
  
  i = 0;
  while (all_mechanisms[i].mechanism != NULL)
    {      
      if (_dbus_string_equal_c_str (name,
                                    all_mechanisms[i].mechanism))

        return &all_mechanisms[i];
      
      ++i;
    }
  
  return NULL;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_set_unix_fd_possible(DBusAuth *auth, dbus_bool_t b)
{
  auth->unix_fd_possible = b;
}

Project: dbus
File: dbus-auth.c

handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth)
{
  auth->cookie_id = -1;  
  _dbus_string_set_length (&auth->challenge, 0);
}

Project: dbus
File: dbus-auth.c

_dbus_auth_is_supported_mechanism (DBusString *name)
{
  _dbus_assert (name != NULL);

  return find_mech (name, NULL) != NULL;
}

Project: dbus
File: dbus-auth.c

_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;
}

Project: dbus
File: dbus-auth.c

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;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_bytes_sent (DBusAuth *auth,
                       int       bytes_sent)
{
  _dbus_verbose ("%s: Sent %d bytes of: %s\n",
                 DBUS_AUTH_NAME (auth),
                 bytes_sent,
                 _dbus_string_get_const_data (&auth->outgoing));
  
  _dbus_string_delete (&auth->outgoing,
                       0, bytes_sent);
}

Project: dbus
File: dbus-auth.c

_dbus_auth_decode_data (DBusAuth         *auth,
                        const DBusString *encoded,
                        DBusString       *plaintext)
{
  _dbus_assert (plaintext != encoded);
  
  if (auth->state != &common_state_authenticated)
    return FALSE;
  
  if (_dbus_auth_needs_decoding (auth))
    {
      if (DBUS_AUTH_IS_CLIENT (auth))
        return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
      else
        return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
    }
  else
    {
      return _dbus_string_copy (encoded, 0, plaintext,
                                _dbus_string_get_length (plaintext));
    }
}

Project: dbus
File: dbus-auth.c

handle_client_state_waiting_for_ok (DBusAuth         *auth,
                                    DBusAuthCommand   command,
                                    const DBusString *args)
{
  switch (command)
    {
    case DBUS_AUTH_COMMAND_REJECTED:
      return process_rejected (auth, args);

    case DBUS_AUTH_COMMAND_OK:
      return process_ok(auth, args);

    case DBUS_AUTH_COMMAND_DATA:
    case DBUS_AUTH_COMMAND_ERROR:
      return send_cancel (auth);

    case DBUS_AUTH_COMMAND_AUTH:
    case DBUS_AUTH_COMMAND_CANCEL:
    case DBUS_AUTH_COMMAND_BEGIN:
    case DBUS_AUTH_COMMAND_UNKNOWN:
    case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD:
    case DBUS_AUTH_COMMAND_AGREE_UNIX_FD:
    default:
      return send_error (auth, "Unknown command");
    }
}

Project: dbus
File: dbus-auth.c

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;
}

Project: dbus
File: dbus-auth.c

send_begin (DBusAuth         *auth)
{

  if (!_dbus_string_append (&auth->outgoing,
                            "BEGIN\r\n"))
    return FALSE;

  goto_state (auth, &common_state_authenticated);
  return TRUE;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_server_new (const DBusString *guid)
{
  DBusAuth *auth;
  DBusAuthServer *server_auth;
  DBusString guid_copy;

  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
   */
  server_auth->failures = 0;
  server_auth->max_failures = 6;
  
  return auth;
}

Project: dbus
File: dbus-auth.c

handle_server_shutdown_anonymous_mech (DBusAuth *auth)
{
  
}

Project: dbus
File: dbus-auth.c

process_ok(DBusAuth *auth,
          const DBusString *args_from_ok) {

  int end_of_hex;
  
  /* "args_from_ok" should be the GUID, whitespace already pulled off the front */
  _dbus_assert (_dbus_string_get_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server) == 0);

  /* We decode the hex string to binary, using guid_from_server as scratch... */
  
  end_of_hex = 0;
  if (!_dbus_string_hex_decode (args_from_ok, 0, &end_of_hex,
                                & DBUS_AUTH_CLIENT (auth)->guid_from_server, 0))
    return FALSE;

  /* now clear out the scratch */
  _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
  
  if (end_of_hex != _dbus_string_get_length (args_from_ok) ||
      end_of_hex == 0)
    {
      _dbus_verbose ("Bad GUID from server, parsed %d bytes and had %d bytes from server\n",
                     end_of_hex, _dbus_string_get_length (args_from_ok));
      goto_state (auth, &common_state_need_disconnect);
      return TRUE;
    }

  if (!_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0)) {
      _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
      return FALSE;
  }

  _dbus_verbose ("Got GUID '%s' from the server\n",
                 _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server));

  if (auth->unix_fd_possible)
    return send_negotiate_unix_fd(auth);

  _dbus_verbose("Not negotiating unix fd passing, since not possible\n");
  return send_begin (auth);
}

Project: dbus
File: dbus-auth.c

sha1_compute_hash (DBusAuth         *auth,
                   int               cookie_id,
                   const DBusString *server_challenge,
                   const DBusString *client_challenge,
                   DBusString       *hash)
{
  DBusString cookie;
  DBusString to_hash;
  dbus_bool_t retval;
  
  _dbus_assert (auth->keyring != NULL);

  retval = FALSE;
  
  if (!_dbus_string_init (&cookie))
    return FALSE;

  if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id,
                                  &cookie))
    goto out_0;

  if (_dbus_string_get_length (&cookie) == 0)
    {
      retval = TRUE;
      goto out_0;
    }

  if (!_dbus_string_init (&to_hash))
    goto out_0;
  
  if (!_dbus_string_copy (server_challenge, 0,
                          &to_hash, _dbus_string_get_length (&to_hash)))
    goto out_1;

  if (!_dbus_string_append (&to_hash, ":"))
    goto out_1;
  
  if (!_dbus_string_copy (client_challenge, 0,
                          &to_hash, _dbus_string_get_length (&to_hash)))
    goto out_1;

  if (!_dbus_string_append (&to_hash, ":"))
    goto out_1;

  if (!_dbus_string_copy (&cookie, 0,
                          &to_hash, _dbus_string_get_length (&to_hash)))
    goto out_1;

  if (!_dbus_sha_compute (&to_hash, hash))
    goto out_1;
  
  retval = TRUE;

 out_1:
  _dbus_string_zero (&to_hash);
  _dbus_string_free (&to_hash);
 out_0:
  _dbus_string_zero (&cookie);
  _dbus_string_free (&cookie);
  return retval;
}

Project: dbus
File: dbus-auth.c

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;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_get_unused_bytes (DBusAuth           *auth,
                             const DBusString **str)
{
  if (!DBUS_AUTH_IN_END_STATE (auth))
    return;

  *str = &auth->incoming;
}

Project: dbus
File: dbus-auth.c

handle_server_shutdown_cookie_sha1_mech (DBusAuth *auth)
{
  auth->cookie_id = -1;  
  _dbus_string_set_length (&auth->challenge, 0);
}

Project: dbus
File: dbus-auth.c

_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)
    {
      _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;

  /* 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;
}

Project: dbus
File: dbus-auth.c

handle_client_data_cookie_sha1_mech (DBusAuth         *auth,
                                     const DBusString *data)
{
  /* The data we get from the server should be the cookie context
   * name, the cookie ID, and the server challenge, separated by
   * spaces. We send back our challenge string and the correct hash.
   */
  dbus_bool_t retval = FALSE;
  DBusString context;
  DBusString cookie_id_str;
  DBusString server_challenge;
  DBusString client_challenge;
  DBusString correct_hash;
  DBusString tmp;
  int i, j;
  long val;
  DBusError error = DBUS_ERROR_INIT;

  if (!_dbus_string_find_blank (data, 0, &i))
    {
      if (send_error (auth,
                      "Server did not send context/ID/challenge properly"))
        retval = TRUE;
      goto out_0;
    }

  if (!_dbus_string_init (&context))
    goto out_0;

  if (!_dbus_string_copy_len (data, 0, i,
                              &context, 0))
    goto out_1;
  
  _dbus_string_skip_blank (data, i, &i);
  if (!_dbus_string_find_blank (data, i, &j))
    {
      if (send_error (auth,
                      "Server did not send context/ID/challenge properly"))
        retval = TRUE;
      goto out_1;
    }

  if (!_dbus_string_init (&cookie_id_str))
    goto out_1;
  
  if (!_dbus_string_copy_len (data, i, j - i,
                              &cookie_id_str, 0))
    goto out_2;  

  if (!_dbus_string_init (&server_challenge))
    goto out_2;

  i = j;
  _dbus_string_skip_blank (data, i, &i);
  j = _dbus_string_get_length (data);

  if (!_dbus_string_copy_len (data, i, j - i,
                              &server_challenge, 0))
    goto out_3;

  if (!_dbus_keyring_validate_context (&context))
    {
      if (send_error (auth, "Server sent invalid cookie context"))
        retval = TRUE;
      goto out_3;
    }

  if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL))
    {
      if (send_error (auth, "Could not parse cookie ID as an integer"))
        retval = TRUE;
      goto out_3;
    }

  if (_dbus_string_get_length (&server_challenge) == 0)
    {
      if (send_error (auth, "Empty server challenge string"))
        retval = TRUE;
      goto out_3;
    }

  if (auth->keyring == NULL)
    {
      auth->keyring = _dbus_keyring_new_for_credentials (NULL,
                                                         &context,
                                                         &error);

      if (auth->keyring == NULL)
        {
          if (dbus_error_has_name (&error,
                                   DBUS_ERROR_NO_MEMORY))
            {
              dbus_error_free (&error);
              goto out_3;
            }
          else
            {
              _DBUS_ASSERT_ERROR_IS_SET (&error);

              _dbus_verbose ("%s: Error loading keyring: %s\n",
                             DBUS_AUTH_NAME (auth), error.message);
              
              if (send_error (auth, "Could not load cookie file"))
                retval = TRUE; /* retval is only about mem */
              
              dbus_error_free (&error);
              goto out_3;
            }
        }
      else
        {
          _dbus_assert (!dbus_error_is_set (&error));
        }
    }
  
  _dbus_assert (auth->keyring != NULL);
  
  if (!_dbus_string_init (&tmp))
    goto out_3;

  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES, &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;

  if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0))
    goto out_5;

  if (!_dbus_string_init (&correct_hash))
    goto out_5;
  
  if (!sha1_compute_hash (auth, val,
                          &server_challenge,
                          &client_challenge,
                          &correct_hash))
    goto out_6;

  if (_dbus_string_get_length (&correct_hash) == 0)
    {
      /* couldn't find the cookie ID or something */
      if (send_error (auth, "Don't have the requested cookie ID"))
        retval = TRUE;
      goto out_6;
    }
  
  _dbus_string_set_length (&tmp, 0);
  
  if (!_dbus_string_copy (&client_challenge, 0, &tmp,
                          _dbus_string_get_length (&tmp)))
    goto out_6;

  if (!_dbus_string_append (&tmp, " "))
    goto out_6;

  if (!_dbus_string_copy (&correct_hash, 0, &tmp,
                          _dbus_string_get_length (&tmp)))
    goto out_6;

  if (!send_data (auth, &tmp))
    goto out_6;

  retval = TRUE;

 out_6:
  _dbus_string_zero (&correct_hash);
  _dbus_string_free (&correct_hash);
 out_5:
  _dbus_string_free (&client_challenge);
 out_4:
  _dbus_string_zero (&tmp);
  _dbus_string_free (&tmp);
 out_3:
  _dbus_string_free (&server_challenge);
 out_2:
  _dbus_string_free (&cookie_id_str);
 out_1:
  _dbus_string_free (&context);
 out_0:
  return retval;
}

Project: dbus
File: dbus-auth.c

_dbus_auth_delete_unused_bytes (DBusAuth *auth)
{
  if (!DBUS_AUTH_IN_END_STATE (auth))
    return;

  _dbus_string_set_length (&auth->incoming, 0);
}

Project: dbus
File: dbus-auth.c

send_error (DBusAuth *auth, const char *message)
{
  return _dbus_string_append_printf (&auth->outgoing,
                                     "ERROR \"%s\"\r\n", message);
}