Commit 7e050c88 authored by Havoc Pennington's avatar Havoc Pennington

2003-03-04 Havoc Pennington <hp@pobox.com>

	* test/data/auth/*: adapt to changes

	* dbus/dbus-auth-script.c (_dbus_auth_script_run): add
	USERID_BASE64 and change USERNAME_BASE64 to put in username not
	userid

	* dbus/dbus-keyring.c (_dbus_keyring_validate_context): prevent
	more stuff from being in a context name, to make the protocol
	simpler to deal with

	* dbus/dbus-errors.c (dbus_error_has_name): new function
	(dbus_error_is_set): new function

	* dbus/dbus-auth.c: replace DBUS_STUPID_TEST_MECH auth
	with DBUS_COOKIE_SHA1, implement DBUS_COOKIE_SHA1

	* dbus/dbus-connection.c (dbus_connection_flush): also read
	messages during a flush operation

	* dbus/Makefile.am: remove dbus-md5 since it isn't currently used.
parent 6bea42d2
2003-03-04 Havoc Pennington <hp@pobox.com>
* test/data/auth/*: adapt to changes
* dbus/dbus-auth-script.c (_dbus_auth_script_run): add
USERID_BASE64 and change USERNAME_BASE64 to put in username not
userid
* dbus/dbus-keyring.c (_dbus_keyring_validate_context): prevent
more stuff from being in a context name, to make the protocol
simpler to deal with
* dbus/dbus-errors.c (dbus_error_has_name): new function
(dbus_error_is_set): new function
* dbus/dbus-auth.c: replace DBUS_STUPID_TEST_MECH auth
with DBUS_COOKIE_SHA1, implement DBUS_COOKIE_SHA1
* dbus/dbus-connection.c (dbus_connection_flush): also read
messages during a flush operation
* dbus/Makefile.am: remove dbus-md5 since it isn't currently used.
2003-03-05 Anders Carlsson <andersca@codefactory.se>
* configure.in: Check for gethostbyname on Solaris.
......
......@@ -32,8 +32,6 @@ libdbus_1_la_SOURCES= \
dbus-errors.c \
dbus-keyring.c \
dbus-keyring.h \
dbus-md5.c \
dbus-md5.h \
dbus-memory.c \
dbus-message.c \
dbus-message-handler.c \
......@@ -63,6 +61,9 @@ libdbus_1_la_SOURCES= \
dbus-watch.c \
dbus-watch.h
## dbus-md5.c \
## dbus-md5.h \
## this library is linked into both libdbus and the bus
## itself, but does not export any symbols from libdbus.
......
......@@ -191,9 +191,12 @@ _dbus_auth_script_run (const DBusString *filename)
DBusAuth *auth;
DBusString from_auth;
DBusAuthState state;
DBusString context;
retval = FALSE;
auth = NULL;
_dbus_string_init_const (&context, "org_freedesktop_test");
if (!_dbus_string_init (&file, _DBUS_INT_MAX))
return FALSE;
......@@ -299,6 +302,7 @@ _dbus_auth_script_run (const DBusString *filename)
_dbus_credentials_from_current_process (&creds);
_dbus_auth_set_credentials (auth, &creds);
_dbus_auth_set_context (auth, &context);
}
else if (auth == NULL)
{
......@@ -359,23 +363,59 @@ _dbus_auth_script_run (const DBusString *filename)
goto out;
}
/* Replace USERNAME_BASE64 with our username in base64 */
/* Replace USERID_BASE64 with our username in base64 */
{
int where;
if (_dbus_string_find (&to_send, 0,
"USERNAME_BASE64", &where))
"USERID_BASE64", &where))
{
DBusString username;
if (!_dbus_string_init (&username, _DBUS_INT_MAX))
{
_dbus_warn ("no memory for username\n");
_dbus_warn ("no memory for userid\n");
_dbus_string_free (&to_send);
goto out;
}
if (!_dbus_string_append_our_uid (&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_BASE64"));
if (!_dbus_string_base64_encode (&username, 0,
&to_send, where))
{
_dbus_warn ("no memory to subst USERID_BASE64\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_BASE64", &where))
{
DBusString username;
const DBusString *u;
if (!_dbus_string_init (&username, _DBUS_INT_MAX))
{
_dbus_warn ("no memory for username\n");
_dbus_string_free (&to_send);
goto out;
}
if (!_dbus_user_info_from_current_process (&u, NULL, NULL) ||
!_dbus_string_copy (u, 0, &username,
_dbus_string_get_length (&username)))
{
_dbus_warn ("no memory for username\n");
_dbus_string_free (&username);
......
This diff is collapsed.
......@@ -68,6 +68,8 @@ void _dbus_auth_set_credentials (DBusAuth *auth,
void _dbus_auth_get_identity (DBusAuth *auth,
DBusCredentials *credentials);
dbus_bool_t _dbus_auth_set_context (DBusAuth *auth,
const DBusString *context);
DBUS_END_DECLS;
......
......@@ -1367,9 +1367,16 @@ dbus_connection_send_message_with_reply_and_block (DBusConnection *connectio
void
dbus_connection_flush (DBusConnection *connection)
{
/* We have to specify DBUS_ITERATION_DO_READING here
* because otherwise we could have two apps deadlock
* if they are both doing a flush(), and the kernel
* buffers fill up.
*/
dbus_mutex_lock (connection->mutex);
while (connection->n_outgoing > 0)
_dbus_connection_do_iteration (connection,
DBUS_ITERATION_DO_READING |
DBUS_ITERATION_DO_WRITING |
DBUS_ITERATION_BLOCK,
-1);
......
......@@ -200,6 +200,8 @@ dbus_set_error_const (DBusError *error,
/* it's a bug to pile up errors */
_dbus_assert (error->name == NULL);
_dbus_assert (error->message == NULL);
_dbus_assert (name != NULL);
_dbus_assert (message != NULL);
real = (DBusRealError *)error;
......@@ -208,6 +210,48 @@ dbus_set_error_const (DBusError *error,
real->const_message = TRUE;
}
/**
* Checks whether the error is set and has the given
* name.
* @param error the error
* @param name the name
* @returns #TRUE if the given named error occurred
*/
dbus_bool_t
dbus_error_has_name (const DBusError *error,
const char *name)
{
_dbus_assert (error != NULL);
_dbus_assert (name != NULL);
_dbus_assert ((error->name != NULL && error->message != NULL) ||
(error->name == NULL && error->message == NULL));
if (error->name != NULL)
{
DBusString str1, str2;
_dbus_string_init_const (&str1, error->name);
_dbus_string_init_const (&str2, name);
return _dbus_string_equal (&str1, &str2);
}
else
return FALSE;
}
/**
* Checks whether an error occurred (the error is set).
*
* @param error the error object
* @returns #TRUE if an error occurred
*/
dbus_bool_t
dbus_error_is_set (const DBusError *error)
{
_dbus_assert (error != NULL);
_dbus_assert ((error->name != NULL && error->message != NULL) ||
(error->name == NULL && error->message == NULL));
return error->name != NULL;
}
/**
* Assigns an error name and message to a DBusError.
* Does nothing if error is #NULL.
......@@ -240,6 +284,8 @@ dbus_set_error (DBusError *error,
/* it's a bug to pile up errors */
_dbus_assert (error->name == NULL);
_dbus_assert (error->message == NULL);
_dbus_assert (name != NULL);
_dbus_assert (format != NULL);
va_start (args, format);
/* Measure the message length */
......
......@@ -81,16 +81,20 @@ typedef enum
DBUS_RESULT_FILE_NOT_FOUND /**< File doesn't exist */
} DBusResultCode;
void dbus_error_init (DBusError *error);
void dbus_error_free (DBusError *error);
void dbus_set_error (DBusError *error,
const char *name,
const char *message,
...);
void dbus_set_error_const (DBusError *error,
const char *name,
const char *message);
void dbus_error_init (DBusError *error);
void dbus_error_free (DBusError *error);
void dbus_set_error (DBusError *error,
const char *name,
const char *message,
...);
void dbus_set_error_const (DBusError *error,
const char *name,
const char *message);
dbus_bool_t dbus_error_has_name (const DBusError *error,
const char *name);
dbus_bool_t dbus_error_is_set (const DBusError *error);
void dbus_set_result (DBusResultCode *code_address,
DBusResultCode code);
const char* dbus_result_to_string (DBusResultCode code);
......
......@@ -92,6 +92,7 @@ typedef struct
struct DBusKeyring
{
int refcount; /**< Reference count */
DBusString username; /**< Username keyring is for */
DBusString directory; /**< Directory the below two items are inside */
DBusString filename; /**< Keyring filename */
DBusString filename_lock; /**< Name of lockfile */
......@@ -117,12 +118,17 @@ _dbus_keyring_new (void)
if (!_dbus_string_init (&keyring->filename_lock, _DBUS_INT_MAX))
goto out_3;
if (!_dbus_string_init (&keyring->username, _DBUS_INT_MAX))
goto out_4;
keyring->refcount = 1;
keyring->keys = NULL;
keyring->n_keys = 0;
return keyring;
out_4:
_dbus_string_free (&keyring->username);
out_3:
_dbus_string_free (&keyring->filename);
out_2:
......@@ -614,6 +620,7 @@ _dbus_keyring_reload (DBusKeyring *keyring,
i = 0;
while (i < n_keys)
{
_dbus_string_zero (&keys[i].secret);
_dbus_string_free (&keys[i].secret);
++i;
}
......@@ -659,6 +666,7 @@ _dbus_keyring_unref (DBusKeyring *keyring)
if (keyring->refcount == 0)
{
_dbus_string_free (&keyring->username);
_dbus_string_free (&keyring->filename);
_dbus_string_free (&keyring->filename_lock);
_dbus_string_free (&keyring->directory);
......@@ -715,6 +723,8 @@ _dbus_keyring_new_homedir (const DBusString *username,
goto failed;
}
_dbus_assert (username != NULL);
keyring = _dbus_keyring_new ();
if (keyring == NULL)
goto failed;
......@@ -728,7 +738,11 @@ _dbus_keyring_new_homedir (const DBusString *username,
"Invalid context in keyring creation");
goto failed;
}
if (!_dbus_string_copy (username, 0,
&keyring->username, 0))
goto failed;
if (!_dbus_string_copy (&homedir, 0,
&keyring->directory, 0))
goto failed;
......@@ -795,6 +809,9 @@ _dbus_keyring_new_homedir (const DBusString *username,
* in filenames are not allowed (contexts can't
* start with a dot or contain dir separators).
*
* @todo this is the most inefficient implementation
* imaginable.
*
* @param context the context
* @returns #TRUE if valid
*/
......@@ -836,6 +853,25 @@ _dbus_keyring_validate_context (const DBusString *context)
return FALSE;
}
/* no spaces/tabs, those are used for separators in the protocol */
if (_dbus_string_find_blank (context, 0, NULL))
{
_dbus_verbose ("context contains a blank\n");
return FALSE;
}
if (_dbus_string_find (context, 0, "\n", NULL))
{
_dbus_verbose ("context contains a newline\n");
return FALSE;
}
if (_dbus_string_find (context, 0, "\r", NULL))
{
_dbus_verbose ("context contains a carriage return\n");
return FALSE;
}
return TRUE;
}
......@@ -904,6 +940,51 @@ _dbus_keyring_get_best_key (DBusKeyring *keyring,
}
}
/**
* Checks whether the keyring is for the given username.
*
* @param keyring the keyring
* @param username the username to check
*
* @returns #TRUE if the keyring belongs to the given user
*/
dbus_bool_t
_dbus_keyring_is_for_user (DBusKeyring *keyring,
const DBusString *username)
{
return _dbus_string_equal (&keyring->username,
username);
}
/**
* Gets the hex-encoded secret key for the given ID.
* Returns #FALSE if not enough memory. Returns #TRUE
* but empty key on any other error such as unknown
* key ID.
*
* @param keyring the keyring
* @param key_id the key ID
* @param hex_key string to append hex-encoded key to
* @returns #TRUE if we had enough memory
*/
dbus_bool_t
_dbus_keyring_get_hex_key (DBusKeyring *keyring,
int key_id,
DBusString *hex_key)
{
DBusKey *key;
key = find_key_by_id (keyring->keys,
keyring->n_keys,
key_id);
if (key == NULL)
return TRUE; /* had enough memory, so TRUE */
return _dbus_string_hex_encode (&key->secret, 0,
hex_key,
_dbus_string_get_length (hex_key));
}
/** @} */ /* end of exposed API */
#ifdef DBUS_BUILD_TESTS
......@@ -946,6 +1027,8 @@ _dbus_keyring_test (void)
_dbus_assert (!_dbus_keyring_validate_context (&context));
_dbus_string_init_const (&context, "foo\x7f");
_dbus_assert (_dbus_keyring_validate_context (&context));
_dbus_string_init_const (&context, "foo bar");
_dbus_assert (!_dbus_keyring_validate_context (&context));
if (!_dbus_string_init (&context, _DBUS_INT_MAX))
_dbus_assert_not_reached ("no memory");
......
......@@ -39,16 +39,11 @@ void _dbus_keyring_unref (DBusKeyring *keyring);
dbus_bool_t _dbus_keyring_validate_context (const DBusString *context);
int _dbus_keyring_get_best_key (DBusKeyring *keyring,
DBusError *error);
dbus_bool_t _dbus_keyring_create_challenge (DBusString *challenge);
dbus_bool_t _dbus_keyring_compute_response (DBusKeyring *keyring,
dbus_bool_t _dbus_keyring_is_for_user (DBusKeyring *keyring,
const DBusString *username);
dbus_bool_t _dbus_keyring_get_hex_key (DBusKeyring *keyring,
int key_id,
const DBusString *challenge,
DBusString *response);
dbus_bool_t _dbus_keyring_check_response (DBusKeyring *keyring,
int key_id,
const DBusString *challenge,
const DBusString *response);
DBusString *hex_key);
DBUS_END_DECLS;
......
......@@ -148,7 +148,7 @@ clear_header_padding (DBusMessage *message)
_dbus_string_shorten (&message->header,
message->header_padding);
message->header_padding = 0;
}
}
static dbus_bool_t
append_header_padding (DBusMessage *message)
......@@ -2653,7 +2653,9 @@ _dbus_message_loader_return_buffer (DBusMessageLoader *loader,
int i;
int next_arg;
#if 0
_dbus_verbose_bytes_of_string (&loader->data, 0, header_len);
#endif
if (!decode_header_data (&loader->data, header_len, byte_order,
fields, &header_padding))
{
......
......@@ -1107,14 +1107,17 @@ get_user_info (const DBusString *username,
DBusString *username_out)
{
const char *username_c_str;
credentials->pid = -1;
credentials->uid = -1;
credentials->gid = -1;
/* exactly one of username/uid provided */
_dbus_assert (username != NULL || uid >= 0);
_dbus_assert (username == NULL || uid < 0);
if (credentials)
{
credentials->pid = -1;
credentials->uid = -1;
credentials->gid = -1;
}
if (username != NULL)
_dbus_string_get_const_data (username, &username_c_str);
......
......@@ -66,11 +66,13 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir)
printf ("%s: running keyring tests\n", "dbus-test");
if (!_dbus_keyring_test ())
die ("keyring");
#if 0
printf ("%s: running md5 tests\n", "dbus-test");
if (!_dbus_md5_test ())
die ("md5");
#endif
printf ("%s: running SHA-1 tests\n", "dbus-test");
if (!_dbus_sha_test (test_data_dir))
die ("SHA-1");
......
......@@ -476,7 +476,6 @@
<para>
The credentials sent along with the nul byte may be used with the
SASL mechanism EXTERNAL.
</para>
</sect2>
<sect2 id="auth-command-auth">
......@@ -683,6 +682,150 @@
</figure>
</para>
</sect2>
<sect2 id="auth-mechanisms">
<title>Authentication mechanisms</title>
<para>
This section describes some new authentication mechanisms.
D-BUS also allows any standard SASL mechanism of course.
</para>
<sect3 id="auth-mechanisms-sha">
<title>DBUS_COOKIE_SHA1</title>
<para>
The DBUS_COOKIE_SHA1 mechanism is designed to establish that a client
has the ability to read a private file owned by the user being
authenticated. If the client can prove that it has access to a secret
cookie stored in this file, then the client is authenticated.
Thus the security of DBUS_COOKIE_SHA1 depends on a secure home
directory.
</para>
<para>
Authentication proceeds as follows:
<itemizedlist>
<listitem>
<para>
The client sends the username it would like to authenticate
as.
</para>
</listitem>
<listitem>
<para>
The server sends the name of its "cookie context" (see below); a
space character; the integer ID of the secret cookie the client
must demonstrate knowledge of; a space character; then a
hex-encoded randomly-generated challenge string.
</para>
</listitem>
<listitem>
<para>
The client locates the cookie, and generates its own hex-encoded
randomly-generated challenge string. The client then
concatentates the server's hex-encoded challenge, a ":"
character, its own hex-encoded challenge, another ":" character,
and the hex-encoded cookie. It computes the SHA-1 hash of this
composite string. It sends back to the server the client's
hex-encoded challenge string, a space character, and the SHA-1
hash.
</para>
</listitem>
<listitem>
<para>
The server generates the same concatenated string used by the
client and computes its SHA-1 hash. It compares the hash with
the hash received from the client; if the two hashes match, the
client is authenticated.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Each server has a "cookie context," which is a name that identifies a
set of cookies that apply to that server. A sample context might be
"org_freedesktop_session_bus". Context names must be valid ASCII,
nonzero length, and may not contain the characters slash ("/"),
backslash ("\"), space (" "), newline ("\n"), carriage return ("\r"),
tab ("\t"), or period ("."). There is a default context,
"org_freedesktop_global" that's used by servers that do not specify
otherwise.
</para>
<para>
Cookies are stored in a user's home directory, in the directory
<filename>~/.dbus-keyrings/</filename>. This directory must
not be readable or writable by other users. If it is,
clients and servers must ignore it. The directory
contains cookie files named after the cookie context.
</para>
<para>
A cookie file contains one cookie per line. Each line
has three space-separated fields:
<itemizedlist>
<listitem>
<para>
The cookie ID number, which must be a non-negative integer and
may not be used twice in the same file.
</para>
</listitem>
<listitem>
<para>
The cookie's creation time, in UNIX seconds-since-the-epoch
format.
</para>
</listitem>
<listitem>
<para>
The cookie itself, a hex-encoded random block of bytes.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Only server processes modify the cookie file.
They must do so with this procedure:
<itemizedlist>
<listitem>
<para>
Create a lockfile name by appending ".lock" to the name of the
cookie file. The server should attempt to create this file
using <literal>O_CREAT | O_EXCL</literal>. If file creation
fails, the lock fails. Servers should retry for a reasonable
period of time, then they may choose to delete an existing lock
to keep users from having to manually delete a stale
lock. <footnote><para>Lockfiles are used instead of real file
locking <literal>fcntl()</literal> because real locking
implementations are still flaky on network
filesystems.</para></footnote>
</para>
</listitem>
<listitem>
<para>
Once the lockfile has been created, the server loads the cookie
file. It should then delete any cookies that are old (the
timeout can be fairly short), or more than a reasonable
time in the future (so that cookies never accidentally
become permanent, if the clock was set far into the future
at some point). If no recent keys remain, the
server may generate a new key.
</para>
</listitem>
<listitem>
<para>
The pruned and possibly added-to cookie file
must be resaved atomically (using a temporary
file which is rename()'d).
</para>
</listitem>
<listitem>
<para>
The lock must be dropped by deleting the lockfile.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Clients need not lock the file in order to load it,
because servers are required to save the file atomically.
</para>
</sect3>
</sect2>
</sect1>
<sect1 id="addresses">
<title>Server Addresses</title>
......
......@@ -2,7 +2,7 @@
SERVER
NO_CREDENTIALS
SEND 'AUTH EXTERNAL USERNAME_BASE64'