1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-keyring.c Store secret cookies in your homedir
4 * Copyright (C) 2003 Red Hat Inc.
6 * Licensed under the Academic Free License version 1.2
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "dbus-keyring.h"
25 #include <dbus/dbus-string.h>
26 #include <dbus/dbus-list.h>
27 #include <dbus/dbus-sysdeps.h>
30 * @defgroup DBusKeyring keyring class
31 * @ingroup DBusInternals
32 * @brief DBusKeyring data structure
34 * Types and functions related to DBusKeyring. DBusKeyring is intended
35 * to manage cookies used to authenticate clients to servers. This is
36 * essentially the "verify that client can read the user's homedir"
37 * authentication mechanism. Both client and server must have access
40 * The secret keys are not kept in locked memory, and are written to a
41 * file in the user's homedir. However they are transient (only used
42 * by a single server instance for a fixed period of time, then
43 * discarded). Also, the keys are not sent over the wire.
47 * @defgroup DBusKeyringInternals DBusKeyring implementation details
48 * @ingroup DBusInternals
49 * @brief DBusKeyring implementation details
51 * The guts of DBusKeyring.
56 /** The maximum age of a key before we create a new key to use in
57 * challenges. This isn't super-reliably enforced, since system
58 * clocks can change or be wrong, but we make a best effort to only
59 * use keys for a short time.
61 #define NEW_KEY_TIMEOUT (60*5)
63 * The time after which we drop a key from the secrets file
65 #define EXPIRE_KEYS_TIMEOUT (NEW_KEY_TIMEOUT + (60*2))
69 dbus_int32_t id; /**< identifier used to refer to the key */
71 unsigned long creation_time; /**< when the key was generated,
75 DBusString secret; /**< the actual key */
80 * @brief Internals of DBusKeyring.
82 * DBusKeyring internals. DBusKeyring is an opaque object, it must be
83 * used via accessor functions.
87 int refcount; /**< Reference count */
88 DBusString directory; /**< Directory the below two items are inside */
89 DBusString filename; /**< Keyring filename */
90 DBusString filename_lock; /**< Name of lockfile */
91 DBusKey *keys; /**< Keys loaded from the file */
92 int n_keys; /**< Number of keys */
96 _dbus_keyring_new (void)
100 keyring = dbus_new0 (DBusKeyring, 1);
104 if (!_dbus_string_init (&keyring->directory))
107 if (!_dbus_string_init (&keyring->filename))
110 if (!_dbus_string_init (&keyring->filename_lock))
113 keyring->refcount = 1;
114 keyring->keys = NULL;
120 _dbus_string_free (&keyring->filename);
122 _dbus_string_free (&keyring->directory);
130 free_keys (DBusKey *keys,
135 /* should be safe for args NULL, 0 */
140 _dbus_string_free (&keys[i].secret);
147 /* Our locking scheme is highly unreliable. However, there is
148 * unfortunately no reliable locking scheme in user home directories;
149 * between bugs in Linux NFS, people using Tru64 or other total crap
150 * NFS, AFS, random-file-system-of-the-week, and so forth, fcntl() in
151 * homedirs simply generates tons of bug reports. This has been
152 * learned through hard experience with GConf, unfortunately.
154 * This bad hack might work better for the kind of lock we have here,
155 * which we don't expect to hold for any length of time. Crashing
156 * while we hold it should be unlikely, and timing out such that we
157 * delete a stale lock should also be unlikely except when the
158 * filesystem is running really slowly. Stuff might break in corner
159 * cases but as long as it's not a security-level breakage it should
163 /** Maximum number of timeouts waiting for lock before we decide it's stale */
164 #define MAX_LOCK_TIMEOUTS 6
165 /** Length of each timeout while waiting for a lock */
166 #define LOCK_TIMEOUT 500
169 _dbus_keyring_lock (DBusKeyring *keyring)
174 while (n_timeouts < MAX_LOCK_TIMEOUTS)
178 dbus_error_init (&error);
179 if (_dbus_create_file_exclusively (&keyring->filename_lock,
183 _dbus_verbose ("Did not get lock file: %s\n",
185 dbus_error_free (&error);
187 _dbus_sleep_milliseconds (LOCK_TIMEOUT);
192 if (n_timeouts == MAX_LOCK_TIMEOUTS)
194 _dbus_verbose ("Lock file timed out, assuming stale\n");
196 _dbus_delete_file (&keyring->filename_lock);
198 if (!_dbus_create_file_exclusively (&keyring->filename_lock,
201 _dbus_verbose ("Couldn't create lock file after trying to delete the stale one, giving up\n");
210 _dbus_keyring_unlock (DBusKeyring *keyring)
212 if (!_dbus_delete_file (&keyring->filename_lock))
213 _dbus_warn ("Failed to delete lock file\n");
217 * Reloads the keyring file, optionally adds one new key to the file,
218 * removes all expired keys from the file, then resaves the file.
219 * Stores the keys from the file in keyring->keys.
221 * @param keyring the keyring
222 * @param add_new #TRUE to add a new key to the file before resave
223 * @param error return location for errors
224 * @returns #FALSE on failure
227 _dbus_keyring_reload (DBusKeyring *keyring,
235 /** @} */ /* end of internals */
238 * @addtogroup DBusKeyring
244 * Increments reference count of the keyring
246 * @param keyring the keyring
249 _dbus_keyring_ref (DBusKeyring *keyring)
251 keyring->refcount += 1;
255 * Decrements refcount and finalizes if it reaches
258 * @param keyring the keyring
261 _dbus_keyring_unref (DBusKeyring *keyring)
263 keyring->refcount -= 1;
265 if (keyring->refcount == 0)
267 _dbus_string_free (&keyring->filename);
268 _dbus_string_free (&keyring->filename_lock);
269 _dbus_string_free (&keyring->directory);
270 free_keys (keyring->keys, keyring->n_keys);
276 * Creates a new keyring that lives in the ~/.dbus-keyrings
277 * directory of the given user. If the username is #NULL,
278 * uses the user owning the current process.
280 * @param username username to get keyring for, or #NULL
281 * @param context which keyring to get
282 * @param error return location for errors
283 * @returns the keyring or #NULL on error
286 _dbus_keyring_new_homedir (const DBusString *username,
287 const DBusString *context,
291 DBusKeyring *keyring;
292 dbus_bool_t error_set;
294 DBusString lock_extension;
299 if (!_dbus_string_init (&homedir, _DBUS_INT_MAX))
302 _dbus_string_init_const (&dotdir, ".dbus-keyrings");
303 _dbus_string_init_const (&lock_extension, ".lock");
305 if (username == NULL)
307 const DBusString *const_homedir;
309 if (!_dbus_user_info_from_current_process (&username,
314 if (!_dbus_string_copy (const_homedir, 0,
320 if (!_dbus_homedir_from_username (username, &homedir))
324 keyring = _dbus_keyring_new ();
328 /* should have been validated already, but paranoia check here */
329 if (!_dbus_keyring_validate_context (context))
332 dbus_set_error_const (error,
334 "Invalid context in keyring creation");
338 if (!_dbus_string_copy (&homedir, 0,
339 &keyring->directory, 0))
342 if (!_dbus_concat_dir_and_file (&keyring->directory,
346 if (!_dbus_string_copy (&keyring->directory, 0,
347 &keyring->filename, 0))
350 if (!_dbus_concat_dir_and_file (&keyring->filename,
354 if (!_dbus_string_copy (&keyring->filename, 0,
355 &keyring->filename_lock, 0))
358 if (!_dbus_concat_dir_and_file (&keyring->filename_lock,
366 dbus_set_error_const (error,
367 DBUS_ERROR_NO_MEMORY,
368 "No memory to create keyring");
370 _dbus_keyring_unref (keyring);
371 _dbus_string_free (&homedir);
377 * Checks whether the context is a valid context.
378 * Contexts that might cause confusion when used
379 * in filenames are not allowed (contexts can't
380 * start with a dot or contain dir separators).
382 * @param context the context
383 * @returns #TRUE if valid
386 _dbus_keyring_validate_context (const DBusString *context)
388 if (_dbus_string_length (context) == 0)
390 _dbus_verbose ("context is zero-length\n");
394 if (!_dbus_string_validate_ascii (context, 0,
395 _dbus_string_get_length (context)))
397 _dbus_verbose ("context not valid ascii\n");
401 /* no directory separators */
402 if (_dbus_string_find (context, 0, "/", NULL))
404 _dbus_verbose ("context contains a slash\n");
408 if (_dbus_string_find (context, 0, "\\", NULL))
410 _dbus_verbose ("context contains a backslash\n");
414 /* prevent attempts to use dotfiles or ".." or ".lock"
415 * all of which might allow some kind of attack
417 if (_dbus_string_find (context, 0, ".", NULL))
419 _dbus_verbose ("context contains a dot\n");
427 find_recent_key (DBusKeyring *keyring)
430 long tv_sec, tv_usec;
432 _dbus_get_current_time (&tv_sec, &tv_usec);
435 while (i < keyring->n_keys)
437 DBusKey *key = &keyring->keys[i];
439 if (tv_sec - NEW_KEY_TIMEOUT < key->creation_time)
449 * Gets a recent key to use for authentication.
450 * If no recent key exists, creates one. Returns
451 * the key ID. If a key can't be written to the keyring
452 * file so no recent key can be created, returns -1.
453 * All valid keys are > 0.
455 * @param keyring the keyring
456 * @param error error on failure
457 * @returns key ID to use for auth, or -1 on failure
460 _dbus_keyring_get_best_key (DBusKeyring *keyring,
465 key = find_recent_key (keyring);
469 /* All our keys are too old, or we've never loaded the
470 * keyring. Create a new one.
472 if (!_dbus_keyring_reload (keyring, TRUE,
476 key = find_recent_key (keyring);
481 dbus_set_error_const (error,
483 "No recent-enough key found in keyring, and unable to create a new key");
488 /** @} */ /* end of exposed API */