dbus/dbus/dbus-auth.c
Havoc Pennington 2297787455 2002-12-25 Havoc Pennington <hp@pobox.com>
* doc/dbus-sasl-profile.txt: docs on the authentication protocol,
	it is a simple protocol that just maps directly to SASL.

	* dbus/dbus-auth.h, dbus/dbus-auth.c: authentication protocol
	initial implementation, not actually used yet.

	* dbus/dbus-string.c (_dbus_string_find): new function
	(_dbus_string_equal): new function
	(_dbus_string_base64_encode): new function
	(_dbus_string_base64_decode): new function
2002-12-25 18:00:10 +00:00

1215 lines
30 KiB
C

/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-auth.c Authentication
*
* Copyright (C) 2002 Red Hat Inc.
*
* Licensed under the Academic Free License version 1.2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "dbus-auth.h"
#include "dbus-string.h"
#include "dbus-list.h"
#include "dbus-internals.h"
/* See doc/dbus-sasl-profile.txt */
/**
* @defgroup DBusAuth Authentication
* @ingroup DBusInternals
* @brief DBusAuth object
*
* DBusAuth manages the authentication negotiation when a connection
* is first established, and also manage any encryption used over a
* connection.
*
* The file doc/dbus-sasl-profile.txt documents the network protocol
* used for authentication.
*/
/**
* @defgroup DBusAuthInternals Authentication implementation details
* @ingroup DBusInternals
* @brief DBusAuth implementation details
*
* Private details of authentication code.
*
* @{
*/
/**
* Processes a command. Returns whether we had enough memory to
* complete the operation.
*/
typedef dbus_bool_t (* DBusProcessAuthCommandFunction) (DBusAuth *auth,
const DBusString *command,
const DBusString *args);
typedef struct
{
const char *command;
DBusProcessAuthCommandFunction func;
} DBusAuthCommandHandler;
/**
* This function processes a block of data received from the peer.
* i.e. handles a DATA command.
*/
typedef dbus_bool_t (* DBusAuthDataFunction) (DBusAuth *auth,
const DBusString *data);
/**
* This function encodes a block of data from the peer.
*/
typedef dbus_bool_t (* DBusAuthEncodeFunction) (DBusAuth *auth,
const DBusString *data,
DBusString *encoded);
/**
* This function decodes a block of data from the peer.
*/
typedef dbus_bool_t (* DBusAuthDecodeFunction) (DBusAuth *auth,
const DBusString *data,
DBusString *decoded);
/**
* This function is called when the mechanism is abandoned.
*/
typedef void (* DBusAuthShutdownFunction) (DBusAuth *auth);
typedef struct
{
const char *mechanism;
DBusAuthDataFunction server_data_func;
DBusAuthEncodeFunction server_encode_func;
DBusAuthDecodeFunction server_decode_func;
DBusAuthShutdownFunction server_shutdown_func;
DBusAuthDataFunction client_data_func;
DBusAuthEncodeFunction client_encode_func;
DBusAuthDecodeFunction client_decode_func;
DBusAuthShutdownFunction client_shutdown_func;
} DBusAuthMechanismHandler;
/**
* Internal members of DBusAuth.
*/
struct DBusAuth
{
int refcount; /**< reference count */
DBusString incoming; /**< Incoming data buffer */
DBusString outgoing; /**< Outgoing data buffer */
const DBusAuthCommandHandler *handlers; /**< Handlers for commands */
const DBusAuthMechanismHandler *mech; /**< Current auth mechanism */
unsigned int needed_memory : 1; /**< We needed memory to continue since last
* successful getting something done
*/
unsigned int need_disconnect : 1; /**< We've given up, time to disconnect */
unsigned int authenticated : 1; /**< We are authenticated */
unsigned int authenticated_pending_output : 1; /**< Authenticated once we clear outgoing buffer */
unsigned int authenticated_pending_begin : 1; /**< Authenticated once we get BEGIN */
unsigned int already_got_mechanisms : 1; /**< Client already got mech list */
};
typedef struct
{
DBusAuth base;
DBusList *mechs_to_try;
} DBusAuthClient;
typedef struct
{
DBusAuth base;
} DBusAuthServer;
static dbus_bool_t process_auth (DBusAuth *auth,
const DBusString *command,
const DBusString *args);
static dbus_bool_t process_cancel (DBusAuth *auth,
const DBusString *command,
const DBusString *args);
static dbus_bool_t process_begin (DBusAuth *auth,
const DBusString *command,
const DBusString *args);
static dbus_bool_t process_data_server (DBusAuth *auth,
const DBusString *command,
const DBusString *args);
static dbus_bool_t process_error_server (DBusAuth *auth,
const DBusString *command,
const DBusString *args);
static dbus_bool_t process_mechanisms (DBusAuth *auth,
const DBusString *command,
const DBusString *args);
static dbus_bool_t process_rejected (DBusAuth *auth,
const DBusString *command,
const DBusString *args);
static dbus_bool_t process_ok (DBusAuth *auth,
const DBusString *command,
const DBusString *args);
static dbus_bool_t process_data_client (DBusAuth *auth,
const DBusString *command,
const DBusString *args);
static dbus_bool_t process_error_client (DBusAuth *auth,
const DBusString *command,
const DBusString *args);
static DBusAuthCommandHandler
server_handlers[] = {
{ "AUTH", process_auth },
{ "CANCEL", process_cancel },
{ "BEGIN", process_begin },
{ "DATA", process_data_server },
{ "ERROR", process_error_server },
{ NULL, NULL }
};
static DBusAuthCommandHandler
client_handlers[] = {
{ "MECHANISMS", process_mechanisms },
{ "REJECTED", process_rejected },
{ "OK", process_ok },
{ "DATA", process_data_client },
{ "ERROR", process_error_client },
{ NULL, NULL }
};
/**
* @param auth the auth conversation
* @returns #TRUE if the conversation is the server side
*/
#define DBUS_AUTH_IS_SERVER(auth) ((auth)->handlers == server_handlers)
/**
* @param auth the auth conversation
* @returns #TRUE if the conversation is the client side
*/
#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->handlers == client_handlers)
/**
* @param auth the auth conversation
* @returns auth cast to DBusAuthClient
*/
#define DBUS_AUTH_CLIENT(auth) ((DBusAuthClient*)(auth))
/**
* @param auth the auth conversation
* @returns auth cast to DBusAuthServer
*/
#define DBUS_AUTH_SERVER(auth) ((DBusAuthServer*)(auth))
static DBusAuth*
_dbus_auth_new (int size)
{
DBusAuth *auth;
auth = dbus_malloc0 (size);
if (auth == NULL)
return NULL;
auth->refcount = 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, _DBUS_INT_MAX))
{
dbus_free (auth);
return NULL;
}
if (!_dbus_string_init (&auth->outgoing, _DBUS_INT_MAX))
{
_dbus_string_free (&auth->outgoing);
dbus_free (auth);
return NULL;
}
return auth;
}
static DBusAuthState
get_state (DBusAuth *auth)
{
if (auth->need_disconnect)
return DBUS_AUTH_STATE_NEED_DISCONNECT;
else if (auth->authenticated)
return DBUS_AUTH_STATE_AUTHENTICATED;
else 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
return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
}
static void
shutdown_mech (DBusAuth *auth)
{
/* Cancel any auth */
auth->authenticated_pending_begin = FALSE;
auth->authenticated = FALSE;
if (auth->mech != NULL)
{
if (DBUS_AUTH_IS_CLIENT (auth))
(* auth->mech->client_shutdown_func) (auth);
else
(* auth->mech->server_shutdown_func) (auth);
auth->mech = NULL;
}
}
static dbus_bool_t
handle_server_data_stupid_test_mech (DBusAuth *auth,
const DBusString *data)
{
if (!_dbus_string_append (&auth->outgoing,
"OK\r\n"))
return FALSE;
auth->authenticated_pending_begin = TRUE;
return TRUE;
}
static void
handle_server_shutdown_stupid_test_mech (DBusAuth *auth)
{
}
static dbus_bool_t
handle_client_data_stupid_test_mech (DBusAuth *auth,
const DBusString *data)
{
return TRUE;
}
static void
handle_client_shutdown_stupid_test_mech (DBusAuth *auth)
{
}
/* the stupid test mech is a base64-encoded string;
* all the inefficiency, none of the security!
*/
static dbus_bool_t
handle_encode_stupid_test_mech (DBusAuth *auth,
const DBusString *plaintext,
DBusString *encoded)
{
if (!_dbus_string_base64_encode (plaintext, 0, encoded, 0))
return FALSE;
return TRUE;
}
static dbus_bool_t
handle_decode_stupid_test_mech (DBusAuth *auth,
const DBusString *encoded,
DBusString *plaintext)
{
if (!_dbus_string_base64_decode (encoded, 0, plaintext, 0))
return FALSE;
return TRUE;
}
/* Put mechanisms here in order of preference.
* What I eventually want to have is:
*
* - a mechanism that checks UNIX domain socket credentials
* - a simple magic cookie mechanism like X11 or ICE
* - mechanisms that chain to Cyrus SASL, so we can use anything it
* offers such as Kerberos, X509, whatever.
*
*/
static const DBusAuthMechanismHandler
all_mechanisms[] = {
{ "DBUS_STUPID_TEST_MECH",
handle_server_data_stupid_test_mech,
handle_encode_stupid_test_mech,
handle_decode_stupid_test_mech,
handle_server_shutdown_stupid_test_mech,
handle_client_data_stupid_test_mech,
handle_encode_stupid_test_mech,
handle_decode_stupid_test_mech,
handle_client_shutdown_stupid_test_mech },
{ NULL, NULL }
};
static const DBusAuthMechanismHandler*
find_mech (const DBusString *name)
{
int i;
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;
}
static dbus_bool_t
send_mechanisms (DBusAuth *auth)
{
DBusString command;
int i;
if (!_dbus_string_init (&command, _DBUS_INT_MAX))
return FALSE;
if (!_dbus_string_append (&command,
"MECHANISMS"))
goto nomem;
i = 0;
while (all_mechanisms[i].mechanism != NULL)
{
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"))
goto nomem;
if (!_dbus_string_copy (&command, 0, &auth->outgoing,
_dbus_string_get_length (&auth->outgoing)))
goto nomem;
return TRUE;
nomem:
_dbus_string_free (&command);
return FALSE;
}
static dbus_bool_t
process_auth (DBusAuth *auth,
const DBusString *command,
const DBusString *args)
{
if (auth->mech)
{
/* We are already using a mechanism, client is on crack */
if (!_dbus_string_append (&auth->outgoing,
"ERROR \"Sent AUTH while another AUTH in progress\"\r\n"))
return FALSE;
return TRUE;
}
else if (_dbus_string_get_length (args) == 0)
{
/* No args to the auth, send mechanisms */
if (!send_mechanisms (auth))
return FALSE;
return TRUE;
}
else
{
int i;
DBusString mech;
DBusString base64_response;
DBusString decoded_response;
_dbus_string_find_blank (args, 0, &i);
if (!_dbus_string_init (&mech, _DBUS_INT_MAX))
return FALSE;
if (!_dbus_string_init (&base64_response, _DBUS_INT_MAX))
{
_dbus_string_free (&mech);
return FALSE;
}
if (!_dbus_string_init (&decoded_response, _DBUS_INT_MAX))
{
_dbus_string_free (&mech);
_dbus_string_free (&base64_response);
return FALSE;
}
if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
goto failed;
if (!_dbus_string_copy (args, i, &base64_response, 0))
goto failed;
if (!_dbus_string_base64_decode (&base64_response, 0,
&decoded_response, 0))
goto failed;
auth->mech = find_mech (&mech);
if (auth->mech != NULL)
{
if (!(* auth->mech->server_data_func) (auth,
&decoded_response))
goto failed;
}
else
{
/* Unsupported mechanism */
if (!send_mechanisms (auth))
return FALSE;
}
return TRUE;
failed:
auth->mech = NULL;
_dbus_string_free (&mech);
_dbus_string_free (&base64_response);
_dbus_string_free (&decoded_response);
return FALSE;
}
}
static dbus_bool_t
process_cancel (DBusAuth *auth,
const DBusString *command,
const DBusString *args)
{
shutdown_mech (auth);
return TRUE;
}
static dbus_bool_t
process_begin (DBusAuth *auth,
const DBusString *command,
const DBusString *args)
{
if (auth->authenticated_pending_begin)
auth->authenticated = TRUE;
else
{
auth->need_disconnect = TRUE; /* client trying to send data before auth,
* kick it
*/
shutdown_mech (auth);
}
return TRUE;
}
static dbus_bool_t
process_data_server (DBusAuth *auth,
const DBusString *command,
const DBusString *args)
{
if (auth->mech != NULL)
{
DBusString decoded;
if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
return FALSE;
if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
{
_dbus_string_free (&decoded);
return FALSE;
}
if (!(* auth->mech->server_data_func) (auth, &decoded))
{
_dbus_string_free (&decoded);
return FALSE;
}
_dbus_string_free (&decoded);
}
else
{
if (!_dbus_string_append (&auth->outgoing,
"ERROR \"Not currently in an auth conversation\"\r\n"))
return FALSE;
}
return TRUE;
}
static dbus_bool_t
process_error_server (DBusAuth *auth,
const DBusString *command,
const DBusString *args)
{
return TRUE;
}
static dbus_bool_t
get_word (const DBusString *str,
int *start,
DBusString *word)
{
int i;
_dbus_string_find_blank (str, *start, &i);
if (i != *start)
{
if (!_dbus_string_copy_len (str, *start, i, word, 0))
return FALSE;
*start = i;
}
return TRUE;
}
static dbus_bool_t
process_mechanisms (DBusAuth *auth,
const DBusString *command,
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, _DBUS_INT_MAX))
goto nomem;
if (!get_word (args, &next, &m))
goto nomem;
mech = find_mech (&m);
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 (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
(void*) mech))
goto nomem;
}
}
auth->already_got_mechanisms = TRUE;
return TRUE;
nomem:
_dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
return FALSE;
}
static dbus_bool_t
process_rejected (DBusAuth *auth,
const DBusString *command,
const DBusString *args)
{
shutdown_mech (auth);
if (!auth->already_got_mechanisms)
{
/* Ask for mechanisms */
if (!_dbus_string_append (&auth->outgoing,
"AUTH\r\n"))
return FALSE;
}
else if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
{
/* Try next mechanism */
const DBusAuthMechanismHandler *mech;
DBusString auth_command;
mech = DBUS_AUTH_CLIENT (auth)->mechs_to_try->data;
if (!_dbus_string_init (&auth_command, _DBUS_INT_MAX))
return FALSE;
if (!_dbus_string_append (&auth_command,
"AUTH "))
{
_dbus_string_free (&auth_command);
return FALSE;
}
if (!_dbus_string_append (&auth->outgoing,
mech->mechanism))
{
_dbus_string_free (&auth_command);
return FALSE;
}
if (!_dbus_string_append (&auth->outgoing,
"\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_list_pop_first (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
}
else
{
/* Give up */
auth->need_disconnect = TRUE;
}
return TRUE;
}
static dbus_bool_t
process_ok (DBusAuth *auth,
const DBusString *command,
const DBusString *args)
{
if (!_dbus_string_append (&auth->outgoing,
"BEGIN\r\n"))
return FALSE;
auth->authenticated_pending_output = TRUE;
return TRUE;
}
static dbus_bool_t
process_data_client (DBusAuth *auth,
const DBusString *command,
const DBusString *args)
{
if (auth->mech != NULL)
{
DBusString decoded;
if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
return FALSE;
if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
{
_dbus_string_free (&decoded);
return FALSE;
}
if (!(* auth->mech->client_data_func) (auth, &decoded))
{
_dbus_string_free (&decoded);
return FALSE;
}
_dbus_string_free (&decoded);
}
else
{
if (!_dbus_string_append (&auth->outgoing,
"ERROR \"Got DATA when not in an auth exchange\"\r\n"))
return FALSE;
}
return TRUE;
}
static dbus_bool_t
process_error_client (DBusAuth *auth,
const DBusString *command,
const DBusString *args)
{
return TRUE;
}
static dbus_bool_t
process_unknown (DBusAuth *auth,
const DBusString *command,
const DBusString *args)
{
if (!_dbus_string_append (&auth->outgoing,
"ERROR \"Unknown command\"\r\n"))
return FALSE;
return TRUE;
}
/* returns whether to call it again right away */
static dbus_bool_t
process_command (DBusAuth *auth)
{
DBusString command;
DBusString args;
int eol;
int i, j;
dbus_bool_t retval;
retval = FALSE;
eol = 0;
if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
return FALSE;
if (!_dbus_string_init (&command, _DBUS_INT_MAX))
{
auth->needed_memory = TRUE;
return FALSE;
}
if (!_dbus_string_init (&args, _DBUS_INT_MAX))
{
auth->needed_memory = TRUE;
return FALSE;
}
if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0))
goto out;
_dbus_string_find_blank (&command, 0, &i);
_dbus_string_skip_blank (&command, i, &j);
if (i != j)
_dbus_string_delete (&command, i, j);
if (!_dbus_string_move (&command, i, &args, 0))
goto out;
i = 0;
while (auth->handlers[i].command != NULL)
{
if (_dbus_string_equal_c_str (&command,
auth->handlers[i].command))
{
if (!(* auth->handlers[i].func) (auth, &command, &args))
goto out;
break;
}
++i;
}
if (auth->handlers[i].command == NULL)
{
if (!process_unknown (auth, &command, &args))
goto out;
}
/* 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 (&command);
if (!retval)
auth->needed_memory = TRUE;
else
auth->needed_memory = FALSE;
return retval;
}
/** @} */
/**
* @addtogroup DBusAuth
* @{
*/
/**
* Creates a new auth conversation object for the server side.
* See doc/dbus-sasl-profile.txt for full details on what
* this object does.
*
* @returns the new object or #NULL if no memory
*/
DBusAuth*
_dbus_auth_server_new (void)
{
DBusAuth *auth;
auth = _dbus_auth_new (sizeof (DBusAuthServer));
if (auth == NULL)
return NULL;
auth->handlers = server_handlers;
return auth;
}
/**
* Creates a new auth conversation object for the client side.
* See doc/dbus-sasl-profile.txt for full details on what
* this object does.
*
* @returns the new object or #NULL if no memory
*/
DBusAuth*
_dbus_auth_client_new (void)
{
DBusAuth *auth;
auth = _dbus_auth_new (sizeof (DBusAuthClient));
if (auth == NULL)
return NULL;
auth->handlers = client_handlers;
/* Request an auth */
if (!_dbus_string_append (&auth->outgoing,
"AUTH DBUS_STUPID_TEST_MECH\r\n"))
{
_dbus_auth_unref (auth);
return NULL;
}
return auth;
}
/**
* Increments the refcount of an auth object.
*
* @param auth the auth conversation
*/
void
_dbus_auth_ref (DBusAuth *auth)
{
_dbus_assert (auth != NULL);
auth->refcount += 1;
}
/**
* 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)
{
shutdown_mech (auth);
if (DBUS_AUTH_IS_CLIENT (auth))
{
_dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
}
_dbus_string_free (&auth->incoming);
_dbus_string_free (&auth->outgoing);
dbus_free (auth);
}
}
/**
* @param auth the auth conversation object
* @returns #TRUE if we're in a final state
*/
#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->need_disconnect || (auth)->authenticated)
/**
* Analyzes buffered input and moves the auth conversation forward,
* returning the new state of the auth conversation.
*
* @param auth the auth conversation
* @returns the new state
*/
DBusAuthState
_dbus_auth_do_work (DBusAuth *auth)
{
if (DBUS_AUTH_IN_END_STATE (auth))
return get_state (auth);
auth->needed_memory = FALSE;
/* Max amount we'll buffer up before deciding someone's on crack */
#define MAX_BUFFER (16 * 1024)
do
{
if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
_dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
{
auth->need_disconnect = TRUE;
_dbus_verbose ("Disconnecting due to excessive data buffered in auth phase\n");
break;
}
}
while (process_command (auth));
return get_state (auth);
}
/**
* Gets bytes that need to be sent to the
* peer we're conversing with.
*
* @param auth the auth conversation
* @param str initialized string object to be filled in with bytes to send
* @returns #FALSE if not enough memory to fill in the bytes
*/
dbus_bool_t
_dbus_auth_get_bytes_to_send (DBusAuth *auth,
DBusString *str)
{
_dbus_assert (auth != NULL);
_dbus_assert (str != NULL);
if (DBUS_AUTH_IN_END_STATE (auth))
return FALSE;
auth->needed_memory = FALSE;
_dbus_string_set_length (str, 0);
if (!_dbus_string_move (&auth->outgoing,
0, str, 0))
{
auth->needed_memory = TRUE;
return FALSE;
}
if (auth->authenticated_pending_output)
auth->authenticated = TRUE;
return TRUE;
}
/**
* Stores bytes received from the peer we're conversing with.
*
* @param auth the auth conversation
* @param str the received bytes.
* @returns #FALSE if not enough memory to store the bytes.
*/
dbus_bool_t
_dbus_auth_bytes_received (DBusAuth *auth,
const DBusString *str)
{
_dbus_assert (auth != NULL);
_dbus_assert (str != NULL);
if (DBUS_AUTH_IN_END_STATE (auth))
return FALSE;
auth->needed_memory = FALSE;
if (!_dbus_string_copy (str, 0,
&auth->incoming,
_dbus_string_get_length (&auth->incoming)))
{
auth->needed_memory = TRUE;
return FALSE;
}
_dbus_auth_do_work (auth);
return TRUE;
}
/**
* Returns leftover bytes that were not used as part of the auth
* conversation. These bytes will be part of the message stream
* instead. This function may not be called until authentication has
* succeeded.
*
* @param auth the auth conversation
* @param str string to place the unused bytes in
* @returns #FALSE if not enough memory to return the bytes
*/
dbus_bool_t
_dbus_auth_get_unused_bytes (DBusAuth *auth,
DBusString *str)
{
if (!DBUS_AUTH_IN_END_STATE (auth))
return FALSE;
if (!_dbus_string_move (&auth->incoming,
0, str, 0))
return FALSE;
return TRUE;
}
/**
* Called post-authentication, indicates whether we need to encode
* the message stream with _dbus_auth_encode_data() prior to
* sending it to the peer.
*
* @param auth the auth conversation
* @returns #TRUE if we need to encode the stream
*/
dbus_bool_t
_dbus_auth_needs_encoding (DBusAuth *auth)
{
if (!auth->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;
}
/**
* Called post-authentication, encodes a block of bytes for sending to
* the peer. If no encoding was negotiated, just copies the bytes
* (you can avoid this by checking _dbus_auth_needs_encoding()).
*
* @param auth the auth conversation
* @param plaintext the plain text data
* @param encoded initialized string to fill in with encoded data
* @returns #TRUE if we had enough memory and successfully encoded
*/
dbus_bool_t
_dbus_auth_encode_data (DBusAuth *auth,
const DBusString *plaintext,
DBusString *encoded)
{
if (!auth->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, 0);
}
}
/**
* Called post-authentication, indicates whether we need to decode
* the message stream with _dbus_auth_decode_data() after
* receiving it from the peer.
*
* @param auth the auth conversation
* @returns #TRUE if we need to encode the stream
*/
dbus_bool_t
_dbus_auth_needs_decoding (DBusAuth *auth)
{
if (!auth->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;
}
/**
* Called post-authentication, decodes a block of bytes received from
* the peer. If no encoding was negotiated, just copies the bytes (you
* can avoid this by checking _dbus_auth_needs_decoding()).
*
* @param auth the auth conversation
* @param encoded the encoded data
* @param plaintext initialized string to fill in with decoded data
* @returns #TRUE if we had enough memory and successfully decoded
*/
dbus_bool_t
_dbus_auth_decode_data (DBusAuth *auth,
const DBusString *encoded,
DBusString *plaintext)
{
if (!auth->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, 0);
}
}
/** @} */