* @{
*/
-/**
- * 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);
-
-/**
- * Handler for a given auth protocol command
- */
-typedef struct
-{
- const char *command; /**< Name of the command */
- DBusProcessAuthCommandFunction func; /**< Function to handle the command */
-} DBusAuthCommandHandler;
-
/**
* This function appends an initial client response to the given string
*/
DBusAuthShutdownFunction client_shutdown_func; /**< Function on client side for shutdown */
} DBusAuthMechanismHandler;
+/**
+ * Enumeration for the known authentication commands.
+ */
+typedef enum {
+ DBUS_AUTH_COMMAND_AUTH,
+ DBUS_AUTH_COMMAND_CANCEL,
+ DBUS_AUTH_COMMAND_DATA,
+ DBUS_AUTH_COMMAND_BEGIN,
+ DBUS_AUTH_COMMAND_REJECTED,
+ DBUS_AUTH_COMMAND_OK,
+ DBUS_AUTH_COMMAND_ERROR,
+ DBUS_AUTH_COMMAND_UNKNOWN
+} DBusAuthCommand;
+
+/**
+ * Auth state function, determines the reaction to incoming events for
+ * a particular state. Returns whether we had enough memory to
+ * complete the operation.
+ */
+typedef dbus_bool_t (* DBusAuthStateFunction) (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+
+/**
+ * Information about a auth state.
+ */
+typedef struct
+{
+ const char *name; /**< Name of the state */
+ DBusAuthStateFunction handler; /**< State function for this state */
+} DBusAuthStateData;
+
/**
* Internal members of DBusAuth.
*/
struct DBusAuth
{
int refcount; /**< reference count */
+ const char *side; /**< Client or server */
DBusString incoming; /**< Incoming data buffer */
DBusString outgoing; /**< Outgoing data buffer */
- const DBusAuthCommandHandler *handlers; /**< Handlers for commands */
+ const DBusAuthStateData *state; /**< Current protocol state */
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 */
unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */
unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */
} 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_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 dbus_bool_t client_try_next_mechanism (DBusAuth *auth);
+static void goto_state (DBusAuth *auth,
+ const DBusAuthStateData *new_state);
static dbus_bool_t send_auth (DBusAuth *auth,
const DBusAuthMechanismHandler *mech);
static dbus_bool_t send_data (DBusAuth *auth,
static dbus_bool_t send_begin (DBusAuth *auth);
static dbus_bool_t send_cancel (DBusAuth *auth);
-static DBusAuthCommandHandler
-server_handlers[] = {
- { "AUTH", process_auth },
- { "CANCEL", process_cancel },
- { "BEGIN", process_begin },
- { "DATA", process_data_server },
- { "ERROR", process_error_server },
- { NULL, NULL }
+/**
+ * Client states
+ */
+
+static dbus_bool_t handle_server_state_waiting_for_auth (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+static dbus_bool_t handle_server_state_waiting_for_data (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+static dbus_bool_t handle_server_state_waiting_for_begin (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+
+static const DBusAuthStateData server_state_waiting_for_auth = {
+ "WaitingForAuth", handle_server_state_waiting_for_auth
+};
+static const DBusAuthStateData server_state_waiting_for_data = {
+ "WaitingForData", handle_server_state_waiting_for_data
+};
+static const DBusAuthStateData server_state_waiting_for_begin = {
+ "WaitingForBegin", handle_server_state_waiting_for_begin
+};
+
+/**
+ * Client states
+ */
+
+static dbus_bool_t handle_client_state_waiting_for_data (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+static dbus_bool_t handle_client_state_waiting_for_ok (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args);
+
+static const DBusAuthStateData client_state_need_send_auth = {
+ "NeedSendAuth", NULL
+};
+static const DBusAuthStateData client_state_waiting_for_data = {
+ "WaitingForData", handle_client_state_waiting_for_data
};
+static const DBusAuthStateData client_state_waiting_for_ok = {
+ "WaitingForOK", handle_client_state_waiting_for_ok
+};
+static const DBusAuthStateData client_state_waiting_for_reject = {
+ "WaitingForReject", handle_client_state_waiting_for_reject
+};
+
+/**
+ * Common terminal states. Terminal states have handler == NULL.
+ */
-static DBusAuthCommandHandler
-client_handlers[] = {
- { "REJECTED", process_rejected },
- { "OK", process_ok },
- { "DATA", process_data_client },
- { "ERROR", process_error_client },
- { NULL, NULL }
+static const DBusAuthStateData common_state_authenticated = {
+ "Authenticated", NULL
+};
+
+static const DBusAuthStateData common_state_need_disconnect = {
+ "NeedDisconnect", NULL
};
+static const char auth_side_client[] = "client";
+static const char auth_side_server[] = "server";
/**
* @param auth the auth conversation
* @returns #TRUE if the conversation is the server side
*/
-#define DBUS_AUTH_IS_SERVER(auth) ((auth)->handlers == server_handlers)
+#define DBUS_AUTH_IS_SERVER(auth) ((auth)->side == auth_side_server)
/**
* @param auth the auth conversation
* @returns #TRUE if the conversation is the client side
*/
-#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->handlers == client_handlers)
+#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->side == auth_side_client)
/**
* @param auth the auth conversation
* @returns auth cast to DBusAuthClient
* @param auth the auth conversation
* @returns a string
*/
-#define DBUS_AUTH_NAME(auth) (DBUS_AUTH_IS_SERVER(auth) ? "server" : "client")
+#define DBUS_AUTH_NAME(auth) ((auth)->side)
static DBusAuth*
_dbus_auth_new (int size)
shutdown_mech (DBusAuth *auth)
{
/* Cancel any auth */
- auth->authenticated_pending_begin = FALSE;
- auth->authenticated = FALSE;
auth->already_asked_for_initial_response = FALSE;
_dbus_string_set_length (&auth->identity, 0);
if (!send_data (auth, &tmp2))
goto out;
+ goto_state (auth, &server_state_waiting_for_data);
retval = TRUE;
out:
DBUS_AUTH_NAME (auth), auth->desired_identity.uid);
auth->authorized_identity = auth->desired_identity;
- auth->authenticated_pending_begin = TRUE;
retval = TRUE;
out_3:
auth->authorized_identity.uid = auth->desired_identity.uid;
- auth->authenticated_pending_begin = TRUE;
-
return TRUE;
}
else
}
_dbus_string_free (&auth_command);
+ shutdown_mech (auth);
auth->mech = mech;
+ goto_state (auth, &client_state_waiting_for_data);
return TRUE;
}
server_auth->failures += 1;
if (server_auth->failures >= server_auth->max_failures)
- auth->need_disconnect = TRUE;
+ goto_state (auth, &common_state_need_disconnect);
+ else
+ goto_state (auth, &server_state_waiting_for_auth);
_dbus_string_free (&command);
static dbus_bool_t
send_ok (DBusAuth *auth)
{
- return _dbus_string_append (&auth->outgoing, "OK\r\n");
+ if (_dbus_string_append (&auth->outgoing, "OK\r\n"))
+ {
+ goto_state (auth, &server_state_waiting_for_begin);
+ return TRUE;
+ }
+ else
+ return FALSE;
}
static dbus_bool_t
send_begin (DBusAuth *auth)
{
- return _dbus_string_append (&auth->outgoing, "BEGIN\r\n");
+ if (_dbus_string_append (&auth->outgoing, "BEGIN\r\n"))
+ {
+ goto_state (auth, &common_state_authenticated);
+ return TRUE;
+ }
+ else
+ return FALSE;
}
static dbus_bool_t
send_cancel (DBusAuth *auth)
{
- return _dbus_string_append (&auth->outgoing, "CANCEL\r\n");
+ if (_dbus_string_append (&auth->outgoing, "CANCEL\r\n"))
+ {
+ goto_state (auth, &client_state_waiting_for_reject);
+ return TRUE;
+ }
+ else
+ return FALSE;
}
static dbus_bool_t
-process_auth (DBusAuth *auth,
- const DBusString *command,
- const DBusString *args)
+process_data (DBusAuth *auth,
+ const DBusString *args,
+ DBusAuthDataFunction data_func)
{
- if (auth->mech)
+ int end;
+ DBusString decoded;
+
+ if (!_dbus_string_init (&decoded))
+ return FALSE;
+
+ if (!_dbus_string_hex_decode (args, 0, &end, &decoded, 0))
{
- /* We are already using a mechanism, client is on crack */
- if (!send_error (auth, "Sent AUTH while another AUTH in progress"))
+ _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;
}
- else if (_dbus_string_get_length (args) == 0)
+
+#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;
+}
+
+static dbus_bool_t
+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))
}
else
{
- int i, end;
+ int i;
DBusString mech;
DBusString hex_response;
- DBusString decoded_response;
_dbus_string_find_blank (args, 0, &i);
return FALSE;
}
- if (!_dbus_string_init (&decoded_response))
- {
- _dbus_string_free (&mech);
- _dbus_string_free (&hex_response);
- 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;
-
- if (!_dbus_string_hex_decode (&hex_response, 0, &end,
- &decoded_response, 0))
- goto failed;
-
- if (_dbus_string_get_length (&hex_response) != end)
- {
- if (!send_error (auth, "Invalid hex encoding"))
- goto failed;
-
- goto out;
- }
auth->mech = find_mech (&mech, auth->allowed_mechs);
if (auth->mech != NULL)
{
- _dbus_verbose ("%s: Trying mechanism %s with initial response of %d bytes\n",
+ _dbus_verbose ("%s: Trying mechanism %s\n",
DBUS_AUTH_NAME (auth),
- auth->mech->mechanism,
- _dbus_string_get_length (&decoded_response));
+ auth->mech->mechanism);
- if (!(* auth->mech->server_data_func) (auth,
- &decoded_response))
+ 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;
}
- out:
_dbus_string_free (&mech);
_dbus_string_free (&hex_response);
- _dbus_string_free (&decoded_response);
return TRUE;
auth->mech = NULL;
_dbus_string_free (&mech);
_dbus_string_free (&hex_response);
- _dbus_string_free (&decoded_response);
return FALSE;
}
}
static dbus_bool_t
-process_cancel (DBusAuth *auth,
- const DBusString *command,
- const DBusString *args)
+handle_server_state_waiting_for_auth (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
{
- if (!send_rejected (auth))
- return FALSE;
-
- 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
+ switch (command)
{
- auth->need_disconnect = TRUE; /* client trying to send data before auth,
- * kick it
- */
- shutdown_mech (auth);
+ 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_REJECTED:
+ case DBUS_AUTH_COMMAND_OK:
+ case DBUS_AUTH_COMMAND_UNKNOWN:
+ default:
+ return send_error (auth, "Unknown command");
}
-
- return TRUE;
}
static dbus_bool_t
-process_data_server (DBusAuth *auth,
- const DBusString *command,
- const DBusString *args)
+handle_server_state_waiting_for_data (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
{
- int end;
-
- if (auth->mech != NULL)
+ switch (command)
{
- DBusString decoded;
-
- if (!_dbus_string_init (&decoded))
- return FALSE;
+ case DBUS_AUTH_COMMAND_AUTH:
+ return send_error (auth, "Sent AUTH while another AUTH in progress");
- 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;
+ case DBUS_AUTH_COMMAND_CANCEL:
+ case DBUS_AUTH_COMMAND_ERROR:
+ return send_rejected (auth);
- return TRUE;
- }
+ case DBUS_AUTH_COMMAND_DATA:
+ return process_data (auth, args, auth->mech->server_data_func);
-#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 (!(* auth->mech->server_data_func) (auth, &decoded))
- {
- _dbus_string_free (&decoded);
- return FALSE;
- }
+ case DBUS_AUTH_COMMAND_BEGIN:
+ goto_state (auth, &common_state_need_disconnect);
+ return TRUE;
- _dbus_string_free (&decoded);
- }
- else
- {
- if (!send_error (auth, "Not currently in an auth conversation"))
- return FALSE;
+ case DBUS_AUTH_COMMAND_REJECTED:
+ case DBUS_AUTH_COMMAND_OK:
+ case DBUS_AUTH_COMMAND_UNKNOWN:
+ default:
+ return send_error (auth, "Unknown command");
}
-
- return TRUE;
}
static dbus_bool_t
-process_error_server (DBusAuth *auth,
- const DBusString *command,
- const DBusString *args)
+handle_server_state_waiting_for_begin (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
{
- /* Server got error from client, reject the auth,
- * as we don't have anything more intelligent to do.
- */
- if (!send_rejected (auth))
- return FALSE;
-
- return TRUE;
+ 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_REJECTED:
+ case DBUS_AUTH_COMMAND_OK:
+ case DBUS_AUTH_COMMAND_UNKNOWN:
+ default:
+ return send_error (auth, "Unknown command");
+
+ case DBUS_AUTH_COMMAND_CANCEL:
+ case DBUS_AUTH_COMMAND_ERROR:
+ return send_rejected (auth);
+ }
}
/* return FALSE if no memory, TRUE if all OK */
static dbus_bool_t
record_mechanisms (DBusAuth *auth,
- const DBusString *command,
const DBusString *args)
{
int next;
}
static dbus_bool_t
-client_try_next_mechanism (DBusAuth *auth)
+process_rejected (DBusAuth *auth, const DBusString *args)
{
const DBusAuthMechanismHandler *mech;
DBusAuthClient *client;
client = DBUS_AUTH_CLIENT (auth);
- _dbus_assert (client->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),
- auth->mech->mechanism);
-
- return TRUE;
-}
-
-static dbus_bool_t
-process_rejected (DBusAuth *auth,
- const DBusString *command,
- const DBusString *args)
-{
- shutdown_mech (auth);
-
if (!auth->already_got_mechanisms)
{
- if (!record_mechanisms (auth, command, args))
+ if (!record_mechanisms (auth, args))
return FALSE;
}
if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
{
- if (!client_try_next_mechanism (auth))
+ 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));
- auth->need_disconnect = TRUE;
+ goto_state (auth, &common_state_need_disconnect);
}
return TRUE;
}
+
static dbus_bool_t
-process_ok (DBusAuth *auth,
- const DBusString *command,
- const DBusString *args)
+handle_client_state_waiting_for_data (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
{
- if (!send_begin (auth))
- return FALSE;
-
- auth->authenticated_pending_output = TRUE;
-
- return TRUE;
+ _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 send_begin (auth);
+
+ 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:
+ default:
+ return send_error (auth, "Unknown command");
+ }
}
static dbus_bool_t
-process_data_client (DBusAuth *auth,
- const DBusString *command,
- const DBusString *args)
+handle_client_state_waiting_for_ok (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
{
- int end;
-
- if (auth->mech != NULL)
+ switch (command)
{
- DBusString decoded;
+ case DBUS_AUTH_COMMAND_REJECTED:
+ return process_rejected (auth, args);
- if (!_dbus_string_init (&decoded))
- return FALSE;
+ case DBUS_AUTH_COMMAND_OK:
+ return send_begin (auth);
- 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;
- }
+ case DBUS_AUTH_COMMAND_DATA:
+ case DBUS_AUTH_COMMAND_ERROR:
+ return send_cancel (auth);
-#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 (!(* auth->mech->client_data_func) (auth, &decoded))
- {
- _dbus_string_free (&decoded);
- return FALSE;
- }
-
- _dbus_string_free (&decoded);
+ case DBUS_AUTH_COMMAND_AUTH:
+ case DBUS_AUTH_COMMAND_CANCEL:
+ case DBUS_AUTH_COMMAND_BEGIN:
+ case DBUS_AUTH_COMMAND_UNKNOWN:
+ default:
+ return send_error (auth, "Unknown command");
}
- else
+}
+
+static dbus_bool_t
+handle_client_state_waiting_for_reject (DBusAuth *auth,
+ DBusAuthCommand command,
+ const DBusString *args)
+{
+ switch (command)
{
- if (!send_error (auth, "Got DATA when not in an auth exchange"))
- return FALSE;
+ 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:
+ default:
+ goto_state (auth, &common_state_need_disconnect);
+ return TRUE;
}
-
- return TRUE;
}
-static dbus_bool_t
-process_error_client (DBusAuth *auth,
- const DBusString *command,
- const DBusString *args)
+/**
+ * Mapping from command name to enum
+ */
+typedef struct {
+ const char *name; /**< Name of the command */
+ DBusAuthCommand command; /**< Corresponding enum */
+} DBusAuthCommandName;
+
+static DBusAuthCommandName auth_command_names[] = {
+ { "AUTH", DBUS_AUTH_COMMAND_AUTH },
+ { "CANCEL", DBUS_AUTH_COMMAND_CANCEL },
+ { "DATA", DBUS_AUTH_COMMAND_DATA },
+ { "BEGIN", DBUS_AUTH_COMMAND_BEGIN },
+ { "REJECTED", DBUS_AUTH_COMMAND_REJECTED },
+ { "OK", DBUS_AUTH_COMMAND_OK },
+ { "ERROR", DBUS_AUTH_COMMAND_ERROR }
+};
+
+static DBusAuthCommand
+lookup_command_from_name (DBusString *command)
{
- /* Cancel current mechanism, as we don't have anything
- * more clever to do.
- */
- if (!send_cancel (auth))
- return FALSE;
-
- return TRUE;
+ 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;
}
-static dbus_bool_t
-process_unknown (DBusAuth *auth,
- const DBusString *command,
- const DBusString *args)
+static void
+goto_state (DBusAuth *auth, const DBusAuthStateData *state)
{
- if (!send_error (auth, "Unknown command"))
- return FALSE;
+ _dbus_verbose ("%s: going from state %s to state %s\n",
+ DBUS_AUTH_NAME (auth),
+ auth->state->name,
+ state->name);
- return TRUE;
+ auth->state = state;
}
/* returns whether to call it again right away */
static dbus_bool_t
process_command (DBusAuth *auth)
{
- DBusString command;
+ DBusAuthCommand command;
+ DBusString line;
DBusString args;
int eol;
int i, j;
if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
return FALSE;
- if (!_dbus_string_init (&command))
+ if (!_dbus_string_init (&line))
{
auth->needed_memory = TRUE;
return FALSE;
if (!_dbus_string_init (&args))
{
- _dbus_string_free (&command);
+ _dbus_string_free (&line);
auth->needed_memory = TRUE;
return FALSE;
}
- if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0))
+ if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &line, 0))
goto out;
- if (!_dbus_string_validate_ascii (&command, 0,
- _dbus_string_get_length (&command)))
+ 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));
_dbus_verbose ("%s: got command \"%s\"\n",
DBUS_AUTH_NAME (auth),
- _dbus_string_get_const_data (&command));
+ _dbus_string_get_const_data (&line));
- _dbus_string_find_blank (&command, 0, &i);
- _dbus_string_skip_blank (&command, i, &j);
+ _dbus_string_find_blank (&line, 0, &i);
+ _dbus_string_skip_blank (&line, i, &j);
if (j > i)
- _dbus_string_delete (&command, i, j - i);
+ _dbus_string_delete (&line, i, j - i);
- if (!_dbus_string_move (&command, i, &args, 0))
+ if (!_dbus_string_move (&line, i, &args, 0))
goto out;
- i = 0;
- while (auth->handlers[i].command != NULL)
- {
- if (_dbus_string_equal_c_str (&command,
- auth->handlers[i].command))
- {
- _dbus_verbose ("%s: Processing auth command %s\n",
- DBUS_AUTH_NAME (auth),
- 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;
- }
+ command = lookup_command_from_name (&line);
+ if (!(* auth->state->handler) (auth, command, &args))
+ goto out;
next_command:
out:
_dbus_string_free (&args);
- _dbus_string_free (&command);
+ _dbus_string_free (&line);
if (!retval)
auth->needed_memory = TRUE;
if (auth == NULL)
return NULL;
- auth->handlers = server_handlers;
+ auth->side = auth_side_server;
+ auth->state = &server_state_waiting_for_auth;
server_auth = DBUS_AUTH_SERVER (auth);
if (auth == NULL)
return NULL;
- auth->handlers = client_handlers;
+ auth->side = auth_side_client;
+ auth->state = &client_state_need_send_auth;
/* Start the auth conversation by sending AUTH for our default
* mechanism */
* @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)
+#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->state->handler == NULL)
/**
* Analyzes buffered input and moves the auth conversation forward,
if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
_dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
{
- auth->need_disconnect = TRUE;
+ 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->need_disconnect)
- return DBUS_AUTH_STATE_NEED_DISCONNECT;
- else if (auth->authenticated)
- return DBUS_AUTH_STATE_AUTHENTICATED;
- else if (auth->needed_memory)
+ 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;
+ 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;
}
/**
*str = NULL;
- if (DBUS_AUTH_IN_END_STATE (auth))
- return FALSE;
-
if (_dbus_string_get_length (&auth->outgoing) == 0)
return FALSE;
_dbus_string_delete (&auth->outgoing,
0, bytes_sent);
-
- if (auth->authenticated_pending_output &&
- _dbus_string_get_length (&auth->outgoing) == 0)
- auth->authenticated = TRUE;
}
/**
dbus_bool_t
_dbus_auth_needs_encoding (DBusAuth *auth)
{
- if (!auth->authenticated)
+ if (auth->state != &common_state_authenticated)
return FALSE;
if (auth->mech != NULL)
{
_dbus_assert (plaintext != encoded);
- if (!auth->authenticated)
+ if (auth->state != &common_state_authenticated)
return FALSE;
if (_dbus_auth_needs_encoding (auth))
dbus_bool_t
_dbus_auth_needs_decoding (DBusAuth *auth)
{
- if (!auth->authenticated)
+ if (auth->state != &common_state_authenticated)
return FALSE;
if (auth->mech != NULL)
{
_dbus_assert (plaintext != encoded);
- if (!auth->authenticated)
+ if (auth->state != &common_state_authenticated)
return FALSE;
if (_dbus_auth_needs_decoding (auth))
_dbus_auth_get_identity (DBusAuth *auth,
DBusCredentials *credentials)
{
- if (auth->authenticated)
+ if (auth->state == &common_state_authenticated)
*credentials = auth->authorized_identity;
else
_dbus_credentials_clear (credentials);
<listitem><para>AUTH [mechanism] [initial-response]</para></listitem>
<listitem><para>CANCEL</para></listitem>
<listitem><para>BEGIN</para></listitem>
- <listitem><para>DATA <data in base 64 encoding></para></listitem>
+ <listitem><para>DATA <data in hex encoding></para></listitem>
<listitem><para>ERROR [human-readable error explanation]</para></listitem>
</itemizedlist>
<itemizedlist>
<listitem><para>REJECTED <space-separated list of mechanism names></para></listitem>
<listitem><para>OK</para></listitem>
- <listitem><para>DATA <data in base 64 encoding></para></listitem>
+ <listitem><para>DATA <data in hex encoding></para></listitem>
<listitem><para>ERROR</para></listitem>
</itemizedlist>
</para>
<programlisting>
(MAGIC_COOKIE is a made up mechanism)
- C: AUTH MAGIC_COOKIE BsAY3g4gBNo=
+ C: AUTH MAGIC_COOKIE 3138363935333137393635383634
S: OK
C: BEGIN
</programlisting>
<programlisting>
C: AUTH
S: REJECTED KERBEROS_V4 SKEY
- C: AUTH SKEY bW9yZ2Fu
- S: DATA OTUgUWE1ODMwOA==
- C: DATA Rk9VUiBNQU5OIFNPT04gRklSIFZBUlkgTUFTSA==
+ C: AUTH SKEY 7ab83f32ee
+ S: DATA 8799cabb2ea93e
+ C: DATA 8ac876e8f68ee9809bfa876e6f9876g8fa8e76e98f
S: OK
C: BEGIN
</programlisting>
<programlisting>
C: FOOBAR
S: ERROR
- C: AUTH MAGIC_COOKIE BsAY3g4gBNo=
+ C: AUTH MAGIC_COOKIE 3736343435313230333039
S: OK
C: BEGIN
</programlisting>
<figure>
<title>Example of server doesn't support initial auth mechanism</title>
<programlisting>
- C: AUTH MAGIC_COOKIE BsAY3g4gBNo=
+ C: AUTH MAGIC_COOKIE 3736343435313230333039
S: REJECTED KERBEROS_V4 SKEY
- C: AUTH SKEY bW9yZ2Fu
- S: DATA OTUgUWE1ODMwOA==
- C: DATA Rk9VUiBNQU5OIFNPT04gRklSIFZBUlkgTUFTSA==
+ C: AUTH SKEY 7ab83f32ee
+ S: DATA 8799cabb2ea93e
+ C: DATA 8ac876e8f68ee9809bfa876e6f9876g8fa8e76e98f
S: OK
C: BEGIN
</programlisting>
<figure>
<title>Example of wrong password or the like followed by successful retry</title>
<programlisting>
- C: AUTH MAGIC_COOKIE BsAY3g4gBNo=
+ C: AUTH MAGIC_COOKIE 3736343435313230333039
S: REJECTED KERBEROS_V4 SKEY
- C: AUTH SKEY bW9yZ2Fu
- S: DATA OTUgUWE1ODMwOA==
- C: DATA Rk9VUiBNQU5OIFNPT04gRklSIFZBUlkgTUFTSA==
+ C: AUTH SKEY 7ab83f32ee
+ S: DATA 8799cabb2ea93e
+ C: DATA 8ac876e8f68ee9809bfa876e6f9876g8fa8e76e98f
S: REJECTED
- C: AUTH SKEY bW9yZ2Fu
- S: DATA OTUgUWE1ODMwOA==
- C: DATA Rk9VUiBNQU5OIFNPT04gRklSIFZBUlkgTUFTSA==
+ C: AUTH SKEY 7ab83f32ee
+ S: DATA 8799cabb2ea93e
+ C: DATA 8ac876e8f68ee9809bfa876e6f9876g8fa8e76e98f
S: OK
C: BEGIN
</programlisting>
<figure>
<title>Example of skey cancelled and restarted</title>
<programlisting>
- C: AUTH MAGIC_COOKIE BsAY3g4gBNo=
+ C: AUTH MAGIC_COOKIE 3736343435313230333039
S: REJECTED KERBEROS_V4 SKEY
- C: AUTH SKEY bW9yZ2Fu
- S: DATA OTUgUWE1ODMwOA==
+ C: AUTH SKEY 7ab83f32ee
+ S: DATA 8799cabb2ea93e
C: CANCEL
S: REJECTED
- C: AUTH SKEY bW9yZ2Fu
- S: DATA OTUgUWE1ODMwOA==
- C: DATA Rk9VUiBNQU5OIFNPT04gRklSIFZBUlkgTUFTSA==
+ C: AUTH SKEY 7ab83f32ee
+ S: DATA 8799cabb2ea93e
+ C: DATA 8ac876e8f68ee9809bfa876e6f9876g8fa8e76e98f
S: OK
C: BEGIN
</programlisting>
<sect3 id="auth-states-client">
<title>Client states</title>
- <formalpara>
- <title><emphasis>Start</emphasis></title>
- <para>
- <itemizedlist>
- <listitem><para>send AUTH with initial data -> <emphasis>WaitingForData</emphasis></para></listitem>
- <listitem><para>send AUTH with no initial data -> <emphasis>WaitingForData</emphasis> or <emphasis>NeedSendData</emphasis> (depends on mechanism)</para></listitem>
- </itemizedlist>
- The <emphasis>Start</emphasis> state is stateful (it has a list of
- available mechanisms and those it has already attempted). This list
- is used to decide which AUTH command to send. When the list is
- exhausted, the client should give up and close the connection.
- </para>
- </formalpara>
+ <para>
+ To more precisely describe the interaction between the
+ protocol state machine and the authentication mechanisms the
+ following notation is used: MECH(CHALL) means that the
+ server challenge CHALL was fed to the mechanism MECH, which
+ returns one of
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ CONTINUE(RESP) means continue the auth conversation
+ and send RESP as the response to the server;
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ OK(RESP) means that after sending RESP to the server
+ the client side of the auth conversation is finished
+ and the server should return "OK";
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ ERROR means that CHALL was invalid and could not be
+ processed.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ Both RESP and CHALL may be empty.
+ </para>
+
+ <para>
+ The Client starts by getting an initial response from the
+ default mechanism and sends AUTH MECH RESP, or AUTH MECH if
+ the mechanism did not provide an initial response. If the
+ mechanism returns CONTINUE, the client starts in state
+ <emphasis>WaitingForData</emphasis>, if the mechanism
+ returns OK the client starts in state
+ <emphasis>WaitingForOK</emphasis>.
+ </para>
+
+ <para>
+ The client should keep track of available mechanisms and
+ which it mechanisms it has already attempted. This list is
+ used to decide which AUTH command to send. When the list is
+ exhausted, the client should give up and close the
+ connection.
+ </para>
<formalpara>
<title><emphasis>WaitingForData</emphasis></title>
<para>
-
<itemizedlist>
- <listitem><para>receive OK -> <emphasis>NeedSendBegin</emphasis></para></listitem>
- <listitem><para>receive REJECTED -> <emphasis>Start</emphasis></para></listitem>
- <listitem><para>receive ERROR -> <emphasis>Start</emphasis></para></listitem>
- <listitem><para>receive DATA -> <emphasis>NeedSendData</emphasis></para></listitem>
- <listitem><para>receive anything else -> <emphasis>NeedSendError</emphasis></para></listitem>
- </itemizedlist>
- When going back to <emphasis>Start</emphasis>, the mechanism in
- progress should be marked as failed and not retried (at least not
- with the same parameters). When receiving REJECTED with a list of
- mechanisms, the list should be recorded and used to select
- a mechanism.
- </para>
- </formalpara>
+ <listitem>
+ <para>
+ Receive DATA CHALL
+ <simplelist>
+ <member>
+ MECH(CHALL) returns CONTINUE(RESP) → send
+ DATA RESP, goto
+ <emphasis>WaitingForData</emphasis>
+ </member>
+
+ <member>
+ MECH(CHALL) returns OK(RESP) → send DATA
+ RESP, goto <emphasis>WaitingForOK</emphasis>
+ </member>
+
+ <member>
+ MECH(CHALL) returns ERROR → send ERROR
+ [msg], goto <emphasis>WaitingForData</emphasis>
+ </member>
+ </simplelist>
+ </para>
+ </listitem>
- <formalpara>
- <title><emphasis>NeedSendData</emphasis></title>
- <para>
- <itemizedlist>
- <listitem><para>send DATA -> <emphasis>WaitingForData</emphasis></para></listitem>
- <listitem><para>send CANCEL -> <emphasis>Start</emphasis></para></listitem>
+ <listitem>
+ <para>
+ Receive REJECTED [mechs] →
+ send AUTH [next mech], goto
+ WaitingForData or <emphasis>WaitingForOK</emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Receive ERROR → send
+ CANCEL, goto
+ <emphasis>WaitingForReject</emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Receive OK → send
+ BEGIN, terminate auth
+ conversation, authenticated
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Receive anything else → send
+ ERROR, goto
+ <emphasis>WaitingForData</emphasis>
+ </para>
+ </listitem>
</itemizedlist>
</para>
</formalpara>
<formalpara>
- <title><emphasis>NeedSendError</emphasis></title>
+ <title><emphasis>WaitingForOK</emphasis></title>
<para>
-
<itemizedlist>
- <listitem><para>send ERROR -> return to previous state</para></listitem>
+ <listitem>
+ <para>
+ Receive OK → send BEGIN, terminate auth
+ conversation, <emphasis>authenticated</emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Receive REJECT [mechs] → send AUTH [next mech],
+ goto <emphasis>WaitingForData</emphasis> or
+ <emphasis>WaitingForOK</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive DATA → send CANCEL, goto
+ <emphasis>WaitingForReject</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive ERROR → send CANCEL, goto
+ <emphasis>WaitingForReject</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive anything else → send ERROR, goto
+ <emphasis>WaitingForOK</emphasis>
+ </para>
+ </listitem>
</itemizedlist>
</para>
</formalpara>
<formalpara>
- <title><emphasis>NeedSendBegin</emphasis></title>
+ <title><emphasis>WaitingForReject</emphasis></title>
<para>
-
<itemizedlist>
- <listitem><para>send BEGIN -> Authorized</para></listitem>
- </itemizedlist>
- </para>
- </formalpara>
+ <listitem>
+ <para>
+ Receive REJECT [mechs] → send AUTH [next mech],
+ goto <emphasis>WaitingForData</emphasis> or
+ <emphasis>WaitingForOK</emphasis>
+ </para>
+ </listitem>
- <formalpara>
- <title><emphasis>Authorized</emphasis></title>
- <para>
- This is the end state, flow of messages begins.
+ <listitem>
+ <para>
+ Receive anything else → terminate auth
+ conversation, disconnect
+ </para>
+ </listitem>
+ </itemizedlist>
</para>
</formalpara>
<sect3 id="auth-states-server">
<title>Server states</title>
-
+
+ <para>
+ For the server MECH(RESP) means that the client response
+ RESP was fed to the the mechanism MECH, which returns one of
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ CONTINUE(CHALL) means continue the auth conversation and
+ send CHALL as the challenge to the client;
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ OK means that the client has been successfully
+ authenticated;
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ REJECT means that the client failed to authenticate or
+ there was an error in RESP.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ The server starts out in state
+ <emphasis>WaitingForAuth</emphasis>. If the client is
+ rejected too many times the server must disconnect the
+ client.
+ </para>
+
<formalpara>
<title><emphasis>WaitingForAuth</emphasis></title>
<para>
<itemizedlist>
- <listitem><para>receive AUTH with initial response -> <emphasis>NeedSendData</emphasis></para></listitem>
- <listitem><para>receive AUTH without initial response -> <emphasis>NeedSendData</emphasis> or <emphasis>WaitingForData</emphasis> depending on mechanism</para></listitem>
- </itemizedlist>
- </para>
- </formalpara>
- <formalpara>
- <title><emphasis>NeedSendData</emphasis></title>
- <para>
- <itemizedlist>
- <listitem><para>send DATA -> <emphasis>WaitingForData</emphasis></para></listitem>
- <listitem><para>send ERROR -> <emphasis>WaitingForData</emphasis></para></listitem>
- <listitem><para>send REJECTED -> <emphasis>WaitingForAuth</emphasis></para></listitem>
- <listitem><para>send OK -> <emphasis>WaitingForBegin</emphasis></para></listitem>
+ <listitem>
+ <para>
+ Receive AUTH → send REJECTED [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive AUTH MECH RESP
+
+ <simplelist>
+ <member>
+ MECH not valid mechanism → send REJECTED
+ [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </member>
+
+ <member>
+ MECH(RESP) returns CONTINUE(CHALL) → send
+ DATA CHALL, goto
+ <emphasis>WaitingForData</emphasis>
+ </member>
+
+ <member>
+ MECH(RESP) returns OK → send OK, goto
+ <emphasis>WaitingForBegin</emphasis>
+ </member>
+
+ <member>
+ MECH(RESP) returns REJECT → send REJECTED
+ [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </member>
+ </simplelist>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive BEGIN → terminate
+ auth conversation, disconnect
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive ERROR → send REJECTED [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive anything else → send
+ ERROR, goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
</itemizedlist>
</para>
</formalpara>
+
<formalpara>
<title><emphasis>WaitingForData</emphasis></title>
<para>
<itemizedlist>
- <listitem><para>receive DATA -> <emphasis>NeedSendData</emphasis></para></listitem>
- <listitem><para>receive CANCEL -> <emphasis>NeedSendRejected</emphasis></para></listitem>
- <listitem><para>receive ERROR -> <emphasis>NeedSendRejected</emphasis></para></listitem>
- <listitem><para>receive anything else -> <emphasis>NeedSendError</emphasis></para></listitem>
- </itemizedlist>
- </para>
- </formalpara>
+ <listitem>
+ <para>
+ Receive DATA RESP
+ <simplelist>
+ <member>
+ MECH(RESP) returns CONTINUE(CHALL) → send
+ DATA CHALL, goto
+ <emphasis>WaitingForData</emphasis>
+ </member>
+
+ <member>
+ MECH(RESP) returns OK → send OK, goto
+ <emphasis>WaitingForBegin</emphasis>
+ </member>
+
+ <member>
+ MECH(RESP) returns REJECT → send REJECTED
+ [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </member>
+ </simplelist>
+ </para>
+ </listitem>
- <formalpara>
- <title><emphasis>NeedSendError</emphasis></title>
- <para>
-
- <itemizedlist>
- <listitem><para>send ERROR -> return to previous state</para></listitem>
- </itemizedlist>
- </para>
- </formalpara>
+ <listitem>
+ <para>
+ Receive BEGIN → terminate auth conversation,
+ disconnect
+ </para>
+ </listitem>
- <formalpara>
- <title><emphasis>NeedSendRejected</emphasis></title>
- <para>
-
- <itemizedlist>
- <listitem><para>send REJECTED -> <emphasis>WaitingForAuth</emphasis></para></listitem>
+ <listitem>
+ <para>
+ Receive CANCEL → send REJECTED [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive ERROR → send REJECTED [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive anything else → send ERROR, goto
+ <emphasis>WaitingForData</emphasis>
+ </para>
+ </listitem>
</itemizedlist>
</para>
</formalpara>
<formalpara>
<title><emphasis>WaitingForBegin</emphasis></title>
<para>
-
<itemizedlist>
- <listitem><para>receive BEGIN -> <emphasis>Authorized</emphasis></para></listitem>
- <listitem><para>receive anything else -> <emphasis>NeedSendError</emphasis></para></listitem>
- </itemizedlist>
- </para>
- </formalpara>
+ <listitem>
+ <para>
+ Receive BEGIN → terminate auth conversation,
+ client authenticated
+ </para>
+ </listitem>
- <formalpara>
- <title><emphasis>Authorized</emphasis></title>
- <para>
- This is the end state, flow of messages begins.
+ <listitem>
+ <para>
+ Receive CANCEL → send REJECTED [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive ERROR → send REJECTED [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive anything else → send ERROR, goto
+ <emphasis>WaitingForBegin</emphasis>
+ </para>
+ </listitem>
+ </itemizedlist>
</para>
</formalpara>