summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKristian Høgsberg <krh@redhat.com>2004-06-02 14:03:58 +0000
committerKristian Høgsberg <krh@redhat.com>2004-06-02 14:03:58 +0000
commit63de4681299428db8be68bab64b969e0c1229273 (patch)
tree50fd32ab37792c2a70bd2d2ecb7fc6cd65d517f5
parent54dcec2a8312634116c5a1acbbd0070953525c8a (diff)
2004-06-02 Kristian Høgsberg <krh@redhat.com>
* dbus/dbus-auth.c: Rewrite auth protocol handling to use a state machine approach. A state is implemented as a function that handles incoming events as specified for that state. * doc/dbus-specification.xml: Update auth protocol state machine specification to match implementation. Remove some leftover base64 examples.
-rw-r--r--ChangeLog10
-rw-r--r--dbus/dbus-auth.c741
-rw-r--r--doc/dbus-specification.xml461
-rw-r--r--test/data/auth/fail-after-n-attempts.auth-script1
4 files changed, 747 insertions, 466 deletions
diff --git a/ChangeLog b/ChangeLog
index e18acc0e..2f1a6973 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,14 @@
2004-06-02 Kristian Høgsberg <krh@redhat.com>
+
+ * dbus/dbus-auth.c: Rewrite auth protocol handling to use a state
+ machine approach. A state is implemented as a function that
+ handles incoming events as specified for that state.
+
+ * doc/dbus-specification.xml: Update auth protocol state machine
+ specification to match implementation. Remove some leftover
+ base64 examples.
+
+2004-06-02 Kristian Høgsberg <krh@redhat.com>
* glib/dbus-gproxy.c, glib/dbus-gmain.c, dbus/dbus-string.c,
dbus/dbus-object-tree.c, dbus/dbus-message.c: add comments to
diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c
index 57616673..e41137f8 100644
--- a/dbus/dbus-auth.c
+++ b/dbus/dbus-auth.c
@@ -66,23 +66,6 @@
*/
/**
- * 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
*/
typedef dbus_bool_t (* DBusInitialResponseFunction) (DBusAuth *auth,
@@ -132,16 +115,49 @@ typedef struct
} 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 */
@@ -169,10 +185,6 @@ struct DBusAuth
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 */
@@ -201,36 +213,8 @@ typedef struct
} 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,
@@ -242,35 +226,81 @@ static dbus_bool_t send_ok (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
@@ -287,7 +317,7 @@ client_handlers[] = {
* @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)
@@ -355,8 +385,6 @@ static void
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);
@@ -591,6 +619,7 @@ sha1_handle_first_client_response (DBusAuth *auth,
if (!send_data (auth, &tmp2))
goto out;
+ goto_state (auth, &server_state_waiting_for_data);
retval = TRUE;
out:
@@ -685,7 +714,6 @@ sha1_handle_second_client_response (DBusAuth *auth,
DBUS_AUTH_NAME (auth), auth->desired_identity.uid);
auth->authorized_identity = auth->desired_identity;
- auth->authenticated_pending_begin = TRUE;
retval = TRUE;
out_3:
@@ -1022,8 +1050,6 @@ handle_server_data_external_mech (DBusAuth *auth,
auth->authorized_identity.uid = auth->desired_identity.uid;
- auth->authenticated_pending_begin = TRUE;
-
return TRUE;
}
else
@@ -1199,7 +1225,9 @@ send_auth (DBusAuth *auth, const DBusAuthMechanismHandler *mech)
}
_dbus_string_free (&auth_command);
+ shutdown_mech (auth);
auth->mech = mech;
+ goto_state (auth, &client_state_waiting_for_data);
return TRUE;
}
@@ -1275,7 +1303,9 @@ send_rejected (DBusAuth *auth)
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);
@@ -1296,35 +1326,87 @@ send_error (DBusAuth *auth, const char *message)
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))
@@ -1334,10 +1416,9 @@ process_auth (DBusAuth *auth,
}
else
{
- int i, end;
+ int i;
DBusString mech;
DBusString hex_response;
- DBusString decoded_response;
_dbus_string_find_blank (args, 0, &i);
@@ -1350,55 +1431,37 @@ process_auth (DBusAuth *auth,
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;
@@ -1406,106 +1469,95 @@ process_auth (DBusAuth *auth,
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 */
@@ -1532,7 +1584,6 @@ get_word (const DBusString *str,
static dbus_bool_t
record_mechanisms (DBusAuth *auth,
- const DBusString *command,
const DBusString *args)
{
int next;
@@ -1602,157 +1653,173 @@ record_mechanisms (DBusAuth *auth,
}
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;
@@ -1766,7 +1833,7 @@ process_command (DBusAuth *auth)
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;
@@ -1774,16 +1841,16 @@ process_command (DBusAuth *auth)
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));
@@ -1795,40 +1862,20 @@ process_command (DBusAuth *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:
@@ -1845,7 +1892,7 @@ process_command (DBusAuth *auth)
out:
_dbus_string_free (&args);
- _dbus_string_free (&command);
+ _dbus_string_free (&line);
if (!retval)
auth->needed_memory = TRUE;
@@ -1880,7 +1927,8 @@ _dbus_auth_server_new (void)
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);
@@ -1909,7 +1957,8 @@ _dbus_auth_client_new (void)
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 */
@@ -2008,7 +2057,7 @@ _dbus_auth_set_mechanisms (DBusAuth *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)
+#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->state->handler == NULL)
/**
* Analyzes buffered input and moves the auth conversation forward,
@@ -2033,7 +2082,7 @@ _dbus_auth_do_work (DBusAuth *auth)
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;
@@ -2041,16 +2090,15 @@ _dbus_auth_do_work (DBusAuth *auth)
}
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;
}
/**
@@ -2071,9 +2119,6 @@ _dbus_auth_get_bytes_to_send (DBusAuth *auth,
*str = NULL;
- if (DBUS_AUTH_IN_END_STATE (auth))
- return FALSE;
-
if (_dbus_string_get_length (&auth->outgoing) == 0)
return FALSE;
@@ -2101,10 +2146,6 @@ _dbus_auth_bytes_sent (DBusAuth *auth,
_dbus_string_delete (&auth->outgoing,
0, bytes_sent);
-
- if (auth->authenticated_pending_output &&
- _dbus_string_get_length (&auth->outgoing) == 0)
- auth->authenticated = TRUE;
}
/**
@@ -2190,7 +2231,7 @@ _dbus_auth_delete_unused_bytes (DBusAuth *auth)
dbus_bool_t
_dbus_auth_needs_encoding (DBusAuth *auth)
{
- if (!auth->authenticated)
+ if (auth->state != &common_state_authenticated)
return FALSE;
if (auth->mech != NULL)
@@ -2221,7 +2262,7 @@ _dbus_auth_encode_data (DBusAuth *auth,
{
_dbus_assert (plaintext != encoded);
- if (!auth->authenticated)
+ if (auth->state != &common_state_authenticated)
return FALSE;
if (_dbus_auth_needs_encoding (auth))
@@ -2249,7 +2290,7 @@ _dbus_auth_encode_data (DBusAuth *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)
@@ -2284,7 +2325,7 @@ _dbus_auth_decode_data (DBusAuth *auth,
{
_dbus_assert (plaintext != encoded);
- if (!auth->authenticated)
+ if (auth->state != &common_state_authenticated)
return FALSE;
if (_dbus_auth_needs_decoding (auth))
@@ -2326,7 +2367,7 @@ void
_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);
diff --git a/doc/dbus-specification.xml b/doc/dbus-specification.xml
index aacb480a..94f72bf8 100644
--- a/doc/dbus-specification.xml
+++ b/doc/dbus-specification.xml
@@ -822,7 +822,7 @@
<listitem><para>AUTH [mechanism] [initial-response]</para></listitem>
<listitem><para>CANCEL</para></listitem>
<listitem><para>BEGIN</para></listitem>
- <listitem><para>DATA &lt;data in base 64 encoding&gt;</para></listitem>
+ <listitem><para>DATA &lt;data in hex encoding&gt;</para></listitem>
<listitem><para>ERROR [human-readable error explanation]</para></listitem>
</itemizedlist>
@@ -831,7 +831,7 @@
<itemizedlist>
<listitem><para>REJECTED &lt;space-separated list of mechanism names&gt;</para></listitem>
<listitem><para>OK</para></listitem>
- <listitem><para>DATA &lt;data in base 64 encoding&gt;</para></listitem>
+ <listitem><para>DATA &lt;data in hex encoding&gt;</para></listitem>
<listitem><para>ERROR</para></listitem>
</itemizedlist>
</para>
@@ -994,7 +994,7 @@
<programlisting>
(MAGIC_COOKIE is a made up mechanism)
- C: AUTH MAGIC_COOKIE BsAY3g4gBNo=
+ C: AUTH MAGIC_COOKIE 3138363935333137393635383634
S: OK
C: BEGIN
</programlisting>
@@ -1004,9 +1004,9 @@
<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>
@@ -1016,7 +1016,7 @@
<programlisting>
C: FOOBAR
S: ERROR
- C: AUTH MAGIC_COOKIE BsAY3g4gBNo=
+ C: AUTH MAGIC_COOKIE 3736343435313230333039
S: OK
C: BEGIN
</programlisting>
@@ -1024,11 +1024,11 @@
<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>
@@ -1036,15 +1036,15 @@
<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>
@@ -1052,15 +1052,15 @@
<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>
@@ -1079,73 +1079,178 @@
<sect3 id="auth-states-client">
<title>Client states</title>
- <formalpara>
- <title><emphasis>Start</emphasis></title>
- <para>
- <itemizedlist>
- <listitem><para>send AUTH with initial data -&gt; <emphasis>WaitingForData</emphasis></para></listitem>
- <listitem><para>send AUTH with no initial data -&gt; <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 -&gt; <emphasis>NeedSendBegin</emphasis></para></listitem>
- <listitem><para>receive REJECTED -&gt; <emphasis>Start</emphasis></para></listitem>
- <listitem><para>receive ERROR -&gt; <emphasis>Start</emphasis></para></listitem>
- <listitem><para>receive DATA -&gt; <emphasis>NeedSendData</emphasis></para></listitem>
- <listitem><para>receive anything else -&gt; <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) &rarr; send
+ DATA RESP, goto
+ <emphasis>WaitingForData</emphasis>
+ </member>
+
+ <member>
+ MECH(CHALL) returns OK(RESP) &rarr; send DATA
+ RESP, goto <emphasis>WaitingForOK</emphasis>
+ </member>
+
+ <member>
+ MECH(CHALL) returns ERROR &rarr; send ERROR
+ [msg], goto <emphasis>WaitingForData</emphasis>
+ </member>
+ </simplelist>
+ </para>
+ </listitem>
- <formalpara>
- <title><emphasis>NeedSendData</emphasis></title>
- <para>
- <itemizedlist>
- <listitem><para>send DATA -&gt; <emphasis>WaitingForData</emphasis></para></listitem>
- <listitem><para>send CANCEL -&gt; <emphasis>Start</emphasis></para></listitem>
+ <listitem>
+ <para>
+ Receive REJECTED [mechs] &rarr;
+ send AUTH [next mech], goto
+ WaitingForData or <emphasis>WaitingForOK</emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Receive ERROR &rarr; send
+ CANCEL, goto
+ <emphasis>WaitingForReject</emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Receive OK &rarr; send
+ BEGIN, terminate auth
+ conversation, authenticated
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Receive anything else &rarr; 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 -&gt; return to previous state</para></listitem>
+ <listitem>
+ <para>
+ Receive OK &rarr; send BEGIN, terminate auth
+ conversation, <emphasis>authenticated</emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Receive REJECT [mechs] &rarr; send AUTH [next mech],
+ goto <emphasis>WaitingForData</emphasis> or
+ <emphasis>WaitingForOK</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive DATA &rarr; send CANCEL, goto
+ <emphasis>WaitingForReject</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive ERROR &rarr; send CANCEL, goto
+ <emphasis>WaitingForReject</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive anything else &rarr; 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 -&gt; Authorized</para></listitem>
- </itemizedlist>
- </para>
- </formalpara>
+ <listitem>
+ <para>
+ Receive REJECT [mechs] &rarr; 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 &rarr; terminate auth
+ conversation, disconnect
+ </para>
+ </listitem>
+ </itemizedlist>
</para>
</formalpara>
@@ -1153,57 +1258,164 @@
<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 -&gt; <emphasis>NeedSendData</emphasis></para></listitem>
- <listitem><para>receive AUTH without initial response -&gt; <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 -&gt; <emphasis>WaitingForData</emphasis></para></listitem>
- <listitem><para>send ERROR -&gt; <emphasis>WaitingForData</emphasis></para></listitem>
- <listitem><para>send REJECTED -&gt; <emphasis>WaitingForAuth</emphasis></para></listitem>
- <listitem><para>send OK -&gt; <emphasis>WaitingForBegin</emphasis></para></listitem>
+ <listitem>
+ <para>
+ Receive AUTH &rarr; send REJECTED [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive AUTH MECH RESP
+
+ <simplelist>
+ <member>
+ MECH not valid mechanism &rarr; send REJECTED
+ [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </member>
+
+ <member>
+ MECH(RESP) returns CONTINUE(CHALL) &rarr; send
+ DATA CHALL, goto
+ <emphasis>WaitingForData</emphasis>
+ </member>
+
+ <member>
+ MECH(RESP) returns OK &rarr; send OK, goto
+ <emphasis>WaitingForBegin</emphasis>
+ </member>
+
+ <member>
+ MECH(RESP) returns REJECT &rarr; send REJECTED
+ [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </member>
+ </simplelist>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive BEGIN &rarr; terminate
+ auth conversation, disconnect
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive ERROR &rarr; send REJECTED [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive anything else &rarr; send
+ ERROR, goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
</itemizedlist>
</para>
</formalpara>
+
<formalpara>
<title><emphasis>WaitingForData</emphasis></title>
<para>
<itemizedlist>
- <listitem><para>receive DATA -&gt; <emphasis>NeedSendData</emphasis></para></listitem>
- <listitem><para>receive CANCEL -&gt; <emphasis>NeedSendRejected</emphasis></para></listitem>
- <listitem><para>receive ERROR -&gt; <emphasis>NeedSendRejected</emphasis></para></listitem>
- <listitem><para>receive anything else -&gt; <emphasis>NeedSendError</emphasis></para></listitem>
- </itemizedlist>
- </para>
- </formalpara>
+ <listitem>
+ <para>
+ Receive DATA RESP
+ <simplelist>
+ <member>
+ MECH(RESP) returns CONTINUE(CHALL) &rarr; send
+ DATA CHALL, goto
+ <emphasis>WaitingForData</emphasis>
+ </member>
+
+ <member>
+ MECH(RESP) returns OK &rarr; send OK, goto
+ <emphasis>WaitingForBegin</emphasis>
+ </member>
+
+ <member>
+ MECH(RESP) returns REJECT &rarr; send REJECTED
+ [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </member>
+ </simplelist>
+ </para>
+ </listitem>
- <formalpara>
- <title><emphasis>NeedSendError</emphasis></title>
- <para>
-
- <itemizedlist>
- <listitem><para>send ERROR -&gt; return to previous state</para></listitem>
- </itemizedlist>
- </para>
- </formalpara>
+ <listitem>
+ <para>
+ Receive BEGIN &rarr; terminate auth conversation,
+ disconnect
+ </para>
+ </listitem>
- <formalpara>
- <title><emphasis>NeedSendRejected</emphasis></title>
- <para>
-
- <itemizedlist>
- <listitem><para>send REJECTED -&gt; <emphasis>WaitingForAuth</emphasis></para></listitem>
+ <listitem>
+ <para>
+ Receive CANCEL &rarr; send REJECTED [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive ERROR &rarr; send REJECTED [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive anything else &rarr; send ERROR, goto
+ <emphasis>WaitingForData</emphasis>
+ </para>
+ </listitem>
</itemizedlist>
</para>
</formalpara>
@@ -1211,18 +1423,35 @@
<formalpara>
<title><emphasis>WaitingForBegin</emphasis></title>
<para>
-
<itemizedlist>
- <listitem><para>receive BEGIN -&gt; <emphasis>Authorized</emphasis></para></listitem>
- <listitem><para>receive anything else -&gt; <emphasis>NeedSendError</emphasis></para></listitem>
- </itemizedlist>
- </para>
- </formalpara>
+ <listitem>
+ <para>
+ Receive BEGIN &rarr; 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 &rarr; send REJECTED [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive ERROR &rarr; send REJECTED [mechs], goto
+ <emphasis>WaitingForAuth</emphasis>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Receive anything else &rarr; send ERROR, goto
+ <emphasis>WaitingForBegin</emphasis>
+ </para>
+ </listitem>
+ </itemizedlist>
</para>
</formalpara>
diff --git a/test/data/auth/fail-after-n-attempts.auth-script b/test/data/auth/fail-after-n-attempts.auth-script
index a56f182b..0ced386b 100644
--- a/test/data/auth/fail-after-n-attempts.auth-script
+++ b/test/data/auth/fail-after-n-attempts.auth-script
@@ -30,4 +30,5 @@ EXPECT_STATE WAITING_FOR_INPUT
# 6
SEND 'AUTH EXTERNAL USERID_HEX'
+EXPECT_COMMAND REJECTED
EXPECT_STATE NEED_DISCONNECT