blob: 6285e3ba47ec2f65d85724285584e20b2a69ac19 [file] [log] [blame]
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dbus-auth-script.c Test DBusAuth using a special script file (internal to D-Bus implementation)
*
* Copyright (C) 2003 Red Hat, Inc.
*
* Licensed under the Academic Free License version 2.1
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <config.h>
#ifdef DBUS_BUILD_TESTS
#include "dbus-auth-script.h"
#include "dbus-auth.h"
#include "dbus-string.h"
#include "dbus-hash.h"
#include "dbus-credentials.h"
#include "dbus-internals.h"
/**
* @defgroup DBusAuthScript code for running unit test scripts for DBusAuth
* @ingroup DBusInternals
* @brief DBusAuth unit test scripting
*
* The code in here is used for unit testing, it loads
* up a script that tests DBusAuth.
*
* @{
*/
/* this is slightly different from the other append_quoted_string
* in dbus-message-builder.c
*/
static dbus_bool_t
append_quoted_string (DBusString *dest,
const DBusString *quoted)
{
dbus_bool_t in_quotes = FALSE;
dbus_bool_t in_backslash = FALSE;
int i;
i = 0;
while (i < _dbus_string_get_length (quoted))
{
unsigned char b;
b = _dbus_string_get_byte (quoted, i);
if (in_backslash)
{
unsigned char a;
if (b == 'r')
a = '\r';
else if (b == 'n')
a = '\n';
else if (b == '\\')
a = '\\';
else
{
_dbus_warn ("bad backslashed byte %c\n", b);
return FALSE;
}
if (!_dbus_string_append_byte (dest, a))
return FALSE;
in_backslash = FALSE;
}
else if (b == '\\')
{
in_backslash = TRUE;
}
else if (in_quotes)
{
if (b == '\'')
in_quotes = FALSE;
else
{
if (!_dbus_string_append_byte (dest, b))
return FALSE;
}
}
else
{
if (b == '\'')
in_quotes = TRUE;
else if (b == ' ' || b == '\n' || b == '\t')
break; /* end on whitespace if not quoted */
else
{
if (!_dbus_string_append_byte (dest, b))
return FALSE;
}
}
++i;
}
return TRUE;
}
static dbus_bool_t
same_first_word (const DBusString *a,
const DBusString *b)
{
int first_a_blank, first_b_blank;
_dbus_string_find_blank (a, 0, &first_a_blank);
_dbus_string_find_blank (b, 0, &first_b_blank);
if (first_a_blank != first_b_blank)
return FALSE;
return _dbus_string_equal_len (a, b, first_a_blank);
}
static DBusAuthState
auth_state_from_string (const DBusString *str)
{
if (_dbus_string_starts_with_c_str (str, "WAITING_FOR_INPUT"))
return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
else if (_dbus_string_starts_with_c_str (str, "WAITING_FOR_MEMORY"))
return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
else if (_dbus_string_starts_with_c_str (str, "HAVE_BYTES_TO_SEND"))
return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
else if (_dbus_string_starts_with_c_str (str, "NEED_DISCONNECT"))
return DBUS_AUTH_STATE_NEED_DISCONNECT;
else if (_dbus_string_starts_with_c_str (str, "AUTHENTICATED"))
return DBUS_AUTH_STATE_AUTHENTICATED;
else
return -1;
}
static const char*
auth_state_to_string (DBusAuthState state)
{
switch (state)
{
case DBUS_AUTH_STATE_WAITING_FOR_INPUT:
return "WAITING_FOR_INPUT";
case DBUS_AUTH_STATE_WAITING_FOR_MEMORY:
return "WAITING_FOR_MEMORY";
case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND:
return "HAVE_BYTES_TO_SEND";
case DBUS_AUTH_STATE_NEED_DISCONNECT:
return "NEED_DISCONNECT";
case DBUS_AUTH_STATE_AUTHENTICATED:
return "AUTHENTICATED";
}
return "unknown";
}
static char **
split_string (DBusString *str)
{
int i, j, k, count, end;
char **array;
end = _dbus_string_get_length (str);
i = 0;
_dbus_string_skip_blank (str, i, &i);
for (count = 0; i < end; count++)
{
_dbus_string_find_blank (str, i, &i);
_dbus_string_skip_blank (str, i, &i);
}
array = dbus_new0 (char *, count + 1);
if (array == NULL)
return NULL;
i = 0;
_dbus_string_skip_blank (str, i, &i);
for (k = 0; k < count; k++)
{
_dbus_string_find_blank (str, i, &j);
array[k] = dbus_malloc (j - i + 1);
if (array[k] == NULL)
{
dbus_free_string_array (array);
return NULL;
}
memcpy (array[k],
_dbus_string_get_const_data_len (str, i, j - i), j - i);
array[k][j - i] = '\0';
_dbus_string_skip_blank (str, j, &i);
}
array[k] = NULL;
return array;
}
static void
auth_set_unix_credentials(DBusAuth *auth,
dbus_uid_t uid,
dbus_pid_t pid)
{
DBusCredentials *credentials;
credentials = _dbus_credentials_new ();
if (credentials == NULL)
_dbus_assert_not_reached ("no memory");
if (uid != DBUS_UID_UNSET)
_dbus_credentials_add_unix_uid (credentials, uid);
if (pid != DBUS_PID_UNSET)
_dbus_credentials_add_unix_pid (credentials, pid);
_dbus_auth_set_credentials (auth, credentials);
_dbus_credentials_unref (credentials);
}
/**
* Runs an "auth script" which is a script for testing the
* authentication protocol. Scripts send and receive data, and then
* include assertions about the state of both ends of the connection
* after processing the data. A script succeeds if these assertions
* hold.
*
* @param filename the file containing the script to run
* @returns #TRUE if the script succeeds, #FALSE otherwise
*/
dbus_bool_t
_dbus_auth_script_run (const DBusString *filename)
{
DBusString file;
DBusError error = DBUS_ERROR_INIT;
DBusString line;
dbus_bool_t retval;
int line_no;
DBusAuth *auth;
DBusString from_auth;
DBusAuthState state;
DBusString context;
DBusString guid;
retval = FALSE;
auth = NULL;
_dbus_string_init_const (&guid, "5fa01f4202cd837709a3274ca0df9d00");
_dbus_string_init_const (&context, "org_freedesktop_test");
if (!_dbus_string_init (&file))
return FALSE;
if (!_dbus_string_init (&line))
{
_dbus_string_free (&file);
return FALSE;
}
if (!_dbus_string_init (&from_auth))
{
_dbus_string_free (&file);
_dbus_string_free (&line);
return FALSE;
}
if (!_dbus_file_get_contents (&file, filename, &error)) {
_dbus_warn ("Getting contents of %s failed: %s\n",
_dbus_string_get_const_data (filename), error.message);
dbus_error_free (&error);
goto out;
}
state = DBUS_AUTH_STATE_NEED_DISCONNECT;
line_no = 0;
next_iteration:
while (_dbus_string_pop_line (&file, &line))
{
line_no += 1;
/* _dbus_warn ("%s\n", _dbus_string_get_const_data (&line)); */
_dbus_string_delete_leading_blanks (&line);
if (auth != NULL)
{
while ((state = _dbus_auth_do_work (auth)) ==
DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND)
{
const DBusString *tmp;
if (_dbus_auth_get_bytes_to_send (auth, &tmp))
{
int count = _dbus_string_get_length (tmp);
if (_dbus_string_copy (tmp, 0, &from_auth,
_dbus_string_get_length (&from_auth)))
_dbus_auth_bytes_sent (auth, count);
}
}
}
if (_dbus_string_get_length (&line) == 0)
{
/* empty line */
goto next_iteration;
}
else if (_dbus_string_starts_with_c_str (&line,
"#"))
{
/* Ignore this comment */
goto next_iteration;
}
#ifdef DBUS_WIN
else if (_dbus_string_starts_with_c_str (&line,
"WIN_ONLY"))
{
/* Ignore this line */
goto next_iteration;
}
else if (_dbus_string_starts_with_c_str (&line,
"UNIX_ONLY"))
{
/* skip this file */
_dbus_warn ("skipping unix only auth script\n");
retval = TRUE;
goto out;
}
#endif
#ifdef DBUS_UNIX
else if (_dbus_string_starts_with_c_str (&line,
"UNIX_ONLY"))
{
/* Ignore this line */
goto next_iteration;
}
else if (_dbus_string_starts_with_c_str (&line,
"WIN_ONLY"))
{
/* skip this file */
_dbus_warn ("skipping windows only auth script\n");
retval = TRUE;
goto out;
}
#endif
else if (_dbus_string_starts_with_c_str (&line,
"CLIENT"))
{
DBusCredentials *creds;
if (auth != NULL)
{
_dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
goto out;
}
auth = _dbus_auth_client_new ();
if (auth == NULL)
{
_dbus_warn ("no memory to create DBusAuth\n");
goto out;
}
/* test ref/unref */
_dbus_auth_ref (auth);
_dbus_auth_unref (auth);
creds = _dbus_credentials_new_from_current_process ();
if (creds == NULL)
{
_dbus_warn ("no memory for credentials\n");
_dbus_auth_unref (auth);
auth = NULL;
goto out;
}
if (!_dbus_auth_set_credentials (auth, creds))
{
_dbus_warn ("no memory for setting credentials\n");
_dbus_auth_unref (auth);
auth = NULL;
_dbus_credentials_unref (creds);
goto out;
}
_dbus_credentials_unref (creds);
}
else if (_dbus_string_starts_with_c_str (&line,
"SERVER"))
{
DBusCredentials *creds;
if (auth != NULL)
{
_dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
goto out;
}
auth = _dbus_auth_server_new (&guid);
if (auth == NULL)
{
_dbus_warn ("no memory to create DBusAuth\n");
goto out;
}
/* test ref/unref */
_dbus_auth_ref (auth);
_dbus_auth_unref (auth);
creds = _dbus_credentials_new_from_current_process ();
if (creds == NULL)
{
_dbus_warn ("no memory for credentials\n");
_dbus_auth_unref (auth);
auth = NULL;
goto out;
}
if (!_dbus_auth_set_credentials (auth, creds))
{
_dbus_warn ("no memory for setting credentials\n");
_dbus_auth_unref (auth);
auth = NULL;
_dbus_credentials_unref (creds);
goto out;
}
_dbus_credentials_unref (creds);
_dbus_auth_set_context (auth, &context);
}
else if (auth == NULL)
{
_dbus_warn ("must specify CLIENT or SERVER\n");
goto out;
}
else if (_dbus_string_starts_with_c_str (&line,
"NO_CREDENTIALS"))
{
auth_set_unix_credentials (auth, DBUS_UID_UNSET, DBUS_PID_UNSET);
}
else if (_dbus_string_starts_with_c_str (&line,
"ROOT_CREDENTIALS"))
{
auth_set_unix_credentials (auth, 0, DBUS_PID_UNSET);
}
else if (_dbus_string_starts_with_c_str (&line,
"SILLY_CREDENTIALS"))
{
auth_set_unix_credentials (auth, 4312, DBUS_PID_UNSET);
}
else if (_dbus_string_starts_with_c_str (&line,
"ALLOWED_MECHS"))
{
char **mechs;
_dbus_string_delete_first_word (&line);
mechs = split_string (&line);
_dbus_auth_set_mechanisms (auth, (const char **) mechs);
dbus_free_string_array (mechs);
}
else if (_dbus_string_starts_with_c_str (&line,
"SEND"))
{
DBusString to_send;
_dbus_string_delete_first_word (&line);
if (!_dbus_string_init (&to_send))
{
_dbus_warn ("no memory to allocate string\n");
goto out;
}
if (!append_quoted_string (&to_send, &line))
{
_dbus_warn ("failed to append quoted string line %d\n",
line_no);
_dbus_string_free (&to_send);
goto out;
}
_dbus_verbose ("Sending '%s'\n", _dbus_string_get_const_data (&to_send));
if (!_dbus_string_append (&to_send, "\r\n"))
{
_dbus_warn ("failed to append \r\n from line %d\n",
line_no);
_dbus_string_free (&to_send);
goto out;
}
/* Replace USERID_HEX with our username in hex */
{
int where;
if (_dbus_string_find (&to_send, 0,
"USERID_HEX", &where))
{
DBusString username;
if (!_dbus_string_init (&username))
{
_dbus_warn ("no memory for userid\n");
_dbus_string_free (&to_send);
goto out;
}
if (!_dbus_append_user_from_current_process (&username))
{
_dbus_warn ("no memory for userid\n");
_dbus_string_free (&username);
_dbus_string_free (&to_send);
goto out;
}
_dbus_string_delete (&to_send, where, strlen ("USERID_HEX"));
if (!_dbus_string_hex_encode (&username, 0,
&to_send, where))
{
_dbus_warn ("no memory to subst USERID_HEX\n");
_dbus_string_free (&username);
_dbus_string_free (&to_send);
goto out;
}
_dbus_string_free (&username);
}
else if (_dbus_string_find (&to_send, 0,
"USERNAME_HEX", &where))
{
DBusString username;
if (!_dbus_string_init (&username))
{
_dbus_warn ("no memory for username\n");
_dbus_string_free (&to_send);
goto out;
}
if (!_dbus_append_user_from_current_process (&username))
{
_dbus_warn ("no memory for username\n");
_dbus_string_free (&username);
_dbus_string_free (&to_send);
goto out;
}
_dbus_string_delete (&to_send, where, strlen ("USERNAME_HEX"));
if (!_dbus_string_hex_encode (&username, 0,
&to_send, where))
{
_dbus_warn ("no memory to subst USERNAME_HEX\n");
_dbus_string_free (&username);
_dbus_string_free (&to_send);
goto out;
}
_dbus_string_free (&username);
}
}
{
DBusString *buffer;
_dbus_auth_get_buffer (auth, &buffer);
if (!_dbus_string_copy (&to_send, 0,
buffer, _dbus_string_get_length (buffer)))
{
_dbus_warn ("not enough memory to call bytes_received, or can't add bytes to auth object already in end state\n");
_dbus_string_free (&to_send);
_dbus_auth_return_buffer (auth, buffer, 0);
goto out;
}
_dbus_auth_return_buffer (auth, buffer, _dbus_string_get_length (&to_send));
}
_dbus_string_free (&to_send);
}
else if (_dbus_string_starts_with_c_str (&line,
"EXPECT_STATE"))
{
DBusAuthState expected;
_dbus_string_delete_first_word (&line);
expected = auth_state_from_string (&line);
if (expected < 0)
{
_dbus_warn ("bad auth state given to EXPECT_STATE\n");
goto parse_failed;
}
if (expected != state)
{
_dbus_warn ("expected auth state %s but got %s on line %d\n",
auth_state_to_string (expected),
auth_state_to_string (state),
line_no);
goto out;
}
}
else if (_dbus_string_starts_with_c_str (&line,
"EXPECT_COMMAND"))
{
DBusString received;
_dbus_string_delete_first_word (&line);
if (!_dbus_string_init (&received))
{
_dbus_warn ("no mem to allocate string received\n");
goto out;
}
if (!_dbus_string_pop_line (&from_auth, &received))
{
_dbus_warn ("no line popped from the DBusAuth being tested, expected command %s on line %d\n",
_dbus_string_get_const_data (&line), line_no);
_dbus_string_free (&received);
goto out;
}
if (!same_first_word (&received, &line))
{
_dbus_warn ("line %d expected command '%s' and got '%s'\n",
line_no,
_dbus_string_get_const_data (&line),
_dbus_string_get_const_data (&received));
_dbus_string_free (&received);
goto out;
}
_dbus_string_free (&received);
}
else if (_dbus_string_starts_with_c_str (&line,
"EXPECT_UNUSED"))
{
DBusString expected;
const DBusString *unused;
_dbus_string_delete_first_word (&line);
if (!_dbus_string_init (&expected))
{
_dbus_warn ("no mem to allocate string expected\n");
goto out;
}
if (!append_quoted_string (&expected, &line))
{
_dbus_warn ("failed to append quoted string line %d\n",
line_no);
_dbus_string_free (&expected);
goto out;
}
_dbus_auth_get_unused_bytes (auth, &unused);
if (_dbus_string_equal (&expected, unused))
{
_dbus_auth_delete_unused_bytes (auth);
_dbus_string_free (&expected);
}
else
{
_dbus_warn ("Expected unused bytes '%s' and have '%s'\n",
_dbus_string_get_const_data (&expected),
_dbus_string_get_const_data (unused));
_dbus_string_free (&expected);
goto out;
}
}
else if (_dbus_string_starts_with_c_str (&line,
"EXPECT_HAVE_NO_CREDENTIALS"))
{
DBusCredentials *authorized_identity;
authorized_identity = _dbus_auth_get_identity (auth);
if (!_dbus_credentials_are_anonymous (authorized_identity))
{
_dbus_warn ("Expected anonymous login or failed login, but some credentials were authorized\n");
goto out;
}
}
else if (_dbus_string_starts_with_c_str (&line,
"EXPECT_HAVE_SOME_CREDENTIALS"))
{
DBusCredentials *authorized_identity;
authorized_identity = _dbus_auth_get_identity (auth);
if (_dbus_credentials_are_anonymous (authorized_identity))
{
_dbus_warn ("Expected to have some credentials, but we don't\n");
goto out;
}
}
else if (_dbus_string_starts_with_c_str (&line,
"EXPECT"))
{
DBusString expected;
_dbus_string_delete_first_word (&line);
if (!_dbus_string_init (&expected))
{
_dbus_warn ("no mem to allocate string expected\n");
goto out;
}
if (!append_quoted_string (&expected, &line))
{
_dbus_warn ("failed to append quoted string line %d\n",
line_no);
_dbus_string_free (&expected);
goto out;
}
if (_dbus_string_equal_len (&expected, &from_auth,
_dbus_string_get_length (&expected)))
{
_dbus_string_delete (&from_auth, 0,
_dbus_string_get_length (&expected));
_dbus_string_free (&expected);
}
else
{
_dbus_warn ("Expected exact string '%s' and have '%s'\n",
_dbus_string_get_const_data (&expected),
_dbus_string_get_const_data (&from_auth));
_dbus_string_free (&expected);
goto out;
}
}
else
goto parse_failed;
goto next_iteration; /* skip parse_failed */
parse_failed:
{
_dbus_warn ("couldn't process line %d \"%s\"\n",
line_no, _dbus_string_get_const_data (&line));
goto out;
}
}
if (auth == NULL)
{
_dbus_warn ("Auth script is bogus, did not even have CLIENT or SERVER\n");
goto out;
}
else if (state == DBUS_AUTH_STATE_AUTHENTICATED)
{
const DBusString *unused;
_dbus_auth_get_unused_bytes (auth, &unused);
if (_dbus_string_get_length (unused) > 0)
{
_dbus_warn ("did not expect unused bytes (scripts must specify explicitly if they are expected)\n");
goto out;
}
}
if (_dbus_string_get_length (&from_auth) > 0)
{
_dbus_warn ("script did not have EXPECT_ statements for all the data received from the DBusAuth\n");
_dbus_warn ("Leftover data: %s\n", _dbus_string_get_const_data (&from_auth));
goto out;
}
retval = TRUE;
out:
if (auth)
_dbus_auth_unref (auth);
_dbus_string_free (&file);
_dbus_string_free (&line);
_dbus_string_free (&from_auth);
return retval;
}
/** @} */
#endif /* DBUS_BUILD_TESTS */