2003-02-23 Havoc Pennington <hp@pobox.com>

* dbus/dbus-keyring.c: finish most of this implementation and
	simple unit test

	* dbus/dbus-errors.c (dbus_set_error_const, dbus_set_error): make
	these barf if the error isn't cleared to NULL

	* dbus/dbus-sysdeps.c (_dbus_delete_file): set error on failure
	(_dbus_create_directory): new function

	* dbus/dbus-errors.c (dbus_set_error): fix warning

	* dbus/dbus-string.c (_dbus_string_hex_encode): new function
	(_dbus_string_hex_decode): new function
	(test_hex_roundtrip): test code

	* dbus/dbus-sha.c (_dbus_sha_compute): use dbus_string_hex_encode

	* dbus/dbus-md5.c (_dbus_md5_compute): use dbus_string_hex_encode

	* dbus/dbus-sysdeps.c (_dbus_string_save_to_file): make this use
	the save-to-temp/rename trick to atomically write the new file
	(_dbus_string_parse_uint): new function
This commit is contained in:
Havoc Pennington 2003-02-24 02:24:13 +00:00
parent 32b4b2a2f6
commit 2f38c95921
14 changed files with 1124 additions and 146 deletions

View file

@ -1,3 +1,28 @@
2003-02-23 Havoc Pennington <hp@pobox.com>
* dbus/dbus-keyring.c: finish most of this implementation and
simple unit test
* dbus/dbus-errors.c (dbus_set_error_const, dbus_set_error): make
these barf if the error isn't cleared to NULL
* dbus/dbus-sysdeps.c (_dbus_delete_file): set error on failure
(_dbus_create_directory): new function
* dbus/dbus-errors.c (dbus_set_error): fix warning
* dbus/dbus-string.c (_dbus_string_hex_encode): new function
(_dbus_string_hex_decode): new function
(test_hex_roundtrip): test code
* dbus/dbus-sha.c (_dbus_sha_compute): use dbus_string_hex_encode
* dbus/dbus-md5.c (_dbus_md5_compute): use dbus_string_hex_encode
* dbus/dbus-sysdeps.c (_dbus_string_save_to_file): make this use
the save-to-temp/rename trick to atomically write the new file
(_dbus_string_parse_uint): new function
2003-02-22 Havoc Pennington <hp@pobox.com>
* test/Makefile.am (dist-hook): fix dist for test/data/sha-1

View file

@ -30,6 +30,8 @@ libdbus_1_la_SOURCES= \
dbus-connection.c \
dbus-connection-internal.h \
dbus-errors.c \
dbus-keyring.c \
dbus-keyring.h \
dbus-md5.c \
dbus-md5.h \
dbus-memory.c \

View file

@ -196,6 +196,10 @@ dbus_set_error_const (DBusError *error,
if (error == NULL)
return;
/* it's a bug to pile up errors */
_dbus_assert (error->name == NULL);
_dbus_assert (error->message == NULL);
real = (DBusRealError *)error;
@ -225,13 +229,17 @@ dbus_set_error (DBusError *error,
...)
{
DBusRealError *real;
va_list args, args2;
va_list args;
int message_length;
char *message;
char c;
if (error == NULL)
return;
/* it's a bug to pile up errors */
_dbus_assert (error->name == NULL);
_dbus_assert (error->message == NULL);
va_start (args, format);
/* Measure the message length */
@ -248,7 +256,7 @@ dbus_set_error (DBusError *error,
}
va_start (args, format);
vsprintf (message, format, args2);
vsprintf (message, format, args);
va_end (args);
real = (DBusRealError *)error;

View file

@ -180,19 +180,6 @@ _dbus_verbose_real (const char *format,
va_end (args);
}
/**
* A wrapper around strerror() because some platforms
* may be lame and not have strerror().
*
* @param error_number errno.
* @returns error description.
*/
const char*
_dbus_strerror (int error_number)
{
return strerror (error_number);
}
/**
* Converts a UNIX errno into a DBusResultCode.
*

View file

@ -58,19 +58,26 @@
* clocks can change or be wrong, but we make a best effort to only
* use keys for a short time.
*/
#define NEW_KEY_TIMEOUT (60*5)
#define NEW_KEY_TIMEOUT_SECONDS (60*5)
/**
* The time after which we drop a key from the secrets file
* The time after which we drop a key from the secrets file.
* The EXPIRE_KEYS_TIMEOUT_SECONDS - NEW_KEY_TIMEOUT_SECONDS is the minimum
* time window a client has to complete authentication.
*/
#define EXPIRE_KEYS_TIMEOUT (NEW_KEY_TIMEOUT + (60*2))
#define EXPIRE_KEYS_TIMEOUT_SECONDS (NEW_KEY_TIMEOUT_SECONDS + (60*2))
/**
* The maximum amount of time a key can be in the future.
*/
#define MAX_TIME_TRAVEL_SECONDS (60*5)
typedef struct
{
dbus_int32_t id; /**< identifier used to refer to the key */
unsigned long creation_time; /**< when the key was generated,
* as unix timestamp
*/
long creation_time; /**< when the key was generated,
* as unix timestamp. signed long
* matches struct timeval.
*/
DBusString secret; /**< the actual key */
@ -101,13 +108,13 @@ _dbus_keyring_new (void)
if (keyring == NULL)
goto out_0;
if (!_dbus_string_init (&keyring->directory))
if (!_dbus_string_init (&keyring->directory, _DBUS_INT_MAX))
goto out_1;
if (!_dbus_string_init (&keyring->filename))
if (!_dbus_string_init (&keyring->filename, _DBUS_INT_MAX))
goto out_2;
if (!_dbus_string_init (&keyring->filename_lock))
if (!_dbus_string_init (&keyring->filename_lock, _DBUS_INT_MAX))
goto out_3;
keyring->refcount = 1;
@ -161,9 +168,9 @@ free_keys (DBusKey *keys,
*/
/** Maximum number of timeouts waiting for lock before we decide it's stale */
#define MAX_LOCK_TIMEOUTS 6
#define MAX_LOCK_TIMEOUTS 16
/** Length of each timeout while waiting for a lock */
#define LOCK_TIMEOUT 500
#define LOCK_TIMEOUT_MILLISECONDS 250
static dbus_bool_t
_dbus_keyring_lock (DBusKeyring *keyring)
@ -180,25 +187,38 @@ _dbus_keyring_lock (DBusKeyring *keyring)
&error))
break;
_dbus_verbose ("Did not get lock file: %s\n",
error.message);
_dbus_verbose ("Did not get lock file, sleeping %d milliseconds (%s)\n",
LOCK_TIMEOUT_MILLISECONDS, error.message);
dbus_error_free (&error);
_dbus_sleep_milliseconds (LOCK_TIMEOUT);
_dbus_sleep_milliseconds (LOCK_TIMEOUT_MILLISECONDS);
++n_timeouts;
}
if (n_timeouts == MAX_LOCK_TIMEOUTS)
{
_dbus_verbose ("Lock file timed out, assuming stale\n");
DBusError error;
_dbus_verbose ("Lock file timed out %d times, assuming stale\n",
n_timeouts);
_dbus_delete_file (&keyring->filename_lock);
dbus_error_init (&error);
if (!_dbus_delete_file (&keyring->filename_lock, &error))
{
_dbus_verbose ("Couldn't delete old lock file: %s\n",
error.message);
dbus_error_free (&error);
return FALSE;
}
if (!_dbus_create_file_exclusively (&keyring->filename_lock,
NULL))
&error))
{
_dbus_verbose ("Couldn't create lock file after trying to delete the stale one, giving up\n");
_dbus_verbose ("Couldn't create lock file after deleting stale one: %s\n",
error.message);
dbus_error_free (&error);
return FALSE;
}
}
@ -209,17 +229,153 @@ _dbus_keyring_lock (DBusKeyring *keyring)
static void
_dbus_keyring_unlock (DBusKeyring *keyring)
{
if (!_dbus_delete_file (&keyring->filename_lock))
_dbus_warn ("Failed to delete lock file\n");
DBusError error;
dbus_error_init (&error);
if (!_dbus_delete_file (&keyring->filename_lock, &error))
{
_dbus_warn ("Failed to delete lock file: %s\n",
error.message);
dbus_error_free (&error);
}
}
static DBusKey*
find_key_by_id (DBusKey *keys,
int n_keys,
int id)
{
int i;
i = 0;
while (i < n_keys)
{
if (keys[i].id == id)
return &keys[i];
++i;
}
return NULL;
}
static dbus_bool_t
add_new_key (DBusKey **keys_p,
int *n_keys_p,
DBusError *error)
{
DBusKey *new;
DBusString bytes;
int id;
unsigned long timestamp;
const unsigned char *s;
dbus_bool_t retval;
DBusKey *keys;
int n_keys;
if (!_dbus_string_init (&bytes, _DBUS_INT_MAX))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"No memory to generate new secret key");
return FALSE;
}
keys = *keys_p;
n_keys = *n_keys_p;
retval = FALSE;
/* Generate an integer ID and then the actual key. */
retry:
if (!_dbus_generate_random_bytes (&bytes, 4))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"No memory to generate new secret key");
goto out;
}
_dbus_string_get_const_data (&bytes, (const char**) &s);
id = s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24);
if (id < 0)
id = - id;
_dbus_assert (id >= 0);
if (find_key_by_id (keys, n_keys, id) != NULL)
{
_dbus_string_set_length (&bytes, 0);
_dbus_verbose ("Key ID %d already existed, trying another one\n",
id);
goto retry;
}
_dbus_verbose ("Creating key with ID %d\n", id);
#define KEY_LENGTH_BYTES 24
_dbus_string_set_length (&bytes, 0);
if (!_dbus_generate_random_bytes (&bytes, KEY_LENGTH_BYTES))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"No memory to generate new secret key");
goto out;
}
new = dbus_realloc (keys, sizeof (DBusKey) * (n_keys + 1));
if (new == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"No memory to reallocate secret key list");
goto out;
}
keys = new;
n_keys += 1;
if (!_dbus_string_init (&keys[n_keys-1].secret,
_DBUS_INT_MAX))
{
n_keys -= 1; /* we don't want to free the one we didn't init */
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"No memory to store secret key");
goto out;
}
_dbus_get_current_time (&timestamp, NULL);
keys[n_keys-1].id = id;
keys[n_keys-1].creation_time = timestamp;
if (!_dbus_string_move (&bytes, 0,
&keys[n_keys-1].secret,
0))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"No memory to store secret key");
goto out;
}
retval = TRUE;
out:
if (retval)
{
*n_keys_p = n_keys;
*keys_p = keys;
}
_dbus_string_free (&bytes);
return retval;
}
/**
* Reloads the keyring file, optionally adds one new key to the file,
* removes all expired keys from the file, then resaves the file.
* Stores the keys from the file in keyring->keys.
* removes all expired keys from the file iff a key was added, then
* resaves the file. Stores the keys from the file in keyring->keys.
* Note that the file is only resaved (written to) if a key is added,
* this means that only servers ever write to the file and need to
* lock it, which avoids a lot of lock contention at login time and
* such.
*
* @param keyring the keyring
* @param add_new #TRUE to add a new key to the file before resave
* @param add_new #TRUE to add a new key to the file, expire keys, and resave
* @param error return location for errors
* @returns #FALSE on failure
*/
@ -228,8 +384,247 @@ _dbus_keyring_reload (DBusKeyring *keyring,
dbus_bool_t add_new,
DBusError *error)
{
/* FIXME */
DBusString contents;
DBusString line;
DBusResultCode result;
dbus_bool_t retval;
dbus_bool_t have_lock;
DBusKey *keys;
int n_keys;
int i;
long now;
if (!_dbus_string_init (&contents, _DBUS_INT_MAX))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"No memory to reload keyring");
return FALSE;
}
if (!_dbus_string_init (&line, _DBUS_INT_MAX))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"No memory to reload keyring");
_dbus_string_free (&contents);
return FALSE;
}
keys = NULL;
n_keys = 0;
retval = FALSE;
have_lock = FALSE;
_dbus_get_current_time (&now, NULL);
if (add_new)
{
if (!_dbus_keyring_lock (keyring))
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"Could not lock keyring file to add to it");
goto out;
}
have_lock = TRUE;
}
result = _dbus_file_get_contents (&contents,
&keyring->filename);
if (result != DBUS_RESULT_SUCCESS)
{
_dbus_verbose ("Failed to load keyring file: %s\n",
dbus_result_to_string (result));
/* continue with empty keyring file, so we recreate it */
}
if (!_dbus_string_validate_ascii (&contents, 0,
_dbus_string_get_length (&contents)))
{
_dbus_warn ("Secret keyring file contains non-ASCII! Ignoring existing contents\n");
_dbus_string_set_length (&contents, 0);
}
while (_dbus_string_pop_line (&contents, &line))
{
int next;
long val;
int id;
long timestamp;
int len;
DBusKey *new;
next = 0;
if (!_dbus_string_parse_int (&line, 0, &val, &next))
{
_dbus_verbose ("could not parse secret key ID at start of line\n");
continue;
}
if (val > _DBUS_INT_MAX || val < 0)
{
_dbus_verbose ("invalid secret key ID at start of line\n");
continue;
}
id = val;
_dbus_string_skip_blank (&line, next, &next);
if (!_dbus_string_parse_int (&line, next, &timestamp, &next))
{
_dbus_verbose ("could not parse secret key timestamp\n");
continue;
}
if (timestamp < 0 ||
(now + MAX_TIME_TRAVEL_SECONDS) < timestamp ||
(now - EXPIRE_KEYS_TIMEOUT_SECONDS) > timestamp)
{
_dbus_verbose ("dropping/ignoring %ld-seconds old key with timestamp %ld as current time is %ld\n",
now - timestamp, timestamp, now);
continue;
}
_dbus_string_skip_blank (&line, next, &next);
len = _dbus_string_get_length (&line);
if ((len - next) == 0)
{
_dbus_verbose ("no secret key after ID and timestamp\n");
continue;
}
/* We have all three parts */
new = dbus_realloc (keys, sizeof (DBusKey) * (n_keys + 1));
if (new == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"No memory to reallocate secret key list");
goto out;
}
keys = new;
n_keys += 1;
if (!_dbus_string_init (&keys[n_keys-1].secret,
_DBUS_INT_MAX))
{
n_keys -= 1; /* we don't want to free the one we didn't init */
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"No memory to store secret key");
goto out;
}
keys[n_keys-1].id = id;
keys[n_keys-1].creation_time = timestamp;
if (!_dbus_string_hex_decode (&line, next,
&keys[n_keys-1].secret,
0))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"No memory to store secret key or invalid hex encoding");
goto out;
}
}
_dbus_verbose ("Successfully loaded %d existing keys\n",
n_keys);
if (add_new)
{
if (!add_new_key (&keys, &n_keys, error))
{
_dbus_verbose ("Failed to generate new key: %s\n",
error ? "(unknown)" : error->message);
goto out;
}
_dbus_string_set_length (&contents, 0);
i = 0;
while (i < n_keys)
{
if (!_dbus_string_append_int (&contents,
keys[i].id))
goto nomem;
if (!_dbus_string_append_byte (&contents, ' '))
goto nomem;
if (!_dbus_string_append_int (&contents,
keys[i].creation_time))
goto nomem;
if (!_dbus_string_append_byte (&contents, ' '))
goto nomem;
if (!_dbus_string_hex_encode (&keys[i].secret, 0,
&contents,
_dbus_string_get_length (&contents)))
goto nomem;
if (!_dbus_string_append_byte (&contents, '\n'))
goto nomem;
++i;
continue;
nomem:
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"No memory to save secret keyring");
goto out;
}
result = _dbus_string_save_to_file (&contents, &keyring->filename);
if (result != DBUS_RESULT_SUCCESS)
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"Failed to save keyring file: %s",
dbus_result_to_string (result));
goto out;
}
}
dbus_free (keyring->keys);
keyring->keys = keys;
keyring->n_keys = n_keys;
keys = NULL;
n_keys = 0;
retval = TRUE;
out:
if (have_lock)
_dbus_keyring_unlock (keyring);
if (! ((retval == TRUE && (error == NULL || error->name == NULL)) ||
(retval == FALSE && (error == NULL || error->name != NULL))))
{
if (error && error->name)
_dbus_verbose ("error is %s: %s\n", error->name, error->message);
_dbus_warn ("returning %d but error pointer %p name %s\n",
retval, error, error->name ? error->name : "(none)");
_dbus_assert_not_reached ("didn't handle errors properly");
}
if (keys != NULL)
{
i = 0;
while (i < n_keys)
{
_dbus_string_free (&keys[i].secret);
++i;
}
dbus_free (keys);
}
_dbus_string_free (&contents);
_dbus_string_free (&line);
return retval;
}
/** @} */ /* end of internals */
@ -291,7 +686,7 @@ _dbus_keyring_new_homedir (const DBusString *username,
DBusKeyring *keyring;
dbus_bool_t error_set;
DBusString dotdir;
DBusString lock_extension;
DBusError tmp_error;
keyring = NULL;
error_set = FALSE;
@ -300,7 +695,6 @@ _dbus_keyring_new_homedir (const DBusString *username,
return FALSE;
_dbus_string_init_const (&dotdir, ".dbus-keyrings");
_dbus_string_init_const (&lock_extension, ".lock");
if (username == NULL)
{
@ -355,10 +749,30 @@ _dbus_keyring_new_homedir (const DBusString *username,
&keyring->filename_lock, 0))
goto failed;
if (!_dbus_concat_dir_and_file (&keyring->filename_lock,
&lock_extension))
if (!_dbus_string_append (&keyring->filename_lock, ".lock"))
goto failed;
dbus_error_init (&tmp_error);
if (!_dbus_keyring_reload (keyring, FALSE, &tmp_error))
{
_dbus_verbose ("didn't load an existing keyring: %s\n",
tmp_error.message);
dbus_error_free (&tmp_error);
}
/* We don't fail fatally if we can't create the directory,
* but the keyring will probably always be empty
* unless someone else manages to create it
*/
dbus_error_init (&tmp_error);
if (!_dbus_create_directory (&keyring->directory,
&tmp_error))
{
_dbus_verbose ("Creating keyring directory: %s\n",
tmp_error.message);
dbus_error_free (&tmp_error);
}
return keyring;
failed:
@ -385,7 +799,7 @@ _dbus_keyring_new_homedir (const DBusString *username,
dbus_bool_t
_dbus_keyring_validate_context (const DBusString *context)
{
if (_dbus_string_length (context) == 0)
if (_dbus_string_get_length (context) == 0)
{
_dbus_verbose ("context is zero-length\n");
return FALSE;
@ -436,7 +850,10 @@ find_recent_key (DBusKeyring *keyring)
{
DBusKey *key = &keyring->keys[i];
if (tv_sec - NEW_KEY_TIMEOUT < key->creation_time)
_dbus_verbose ("Key %d is %ld seconds old\n",
i, tv_sec - key->creation_time);
if ((tv_sec - NEW_KEY_TIMEOUT_SECONDS) < key->creation_time)
return key;
++i;
@ -458,7 +875,7 @@ find_recent_key (DBusKeyring *keyring)
*/
int
_dbus_keyring_get_best_key (DBusKeyring *keyring,
DBusError **error)
DBusError *error)
{
DBusKey *key;
@ -487,3 +904,125 @@ _dbus_keyring_get_best_key (DBusKeyring *keyring,
/** @} */ /* end of exposed API */
#ifdef DBUS_BUILD_TESTS
#include "dbus-test.h"
#include <stdio.h>
dbus_bool_t
_dbus_keyring_test (void)
{
DBusString context;
DBusKeyring *ring1;
DBusKeyring *ring2;
int id;
DBusError error;
int i;
ring1 = NULL;
ring2 = NULL;
/* Context validation */
_dbus_string_init_const (&context, "foo");
_dbus_assert (_dbus_keyring_validate_context (&context));
_dbus_string_init_const (&context, "org_freedesktop_blah");
_dbus_assert (_dbus_keyring_validate_context (&context));
_dbus_string_init_const (&context, "");
_dbus_assert (!_dbus_keyring_validate_context (&context));
_dbus_string_init_const (&context, ".foo");
_dbus_assert (!_dbus_keyring_validate_context (&context));
_dbus_string_init_const (&context, "bar.foo");
_dbus_assert (!_dbus_keyring_validate_context (&context));
_dbus_string_init_const (&context, "bar/foo");
_dbus_assert (!_dbus_keyring_validate_context (&context));
_dbus_string_init_const (&context, "bar\\foo");
_dbus_assert (!_dbus_keyring_validate_context (&context));
_dbus_string_init_const (&context, "foo\xfa\xf0");
_dbus_assert (!_dbus_keyring_validate_context (&context));
_dbus_string_init_const (&context, "foo\x80");
_dbus_assert (!_dbus_keyring_validate_context (&context));
_dbus_string_init_const (&context, "foo\x7f");
_dbus_assert (_dbus_keyring_validate_context (&context));
if (!_dbus_string_init (&context, _DBUS_INT_MAX))
_dbus_assert_not_reached ("no memory");
if (!_dbus_string_append_byte (&context, '\0'))
_dbus_assert_not_reached ("no memory");
_dbus_assert (!_dbus_keyring_validate_context (&context));
_dbus_string_free (&context);
/* Now verify that if we create a key in keyring 1,
* it is properly loaded in keyring 2
*/
_dbus_string_init_const (&context, "org_freedesktop_dbus_testsuite");
dbus_error_init (&error);
ring1 = _dbus_keyring_new_homedir (NULL, &context,
&error);
_dbus_assert (ring1);
_dbus_assert (error.name == NULL);
id = _dbus_keyring_get_best_key (ring1, &error);
if (id < 0)
{
fprintf (stderr, "Could not load keyring: %s\n", error.message);
dbus_error_free (&error);
goto failure;
}
ring2 = _dbus_keyring_new_homedir (NULL, &context, &error);
_dbus_assert (ring2);
_dbus_assert (error.name == NULL);
if (ring1->n_keys != ring2->n_keys)
{
fprintf (stderr, "Different number of keys in keyrings\n");
goto failure;
}
/* We guarantee we load and save keeping keys in a fixed
* order
*/
i = 0;
while (i < ring1->n_keys)
{
if (ring1->keys[i].id != ring2->keys[i].id)
{
fprintf (stderr, "Keyring 1 has first key ID %d and keyring 2 has %d\n",
ring1->keys[i].id, ring2->keys[i].id);
goto failure;
}
if (ring1->keys[i].creation_time != ring2->keys[i].creation_time)
{
fprintf (stderr, "Keyring 1 has first key time %ld and keyring 2 has %ld\n",
ring1->keys[i].creation_time, ring2->keys[i].creation_time);
goto failure;
}
if (!_dbus_string_equal (&ring1->keys[i].secret,
&ring2->keys[i].secret))
{
fprintf (stderr, "Keyrings 1 and 2 have different secrets for same ID/timestamp\n");
goto failure;
}
++i;
}
printf (" %d keys in test\n", ring1->n_keys);
return TRUE;
failure:
if (ring1)
_dbus_keyring_unref (ring1);
if (ring2)
_dbus_keyring_unref (ring2);
return FALSE;
}
#endif /* DBUS_BUILD_TESTS */

View file

@ -25,6 +25,7 @@
#include <dbus/dbus-macros.h>
#include <dbus/dbus-errors.h>
#include <dbus/dbus-string.h>
DBUS_BEGIN_DECLS;
@ -37,7 +38,7 @@ void _dbus_keyring_ref (DBusKeyring *keyring);
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);
DBusError *error);
dbus_bool_t _dbus_keyring_create_challenge (DBusString *challenge);
dbus_bool_t _dbus_keyring_compute_response (DBusKeyring *keyring,
int key_id,

View file

@ -470,13 +470,6 @@ _dbus_md5_compute (const DBusString *data,
{
DBusMD5Context context;
DBusString digest;
const char hexdigits[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'
};
unsigned char *p;
unsigned char *end;
int orig_len;
_dbus_md5_init (&context);
@ -485,32 +478,17 @@ _dbus_md5_compute (const DBusString *data,
if (!_dbus_string_init (&digest, _DBUS_INT_MAX))
return FALSE;
orig_len = _dbus_string_get_length (ascii_output);
if (!_dbus_md5_final (&context, &digest))
goto error;
_dbus_string_get_const_data (&digest, (const char **) &p);
end = p + 16;
while (p != end)
{
if (!_dbus_string_append_byte (ascii_output,
hexdigits[(*p >> 4)]))
goto error;
if (!_dbus_string_append_byte (ascii_output,
hexdigits[(*p & 0x0f)]))
goto error;
++p;
}
if (!_dbus_string_hex_encode (&digest, 0, ascii_output,
_dbus_string_get_length (ascii_output)))
goto error;
return TRUE;
error:
_dbus_string_free (&digest);
_dbus_string_set_length (ascii_output, orig_len);
return FALSE;
}

View file

@ -484,13 +484,6 @@ _dbus_sha_compute (const DBusString *data,
{
DBusSHAContext context;
DBusString digest;
const char hexdigits[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'
};
unsigned char *p;
unsigned char *end;
int orig_len;
_dbus_sha_init (&context);
@ -499,32 +492,17 @@ _dbus_sha_compute (const DBusString *data,
if (!_dbus_string_init (&digest, _DBUS_INT_MAX))
return FALSE;
orig_len = _dbus_string_get_length (ascii_output);
if (!_dbus_sha_final (&context, &digest))
goto error;
_dbus_string_get_const_data (&digest, (const char **) &p);
end = p + 20;
while (p != end)
{
if (!_dbus_string_append_byte (ascii_output,
hexdigits[(*p >> 4)]))
goto error;
if (!_dbus_string_append_byte (ascii_output,
hexdigits[(*p & 0x0f)]))
goto error;
++p;
}
if (!_dbus_string_hex_encode (&digest, 0, ascii_output,
_dbus_string_get_length (ascii_output)))
goto error;
return TRUE;
error:
_dbus_string_free (&digest);
_dbus_string_set_length (ascii_output, orig_len);
return FALSE;
}

View file

@ -1961,7 +1961,6 @@ _dbus_string_base64_encode (const DBusString *source,
return TRUE;
}
/**
* Decodes a string from Base64, as documented in RFC 2045.
*
@ -2067,9 +2066,203 @@ _dbus_string_base64_decode (const DBusString *source,
}
/**
* Checks that the given range of the string
* is valid ASCII. If the given range is not contained
* in the string, returns #FALSE.
* Encodes a string in hex, the way MD5 and SHA-1 are usually
* encoded. (Each byte is two hex digits.)
*
* @param source the string to encode
* @param start byte index to start encoding
* @param dest string where encoded data should be placed
* @param insert_at where to place encoded data
* @returns #TRUE if encoding was successful, #FALSE if no memory etc.
*/
dbus_bool_t
_dbus_string_hex_encode (const DBusString *source,
int start,
DBusString *dest,
int insert_at)
{
DBusString result;
const char hexdigits[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'
};
const unsigned char *p;
const unsigned char *end;
dbus_bool_t retval;
_dbus_assert (start <= _dbus_string_get_length (source));
if (!_dbus_string_init (&result, _DBUS_INT_MAX))
return FALSE;
retval = FALSE;
_dbus_string_get_const_data (source, (const char**) &p);
end = p + _dbus_string_get_length (source);
p += start;
while (p != end)
{
if (!_dbus_string_append_byte (&result,
hexdigits[(*p >> 4)]))
goto out;
if (!_dbus_string_append_byte (&result,
hexdigits[(*p & 0x0f)]))
goto out;
++p;
}
if (!_dbus_string_move (&result, 0, dest, insert_at))
goto out;
retval = TRUE;
out:
_dbus_string_free (&result);
return retval;
}
/**
* Decodes a string from hex encoding.
*
* @param source the string to decode
* @param start byte index to start decode
* @param dest string where decoded data should be placed
* @param insert_at where to place decoded data
* @returns #TRUE if decoding was successful, #FALSE if no memory etc.
*/
dbus_bool_t
_dbus_string_hex_decode (const DBusString *source,
int start,
DBusString *dest,
int insert_at)
{
DBusString result;
const unsigned char *p;
const unsigned char *end;
dbus_bool_t retval;
dbus_bool_t high_bits;
_dbus_assert (start <= _dbus_string_get_length (source));
if (!_dbus_string_init (&result, _DBUS_INT_MAX))
return FALSE;
retval = FALSE;
high_bits = TRUE;
_dbus_string_get_const_data (source, (const char**) &p);
end = p + _dbus_string_get_length (source);
p += start;
while (p != end)
{
unsigned int val;
switch (*p)
{
case '0':
val = 0;
break;
case '1':
val = 1;
break;
case '2':
val = 2;
break;
case '3':
val = 3;
break;
case '4':
val = 4;
break;
case '5':
val = 5;
break;
case '6':
val = 6;
break;
case '7':
val = 7;
break;
case '8':
val = 8;
break;
case '9':
val = 9;
break;
case 'a':
case 'A':
val = 10;
break;
case 'b':
case 'B':
val = 11;
break;
case 'c':
case 'C':
val = 12;
break;
case 'd':
case 'D':
val = 13;
break;
case 'e':
case 'E':
val = 14;
break;
case 'f':
case 'F':
val = 15;
break;
default:
val = 0;
_dbus_verbose ("invalid character '%c' in hex encoded text\n",
*p);
goto out;
}
if (high_bits)
{
if (!_dbus_string_append_byte (&result,
val << 4))
goto out;
}
else
{
int len;
unsigned char b;
len = _dbus_string_get_length (&result);
b = _dbus_string_get_byte (&result, len - 1);
b |= val;
_dbus_string_set_byte (&result, len - 1, b);
}
high_bits = !high_bits;
++p;
}
if (!_dbus_string_move (&result, 0, dest, insert_at))
goto out;
retval = TRUE;
out:
_dbus_string_free (&result);
return retval;
}
/**
* Checks that the given range of the string is valid ASCII with no
* nul bytes. If the given range is not contained in the string,
* returns #FALSE.
*
* @param str the string
* @param start first byte index to check
@ -2250,6 +2443,97 @@ test_base64_roundtrip (const unsigned char *data,
_dbus_string_free (&decoded);
}
static void
test_hex_roundtrip (const unsigned char *data,
int len)
{
DBusString orig;
DBusString encoded;
DBusString decoded;
if (len < 0)
len = strlen (data);
if (!_dbus_string_init (&orig, _DBUS_INT_MAX))
_dbus_assert_not_reached ("could not init string");
if (!_dbus_string_init (&encoded, _DBUS_INT_MAX))
_dbus_assert_not_reached ("could not init string");
if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
_dbus_assert_not_reached ("could not init string");
if (!_dbus_string_append_len (&orig, data, len))
_dbus_assert_not_reached ("couldn't append orig data");
if (!_dbus_string_hex_encode (&orig, 0, &encoded, 0))
_dbus_assert_not_reached ("could not encode");
if (!_dbus_string_hex_decode (&encoded, 0, &decoded, 0))
_dbus_assert_not_reached ("could not decode");
if (!_dbus_string_equal (&orig, &decoded))
{
const char *s;
printf ("Original string %d bytes encoded %d bytes decoded %d bytes\n",
_dbus_string_get_length (&orig),
_dbus_string_get_length (&encoded),
_dbus_string_get_length (&decoded));
printf ("Original: %s\n", data);
_dbus_string_get_const_data (&decoded, &s);
printf ("Decoded: %s\n", s);
_dbus_assert_not_reached ("original string not the same as string decoded from base64");
}
_dbus_string_free (&orig);
_dbus_string_free (&encoded);
_dbus_string_free (&decoded);
}
typedef void (* TestRoundtripFunc) (const unsigned char *data,
int len);
static void
test_roundtrips (TestRoundtripFunc func)
{
(* func) ("Hello this is a string\n", -1);
(* func) ("Hello this is a string\n1", -1);
(* func) ("Hello this is a string\n12", -1);
(* func) ("Hello this is a string\n123", -1);
(* func) ("Hello this is a string\n1234", -1);
(* func) ("Hello this is a string\n12345", -1);
(* func) ("", 0);
(* func) ("1", 1);
(* func) ("12", 2);
(* func) ("123", 3);
(* func) ("1234", 4);
(* func) ("12345", 5);
(* func) ("", 1);
(* func) ("1", 2);
(* func) ("12", 3);
(* func) ("123", 4);
(* func) ("1234", 5);
(* func) ("12345", 6);
{
unsigned char buf[512];
int i;
i = 0;
while (i < _DBUS_N_ELEMENTS (buf))
{
buf[i] = i;
++i;
}
i = 0;
while (i < _DBUS_N_ELEMENTS (buf))
{
(* func) (buf, i);
++i;
}
}
}
/**
* @ingroup DBusStringInternals
* Unit test for DBusString.
@ -2618,40 +2902,9 @@ _dbus_string_test (void)
_dbus_string_free (&str);
/* Base 64 */
test_base64_roundtrip ("Hello this is a string\n", -1);
test_base64_roundtrip ("Hello this is a string\n1", -1);
test_base64_roundtrip ("Hello this is a string\n12", -1);
test_base64_roundtrip ("Hello this is a string\n123", -1);
test_base64_roundtrip ("Hello this is a string\n1234", -1);
test_base64_roundtrip ("Hello this is a string\n12345", -1);
test_base64_roundtrip ("", 0);
test_base64_roundtrip ("1", 1);
test_base64_roundtrip ("12", 2);
test_base64_roundtrip ("123", 3);
test_base64_roundtrip ("1234", 4);
test_base64_roundtrip ("12345", 5);
test_base64_roundtrip ("", 1);
test_base64_roundtrip ("1", 2);
test_base64_roundtrip ("12", 3);
test_base64_roundtrip ("123", 4);
test_base64_roundtrip ("1234", 5);
test_base64_roundtrip ("12345", 6);
{
unsigned char buf[512];
i = 0;
while (i < _DBUS_N_ELEMENTS (buf))
{
buf[i] = i;
++i;
}
i = 0;
while (i < _DBUS_N_ELEMENTS (buf))
{
test_base64_roundtrip (buf, i);
++i;
}
}
/* Base 64 and Hex encoding */
test_roundtrips (test_base64_roundtrip);
test_roundtrips (test_hex_roundtrip);
return TRUE;
}

View file

@ -147,6 +147,10 @@ dbus_bool_t _dbus_string_parse_int (const DBusString *str,
int start,
long *value_return,
int *end_return);
dbus_bool_t _dbus_string_parse_uint (const DBusString *str,
int start,
unsigned long *value_return,
int *end_return);
dbus_bool_t _dbus_string_parse_double (const DBusString *str,
int start,
double *value,
@ -199,6 +203,14 @@ dbus_bool_t _dbus_string_base64_decode (const DBusString *source,
int start,
DBusString *dest,
int insert_at);
dbus_bool_t _dbus_string_hex_encode (const DBusString *source,
int start,
DBusString *dest,
int insert_at);
dbus_bool_t _dbus_string_hex_decode (const DBusString *source,
int start,
DBusString *dest,
int insert_at);
dbus_bool_t _dbus_string_validate_ascii (const DBusString *str,
int start,

View file

@ -930,6 +930,45 @@ _dbus_string_parse_int (const DBusString *str,
return TRUE;
}
/**
* Parses an unsigned integer contained in a DBusString. Either return
* parameter may be #NULL if you aren't interested in it. The integer
* is parsed and stored in value_return. Return parameters are not
* initialized if the function returns #FALSE.
*
* @param str the string
* @param start the byte index of the start of the integer
* @param value_return return location of the integer value or #NULL
* @param end_return return location of the end of the integer, or #NULL
* @returns #TRUE on success
*/
dbus_bool_t
_dbus_string_parse_uint (const DBusString *str,
int start,
unsigned long *value_return,
int *end_return)
{
unsigned long v;
const char *p;
char *end;
_dbus_string_get_const_data_len (str, &p, start,
_dbus_string_get_length (str) - start);
end = NULL;
errno = 0;
v = strtoul (p, &end, 0);
if (end == NULL || end == p || errno != 0)
return FALSE;
if (value_return)
*value_return = v;
if (end_return)
*end_return = start + (end - p);
return TRUE;
}
/**
* Parses a floating point number contained in a DBusString. Either
* return parameter may be #NULL if you aren't interested in it. The
@ -1623,8 +1662,39 @@ _dbus_file_get_contents (DBusString *str,
}
}
static dbus_bool_t
append_unique_chars (DBusString *str)
{
static const char letters[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
int i;
int len;
#define N_UNIQUE_CHARS 8
if (!_dbus_generate_random_bytes (str, N_UNIQUE_CHARS))
return FALSE;
len = _dbus_string_get_length (str);
i = len - N_UNIQUE_CHARS;
while (i < len)
{
_dbus_string_set_byte (str, i,
letters[_dbus_string_get_byte (str, i) %
(sizeof (letters) - 1)]);
++i;
}
_dbus_assert (_dbus_string_validate_ascii (str, len - N_UNIQUE_CHARS,
N_UNIQUE_CHARS));
return TRUE;
}
/**
* Writes a string out to a file.
* Writes a string out to a file. If the file exists,
* it will be atomically overwritten by the new data.
*
* @param str the string to write out
* @param filename the file to save string to
@ -1637,15 +1707,41 @@ _dbus_string_save_to_file (const DBusString *str,
int fd;
int bytes_to_write;
const char *filename_c;
DBusString tmp_filename;
const char *tmp_filename_c;
int total;
_dbus_string_get_const_data (filename, &filename_c);
DBusResultCode result;
dbus_bool_t need_unlink;
fd = open (filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT,
fd = -1;
result = DBUS_RESULT_FAILED;
need_unlink = FALSE;
if (!_dbus_string_init (&tmp_filename, _DBUS_INT_MAX))
return DBUS_RESULT_NO_MEMORY;
if (!_dbus_string_copy (filename, 0, &tmp_filename, 0))
return DBUS_RESULT_NO_MEMORY;
if (!_dbus_string_append (&tmp_filename, "."))
return DBUS_RESULT_NO_MEMORY;
if (!append_unique_chars (&tmp_filename))
return DBUS_RESULT_NO_MEMORY;
_dbus_string_get_const_data (filename, &filename_c);
_dbus_string_get_const_data (&tmp_filename, &tmp_filename_c);
fd = open (tmp_filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT,
0600);
if (fd < 0)
return _dbus_result_from_errno (errno);
{
result = _dbus_result_from_errno (errno);
goto out;
}
need_unlink = TRUE;
total = 0;
bytes_to_write = _dbus_string_get_length (str);
@ -1665,15 +1761,45 @@ _dbus_string_save_to_file (const DBusString *str,
_dbus_verbose ("write() failed: %s",
_dbus_strerror (errno));
close (fd);
return result;
goto out;
}
total += bytes_written;
}
close (fd);
return DBUS_RESULT_SUCCESS;
if (close (fd) < 0)
{
_dbus_verbose ("close() failed: %s\n", _dbus_strerror (errno));
goto out;
}
fd = -1;
if (rename (tmp_filename_c, filename_c) < 0)
{
_dbus_verbose ("rename() failed: %s\n", _dbus_strerror (errno));
goto out;
}
need_unlink = FALSE;
result = DBUS_RESULT_SUCCESS;
out:
/* close first, then unlink, to prevent ".nfs34234235" garbage
* files
*/
if (fd >= 0)
close (fd);
if (need_unlink && unlink (tmp_filename_c) < 0)
_dbus_verbose ("Failed to unlink temp file %s: %s\n",
tmp_filename_c, _dbus_strerror (errno));
_dbus_string_free (&tmp_filename);
return result;
}
/** Creates the given file, failing if the file already exists.
@ -1733,7 +1859,42 @@ _dbus_delete_file (const DBusString *filename,
_dbus_string_get_const_data (filename, &filename_c);
if (unlink (filename_c) < 0)
return FALSE;
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"Failed to delete file %s: %s\n",
filename_c, _dbus_strerror (errno));
return FALSE;
}
else
return TRUE;
}
/**
* Creates a directory; succeeds if the directory
* is created or already existed.
*
* @param filename directory filename
* @param error initialized error object
* @returns #TRUE on success
*/
dbus_bool_t
_dbus_create_directory (const DBusString *filename,
DBusError *error)
{
const char *filename_c;
_dbus_string_get_const_data (filename, &filename_c);
if (mkdir (filename_c, 0700) < 0)
{
if (errno == EEXIST)
return TRUE;
dbus_set_error (error, DBUS_ERROR_FAILED,
"Failed to create directory %s: %s\n",
filename_c, _dbus_strerror (errno));
return FALSE;
}
else
return TRUE;
}
@ -1907,6 +2068,8 @@ _dbus_generate_random_bytes (DBusString *str,
int i;
/* fall back to pseudorandom */
_dbus_verbose ("Falling back to pseudorandom for %d bytes\n",
n_bytes);
_dbus_get_current_time (NULL, &tv_usec);
srand (tv_usec);
@ -1915,7 +2078,7 @@ _dbus_generate_random_bytes (DBusString *str,
while (i < n_bytes)
{
double r;
int b;
unsigned int b;
r = rand ();
b = (r / (double) RAND_MAX) * 255.0;
@ -1933,6 +2096,9 @@ _dbus_generate_random_bytes (DBusString *str,
if (_dbus_read (fd, str, n_bytes) != n_bytes)
goto failed;
_dbus_verbose ("Read %d bytes from /dev/urandom\n",
n_bytes);
close (fd);
return TRUE;
@ -1948,6 +2114,9 @@ _dbus_generate_random_bytes (DBusString *str,
/**
* A wrapper around strerror()
*
* @todo get rid of this function, it's the same as
* _dbus_strerror().
*
* @param errnum the errno
* @returns an error message (never #NULL)
*/
@ -1963,6 +2132,25 @@ _dbus_errno_to_string (int errnum)
return msg;
}
/**
* A wrapper around strerror() because some platforms
* may be lame and not have strerror().
*
* @param error_number errno.
* @returns error description.
*/
const char*
_dbus_strerror (int error_number)
{
const char *msg;
msg = strerror (error_number);
if (msg == NULL)
msg = "unknown";
return msg;
}
/* Avoids a danger in threaded situations (calling close()
* on a file descriptor twice, and another thread has
* re-opened it since the first close)

View file

@ -147,6 +147,8 @@ dbus_bool_t _dbus_create_file_exclusively (const DBusString *filename,
DBusError *error);
dbus_bool_t _dbus_delete_file (const DBusString *filename,
DBusError *error);
dbus_bool_t _dbus_create_directory (const DBusString *filename,
DBusError *error);
dbus_bool_t _dbus_concat_dir_and_file (DBusString *dir,
const DBusString *next_component);

View file

@ -59,6 +59,10 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir)
if (!_dbus_string_test ())
die ("strings");
printf ("%s: running keyring tests\n", "dbus-test");
if (!_dbus_keyring_test ())
die ("keyring");
printf ("%s: running md5 tests\n", "dbus-test");
if (!_dbus_md5_test ())
die ("md5");

View file

@ -45,6 +45,7 @@ dbus_bool_t _dbus_message_test (const char *test_data_dir);
dbus_bool_t _dbus_auth_test (const char *test_data_dir);
dbus_bool_t _dbus_md5_test (void);
dbus_bool_t _dbus_sha_test (const char *test_data_dir);
dbus_bool_t _dbus_keyring_test (void);
void dbus_internal_do_not_use_run_tests (const char *test_data_dir);
dbus_bool_t dbus_internal_do_not_use_try_message_file (const DBusString *filename,