Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

dbus-keyring.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */ 00002 /* dbus-keyring.c Store secret cookies in your homedir 00003 * 00004 * Copyright (C) 2003, 2004 Red Hat Inc. 00005 * 00006 * Licensed under the Academic Free License version 2.1 00007 * 00008 * This program is free software; you can redistribute it and/or modify 00009 * it under the terms of the GNU General Public License as published by 00010 * the Free Software Foundation; either version 2 of the License, or 00011 * (at your option) any later version. 00012 * 00013 * This program is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 * GNU General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU General Public License 00019 * along with this program; if not, write to the Free Software 00020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00021 * 00022 */ 00023 00024 #include "dbus-keyring.h" 00025 #include "dbus-userdb.h" 00026 #include "dbus-protocol.h" 00027 #include <dbus/dbus-string.h> 00028 #include <dbus/dbus-list.h> 00029 #include <dbus/dbus-sysdeps.h> 00030 00067 #define NEW_KEY_TIMEOUT_SECONDS (60*5) 00068 00073 #define EXPIRE_KEYS_TIMEOUT_SECONDS (NEW_KEY_TIMEOUT_SECONDS + (60*2)) 00074 00077 #define MAX_TIME_TRAVEL_SECONDS (60*5) 00078 00083 #ifdef DBUS_BUILD_TESTS 00084 #define MAX_KEYS_IN_FILE 10 00085 #else 00086 #define MAX_KEYS_IN_FILE 256 00087 #endif 00088 00092 typedef struct 00093 { 00094 dbus_int32_t id; 00096 long creation_time; 00101 DBusString secret; 00103 } DBusKey; 00104 00111 struct DBusKeyring 00112 { 00113 int refcount; 00114 DBusString username; 00115 DBusString directory; 00116 DBusString filename; 00117 DBusString filename_lock; 00118 DBusKey *keys; 00119 int n_keys; 00120 }; 00121 00122 static DBusKeyring* 00123 _dbus_keyring_new (void) 00124 { 00125 DBusKeyring *keyring; 00126 00127 keyring = dbus_new0 (DBusKeyring, 1); 00128 if (keyring == NULL) 00129 goto out_0; 00130 00131 if (!_dbus_string_init (&keyring->directory)) 00132 goto out_1; 00133 00134 if (!_dbus_string_init (&keyring->filename)) 00135 goto out_2; 00136 00137 if (!_dbus_string_init (&keyring->filename_lock)) 00138 goto out_3; 00139 00140 if (!_dbus_string_init (&keyring->username)) 00141 goto out_4; 00142 00143 keyring->refcount = 1; 00144 keyring->keys = NULL; 00145 keyring->n_keys = 0; 00146 00147 return keyring; 00148 00149 out_4: 00150 _dbus_string_free (&keyring->filename_lock); 00151 out_3: 00152 _dbus_string_free (&keyring->filename); 00153 out_2: 00154 _dbus_string_free (&keyring->directory); 00155 out_1: 00156 dbus_free (keyring); 00157 out_0: 00158 return NULL; 00159 } 00160 00161 static void 00162 free_keys (DBusKey *keys, 00163 int n_keys) 00164 { 00165 int i; 00166 00167 /* should be safe for args NULL, 0 */ 00168 00169 i = 0; 00170 while (i < n_keys) 00171 { 00172 _dbus_string_free (&keys[i].secret); 00173 ++i; 00174 } 00175 00176 dbus_free (keys); 00177 } 00178 00179 /* Our locking scheme is highly unreliable. However, there is 00180 * unfortunately no reliable locking scheme in user home directories; 00181 * between bugs in Linux NFS, people using Tru64 or other total crap 00182 * NFS, AFS, random-file-system-of-the-week, and so forth, fcntl() in 00183 * homedirs simply generates tons of bug reports. This has been 00184 * learned through hard experience with GConf, unfortunately. 00185 * 00186 * This bad hack might work better for the kind of lock we have here, 00187 * which we don't expect to hold for any length of time. Crashing 00188 * while we hold it should be unlikely, and timing out such that we 00189 * delete a stale lock should also be unlikely except when the 00190 * filesystem is running really slowly. Stuff might break in corner 00191 * cases but as long as it's not a security-level breakage it should 00192 * be OK. 00193 */ 00194 00196 #define MAX_LOCK_TIMEOUTS 32 00197 00198 #define LOCK_TIMEOUT_MILLISECONDS 250 00199 00200 static dbus_bool_t 00201 _dbus_keyring_lock (DBusKeyring *keyring) 00202 { 00203 int n_timeouts; 00204 00205 n_timeouts = 0; 00206 while (n_timeouts < MAX_LOCK_TIMEOUTS) 00207 { 00208 DBusError error; 00209 00210 dbus_error_init (&error); 00211 if (_dbus_create_file_exclusively (&keyring->filename_lock, 00212 &error)) 00213 break; 00214 00215 _dbus_verbose ("Did not get lock file, sleeping %d milliseconds (%s)\n", 00216 LOCK_TIMEOUT_MILLISECONDS, error.message); 00217 dbus_error_free (&error); 00218 00219 _dbus_sleep_milliseconds (LOCK_TIMEOUT_MILLISECONDS); 00220 00221 ++n_timeouts; 00222 } 00223 00224 if (n_timeouts == MAX_LOCK_TIMEOUTS) 00225 { 00226 DBusError error; 00227 00228 _dbus_verbose ("Lock file timed out %d times, assuming stale\n", 00229 n_timeouts); 00230 00231 dbus_error_init (&error); 00232 00233 if (!_dbus_delete_file (&keyring->filename_lock, &error)) 00234 { 00235 _dbus_verbose ("Couldn't delete old lock file: %s\n", 00236 error.message); 00237 dbus_error_free (&error); 00238 return FALSE; 00239 } 00240 00241 if (!_dbus_create_file_exclusively (&keyring->filename_lock, 00242 &error)) 00243 { 00244 _dbus_verbose ("Couldn't create lock file after deleting stale one: %s\n", 00245 error.message); 00246 dbus_error_free (&error); 00247 return FALSE; 00248 } 00249 } 00250 00251 return TRUE; 00252 } 00253 00254 static void 00255 _dbus_keyring_unlock (DBusKeyring *keyring) 00256 { 00257 DBusError error; 00258 dbus_error_init (&error); 00259 if (!_dbus_delete_file (&keyring->filename_lock, &error)) 00260 { 00261 _dbus_warn ("Failed to delete lock file: %s\n", 00262 error.message); 00263 dbus_error_free (&error); 00264 } 00265 } 00266 00267 static DBusKey* 00268 find_key_by_id (DBusKey *keys, 00269 int n_keys, 00270 int id) 00271 { 00272 int i; 00273 00274 i = 0; 00275 while (i < n_keys) 00276 { 00277 if (keys[i].id == id) 00278 return &keys[i]; 00279 00280 ++i; 00281 } 00282 00283 return NULL; 00284 } 00285 00286 static dbus_bool_t 00287 add_new_key (DBusKey **keys_p, 00288 int *n_keys_p, 00289 DBusError *error) 00290 { 00291 DBusKey *new; 00292 DBusString bytes; 00293 int id; 00294 long timestamp; 00295 const unsigned char *s; 00296 dbus_bool_t retval; 00297 DBusKey *keys; 00298 int n_keys; 00299 00300 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00301 00302 if (!_dbus_string_init (&bytes)) 00303 { 00304 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00305 return FALSE; 00306 } 00307 00308 keys = *keys_p; 00309 n_keys = *n_keys_p; 00310 retval = FALSE; 00311 00312 /* Generate an integer ID and then the actual key. */ 00313 retry: 00314 00315 if (!_dbus_generate_random_bytes (&bytes, 4)) 00316 { 00317 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00318 goto out; 00319 } 00320 00321 s = (const unsigned char*) _dbus_string_get_const_data (&bytes); 00322 00323 id = s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24); 00324 if (id < 0) 00325 id = - id; 00326 _dbus_assert (id >= 0); 00327 00328 if (find_key_by_id (keys, n_keys, id) != NULL) 00329 { 00330 _dbus_string_set_length (&bytes, 0); 00331 _dbus_verbose ("Key ID %d already existed, trying another one\n", 00332 id); 00333 goto retry; 00334 } 00335 00336 _dbus_verbose ("Creating key with ID %d\n", id); 00337 00338 #define KEY_LENGTH_BYTES 24 00339 _dbus_string_set_length (&bytes, 0); 00340 if (!_dbus_generate_random_bytes (&bytes, KEY_LENGTH_BYTES)) 00341 { 00342 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00343 goto out; 00344 } 00345 00346 new = dbus_realloc (keys, sizeof (DBusKey) * (n_keys + 1)); 00347 if (new == NULL) 00348 { 00349 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00350 goto out; 00351 } 00352 00353 keys = new; 00354 *keys_p = keys; /* otherwise *keys_p ends up invalid */ 00355 n_keys += 1; 00356 00357 if (!_dbus_string_init (&keys[n_keys-1].secret)) 00358 { 00359 n_keys -= 1; /* we don't want to free the one we didn't init */ 00360 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00361 goto out; 00362 } 00363 00364 _dbus_get_current_time (&timestamp, NULL); 00365 00366 keys[n_keys-1].id = id; 00367 keys[n_keys-1].creation_time = timestamp; 00368 if (!_dbus_string_move (&bytes, 0, 00369 &keys[n_keys-1].secret, 00370 0)) 00371 { 00372 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00373 _dbus_string_free (&keys[n_keys-1].secret); 00374 n_keys -= 1; 00375 goto out; 00376 } 00377 00378 retval = TRUE; 00379 00380 out: 00381 *n_keys_p = n_keys; 00382 00383 _dbus_string_free (&bytes); 00384 return retval; 00385 } 00386 00401 static dbus_bool_t 00402 _dbus_keyring_reload (DBusKeyring *keyring, 00403 dbus_bool_t add_new, 00404 DBusError *error) 00405 { 00406 DBusString contents; 00407 DBusString line; 00408 dbus_bool_t retval; 00409 dbus_bool_t have_lock; 00410 DBusKey *keys; 00411 int n_keys; 00412 int i; 00413 long now; 00414 DBusError tmp_error; 00415 00416 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00417 00418 if (!_dbus_string_init (&contents)) 00419 { 00420 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00421 return FALSE; 00422 } 00423 00424 if (!_dbus_string_init (&line)) 00425 { 00426 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00427 _dbus_string_free (&contents); 00428 return FALSE; 00429 } 00430 00431 keys = NULL; 00432 n_keys = 0; 00433 retval = FALSE; 00434 have_lock = FALSE; 00435 00436 _dbus_get_current_time (&now, NULL); 00437 00438 if (add_new) 00439 { 00440 if (!_dbus_keyring_lock (keyring)) 00441 { 00442 dbus_set_error (error, DBUS_ERROR_FAILED, 00443 "Could not lock keyring file to add to it"); 00444 goto out; 00445 } 00446 00447 have_lock = TRUE; 00448 } 00449 00450 dbus_error_init (&tmp_error); 00451 if (!_dbus_file_get_contents (&contents, 00452 &keyring->filename, 00453 &tmp_error)) 00454 { 00455 _dbus_verbose ("Failed to load keyring file: %s\n", 00456 tmp_error.message); 00457 /* continue with empty keyring file, so we recreate it */ 00458 dbus_error_free (&tmp_error); 00459 } 00460 00461 if (!_dbus_string_validate_ascii (&contents, 0, 00462 _dbus_string_get_length (&contents))) 00463 { 00464 _dbus_warn ("Secret keyring file contains non-ASCII! Ignoring existing contents\n"); 00465 _dbus_string_set_length (&contents, 0); 00466 } 00467 00468 /* FIXME this is badly inefficient for large keyring files 00469 * (not that large keyring files exist outside of test suites) 00470 */ 00471 while (_dbus_string_pop_line (&contents, &line)) 00472 { 00473 int next; 00474 long val; 00475 int id; 00476 long timestamp; 00477 int len; 00478 int end; 00479 DBusKey *new; 00480 00481 /* Don't load more than the max. */ 00482 if (n_keys >= (add_new ? MAX_KEYS_IN_FILE : MAX_KEYS_IN_FILE - 1)) 00483 break; 00484 00485 next = 0; 00486 if (!_dbus_string_parse_int (&line, 0, &val, &next)) 00487 { 00488 _dbus_verbose ("could not parse secret key ID at start of line\n"); 00489 continue; 00490 } 00491 00492 if (val > _DBUS_INT_MAX || val < 0) 00493 { 00494 _dbus_verbose ("invalid secret key ID at start of line\n"); 00495 continue; 00496 } 00497 00498 id = val; 00499 00500 _dbus_string_skip_blank (&line, next, &next); 00501 00502 if (!_dbus_string_parse_int (&line, next, &timestamp, &next)) 00503 { 00504 _dbus_verbose ("could not parse secret key timestamp\n"); 00505 continue; 00506 } 00507 00508 if (timestamp < 0 || 00509 (now + MAX_TIME_TRAVEL_SECONDS) < timestamp || 00510 (now - EXPIRE_KEYS_TIMEOUT_SECONDS) > timestamp) 00511 { 00512 _dbus_verbose ("dropping/ignoring %ld-seconds old key with timestamp %ld as current time is %ld\n", 00513 now - timestamp, timestamp, now); 00514 continue; 00515 } 00516 00517 _dbus_string_skip_blank (&line, next, &next); 00518 00519 len = _dbus_string_get_length (&line); 00520 00521 if ((len - next) == 0) 00522 { 00523 _dbus_verbose ("no secret key after ID and timestamp\n"); 00524 continue; 00525 } 00526 00527 /* We have all three parts */ 00528 new = dbus_realloc (keys, sizeof (DBusKey) * (n_keys + 1)); 00529 if (new == NULL) 00530 { 00531 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00532 goto out; 00533 } 00534 00535 keys = new; 00536 n_keys += 1; 00537 00538 if (!_dbus_string_init (&keys[n_keys-1].secret)) 00539 { 00540 n_keys -= 1; /* we don't want to free the one we didn't init */ 00541 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00542 goto out; 00543 } 00544 00545 keys[n_keys-1].id = id; 00546 keys[n_keys-1].creation_time = timestamp; 00547 if (!_dbus_string_hex_decode (&line, next, &end, 00548 &keys[n_keys-1].secret, 0)) 00549 { 00550 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00551 goto out; 00552 } 00553 00554 if (_dbus_string_get_length (&line) != end) 00555 { 00556 _dbus_verbose ("invalid hex encoding in keyring file\n"); 00557 _dbus_string_free (&keys[n_keys - 1].secret); 00558 n_keys -= 1; 00559 continue; 00560 } 00561 } 00562 00563 _dbus_verbose ("Successfully loaded %d existing keys\n", 00564 n_keys); 00565 00566 if (add_new) 00567 { 00568 if (!add_new_key (&keys, &n_keys, error)) 00569 { 00570 _dbus_verbose ("Failed to generate new key: %s\n", 00571 error ? "(unknown)" : error->message); 00572 goto out; 00573 } 00574 00575 _dbus_string_set_length (&contents, 0); 00576 00577 i = 0; 00578 while (i < n_keys) 00579 { 00580 if (!_dbus_string_append_int (&contents, 00581 keys[i].id)) 00582 goto nomem; 00583 00584 if (!_dbus_string_append_byte (&contents, ' ')) 00585 goto nomem; 00586 00587 if (!_dbus_string_append_int (&contents, 00588 keys[i].creation_time)) 00589 goto nomem; 00590 00591 if (!_dbus_string_append_byte (&contents, ' ')) 00592 goto nomem; 00593 00594 if (!_dbus_string_hex_encode (&keys[i].secret, 0, 00595 &contents, 00596 _dbus_string_get_length (&contents))) 00597 goto nomem; 00598 00599 if (!_dbus_string_append_byte (&contents, '\n')) 00600 goto nomem; 00601 00602 ++i; 00603 continue; 00604 00605 nomem: 00606 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00607 goto out; 00608 } 00609 00610 if (!_dbus_string_save_to_file (&contents, &keyring->filename, 00611 error)) 00612 goto out; 00613 } 00614 00615 if (keyring->keys) 00616 free_keys (keyring->keys, keyring->n_keys); 00617 keyring->keys = keys; 00618 keyring->n_keys = n_keys; 00619 keys = NULL; 00620 n_keys = 0; 00621 00622 retval = TRUE; 00623 00624 out: 00625 if (have_lock) 00626 _dbus_keyring_unlock (keyring); 00627 00628 if (! ((retval == TRUE && (error == NULL || error->name == NULL)) || 00629 (retval == FALSE && (error == NULL || error->name != NULL)))) 00630 { 00631 if (error && error->name) 00632 _dbus_verbose ("error is %s: %s\n", error->name, error->message); 00633 _dbus_warn ("returning %d but error pointer %p name %s\n", 00634 retval, error, error->name ? error->name : "(none)"); 00635 _dbus_assert_not_reached ("didn't handle errors properly"); 00636 } 00637 00638 if (keys != NULL) 00639 { 00640 i = 0; 00641 while (i < n_keys) 00642 { 00643 _dbus_string_zero (&keys[i].secret); 00644 _dbus_string_free (&keys[i].secret); 00645 ++i; 00646 } 00647 00648 dbus_free (keys); 00649 } 00650 00651 _dbus_string_free (&contents); 00652 _dbus_string_free (&line); 00653 00654 return retval; 00655 } 00656 /* end of internals */ 00658 00671 DBusKeyring * 00672 _dbus_keyring_ref (DBusKeyring *keyring) 00673 { 00674 keyring->refcount += 1; 00675 00676 return keyring; 00677 } 00678 00685 void 00686 _dbus_keyring_unref (DBusKeyring *keyring) 00687 { 00688 keyring->refcount -= 1; 00689 00690 if (keyring->refcount == 0) 00691 { 00692 _dbus_string_free (&keyring->username); 00693 _dbus_string_free (&keyring->filename); 00694 _dbus_string_free (&keyring->filename_lock); 00695 _dbus_string_free (&keyring->directory); 00696 free_keys (keyring->keys, keyring->n_keys); 00697 dbus_free (keyring); 00698 } 00699 } 00700 00711 DBusKeyring* 00712 _dbus_keyring_new_homedir (const DBusString *username, 00713 const DBusString *context, 00714 DBusError *error) 00715 { 00716 DBusString homedir; 00717 DBusKeyring *keyring; 00718 dbus_bool_t error_set; 00719 DBusString dotdir; 00720 DBusError tmp_error; 00721 00722 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00723 00724 keyring = NULL; 00725 error_set = FALSE; 00726 00727 if (!_dbus_string_init (&homedir)) 00728 { 00729 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00730 return FALSE; 00731 } 00732 00733 _dbus_string_init_const (&dotdir, ".dbus-keyrings"); 00734 00735 if (username == NULL) 00736 { 00737 const DBusString *const_homedir; 00738 00739 if (!_dbus_username_from_current_process (&username) || 00740 !_dbus_homedir_from_current_process (&const_homedir)) 00741 goto failed; 00742 00743 if (!_dbus_string_copy (const_homedir, 0, 00744 &homedir, 0)) 00745 goto failed; 00746 } 00747 else 00748 { 00749 if (!_dbus_homedir_from_username (username, &homedir)) 00750 goto failed; 00751 } 00752 00753 #ifdef DBUS_BUILD_TESTS 00754 { 00755 const char *override; 00756 00757 override = _dbus_getenv ("DBUS_TEST_HOMEDIR"); 00758 if (override != NULL && *override != '\0') 00759 { 00760 _dbus_string_set_length (&homedir, 0); 00761 if (!_dbus_string_append (&homedir, override)) 00762 _dbus_assert_not_reached ("no memory"); 00763 00764 _dbus_verbose ("Using fake homedir for testing: %s\n", 00765 _dbus_string_get_const_data (&homedir)); 00766 } 00767 else 00768 { 00769 static dbus_bool_t already_warned = FALSE; 00770 if (!already_warned) 00771 { 00772 _dbus_warn ("Using your real home directory for testing, set DBUS_TEST_HOMEDIR to avoid\n"); 00773 already_warned = TRUE; 00774 } 00775 } 00776 } 00777 #endif 00778 00779 _dbus_assert (username != NULL); 00780 00781 keyring = _dbus_keyring_new (); 00782 if (keyring == NULL) 00783 goto failed; 00784 00785 /* should have been validated already, but paranoia check here */ 00786 if (!_dbus_keyring_validate_context (context)) 00787 { 00788 error_set = TRUE; 00789 dbus_set_error_const (error, 00790 DBUS_ERROR_FAILED, 00791 "Invalid context in keyring creation"); 00792 goto failed; 00793 } 00794 00795 if (!_dbus_string_copy (username, 0, 00796 &keyring->username, 0)) 00797 goto failed; 00798 00799 if (!_dbus_string_copy (&homedir, 0, 00800 &keyring->directory, 0)) 00801 goto failed; 00802 00803 if (!_dbus_concat_dir_and_file (&keyring->directory, 00804 &dotdir)) 00805 goto failed; 00806 00807 if (!_dbus_string_copy (&keyring->directory, 0, 00808 &keyring->filename, 0)) 00809 goto failed; 00810 00811 if (!_dbus_concat_dir_and_file (&keyring->filename, 00812 context)) 00813 goto failed; 00814 00815 if (!_dbus_string_copy (&keyring->filename, 0, 00816 &keyring->filename_lock, 0)) 00817 goto failed; 00818 00819 if (!_dbus_string_append (&keyring->filename_lock, ".lock")) 00820 goto failed; 00821 00822 dbus_error_init (&tmp_error); 00823 if (!_dbus_keyring_reload (keyring, FALSE, &tmp_error)) 00824 { 00825 _dbus_verbose ("didn't load an existing keyring: %s\n", 00826 tmp_error.message); 00827 dbus_error_free (&tmp_error); 00828 } 00829 00830 /* We don't fail fatally if we can't create the directory, 00831 * but the keyring will probably always be empty 00832 * unless someone else manages to create it 00833 */ 00834 dbus_error_init (&tmp_error); 00835 if (!_dbus_create_directory (&keyring->directory, 00836 &tmp_error)) 00837 { 00838 _dbus_verbose ("Creating keyring directory: %s\n", 00839 tmp_error.message); 00840 dbus_error_free (&tmp_error); 00841 } 00842 00843 _dbus_string_free (&homedir); 00844 00845 return keyring; 00846 00847 failed: 00848 if (!error_set) 00849 dbus_set_error_const (error, 00850 DBUS_ERROR_NO_MEMORY, 00851 NULL); 00852 if (keyring) 00853 _dbus_keyring_unref (keyring); 00854 _dbus_string_free (&homedir); 00855 return FALSE; 00856 00857 } 00858 00871 dbus_bool_t 00872 _dbus_keyring_validate_context (const DBusString *context) 00873 { 00874 if (_dbus_string_get_length (context) == 0) 00875 { 00876 _dbus_verbose ("context is zero-length\n"); 00877 return FALSE; 00878 } 00879 00880 if (!_dbus_string_validate_ascii (context, 0, 00881 _dbus_string_get_length (context))) 00882 { 00883 _dbus_verbose ("context not valid ascii\n"); 00884 return FALSE; 00885 } 00886 00887 /* no directory separators */ 00888 if (_dbus_string_find (context, 0, "/", NULL)) 00889 { 00890 _dbus_verbose ("context contains a slash\n"); 00891 return FALSE; 00892 } 00893 00894 if (_dbus_string_find (context, 0, "\\", NULL)) 00895 { 00896 _dbus_verbose ("context contains a backslash\n"); 00897 return FALSE; 00898 } 00899 00900 /* prevent attempts to use dotfiles or ".." or ".lock" 00901 * all of which might allow some kind of attack 00902 */ 00903 if (_dbus_string_find (context, 0, ".", NULL)) 00904 { 00905 _dbus_verbose ("context contains a dot\n"); 00906 return FALSE; 00907 } 00908 00909 /* no spaces/tabs, those are used for separators in the protocol */ 00910 if (_dbus_string_find_blank (context, 0, NULL)) 00911 { 00912 _dbus_verbose ("context contains a blank\n"); 00913 return FALSE; 00914 } 00915 00916 if (_dbus_string_find (context, 0, "\n", NULL)) 00917 { 00918 _dbus_verbose ("context contains a newline\n"); 00919 return FALSE; 00920 } 00921 00922 if (_dbus_string_find (context, 0, "\r", NULL)) 00923 { 00924 _dbus_verbose ("context contains a carriage return\n"); 00925 return FALSE; 00926 } 00927 00928 return TRUE; 00929 } 00930 00931 static DBusKey* 00932 find_recent_key (DBusKeyring *keyring) 00933 { 00934 int i; 00935 long tv_sec, tv_usec; 00936 00937 _dbus_get_current_time (&tv_sec, &tv_usec); 00938 00939 i = 0; 00940 while (i < keyring->n_keys) 00941 { 00942 DBusKey *key = &keyring->keys[i]; 00943 00944 _dbus_verbose ("Key %d is %ld seconds old\n", 00945 i, tv_sec - key->creation_time); 00946 00947 if ((tv_sec - NEW_KEY_TIMEOUT_SECONDS) < key->creation_time) 00948 return key; 00949 00950 ++i; 00951 } 00952 00953 return NULL; 00954 } 00955 00967 int 00968 _dbus_keyring_get_best_key (DBusKeyring *keyring, 00969 DBusError *error) 00970 { 00971 DBusKey *key; 00972 00973 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00974 00975 key = find_recent_key (keyring); 00976 if (key) 00977 return key->id; 00978 00979 /* All our keys are too old, or we've never loaded the 00980 * keyring. Create a new one. 00981 */ 00982 if (!_dbus_keyring_reload (keyring, TRUE, 00983 error)) 00984 return -1; 00985 00986 key = find_recent_key (keyring); 00987 if (key) 00988 return key->id; 00989 else 00990 { 00991 dbus_set_error_const (error, 00992 DBUS_ERROR_FAILED, 00993 "No recent-enough key found in keyring, and unable to create a new key"); 00994 return -1; 00995 } 00996 } 00997 01006 dbus_bool_t 01007 _dbus_keyring_is_for_user (DBusKeyring *keyring, 01008 const DBusString *username) 01009 { 01010 return _dbus_string_equal (&keyring->username, 01011 username); 01012 } 01013 01025 dbus_bool_t 01026 _dbus_keyring_get_hex_key (DBusKeyring *keyring, 01027 int key_id, 01028 DBusString *hex_key) 01029 { 01030 DBusKey *key; 01031 01032 key = find_key_by_id (keyring->keys, 01033 keyring->n_keys, 01034 key_id); 01035 if (key == NULL) 01036 return TRUE; /* had enough memory, so TRUE */ 01037 01038 return _dbus_string_hex_encode (&key->secret, 0, 01039 hex_key, 01040 _dbus_string_get_length (hex_key)); 01041 } 01042 /* end of exposed API */ 01044 01045 #ifdef DBUS_BUILD_TESTS 01046 #include "dbus-test.h" 01047 #include <stdio.h> 01048 01049 dbus_bool_t 01050 _dbus_keyring_test (void) 01051 { 01052 DBusString context; 01053 DBusKeyring *ring1; 01054 DBusKeyring *ring2; 01055 int id; 01056 DBusError error; 01057 int i; 01058 01059 ring1 = NULL; 01060 ring2 = NULL; 01061 01062 /* Context validation */ 01063 01064 _dbus_string_init_const (&context, "foo"); 01065 _dbus_assert (_dbus_keyring_validate_context (&context)); 01066 _dbus_string_init_const (&context, "org_freedesktop_blah"); 01067 _dbus_assert (_dbus_keyring_validate_context (&context)); 01068 01069 _dbus_string_init_const (&context, ""); 01070 _dbus_assert (!_dbus_keyring_validate_context (&context)); 01071 _dbus_string_init_const (&context, ".foo"); 01072 _dbus_assert (!_dbus_keyring_validate_context (&context)); 01073 _dbus_string_init_const (&context, "bar.foo"); 01074 _dbus_assert (!_dbus_keyring_validate_context (&context)); 01075 _dbus_string_init_const (&context, "bar/foo"); 01076 _dbus_assert (!_dbus_keyring_validate_context (&context)); 01077 _dbus_string_init_const (&context, "bar\\foo"); 01078 _dbus_assert (!_dbus_keyring_validate_context (&context)); 01079 _dbus_string_init_const (&context, "foo\xfa\xf0"); 01080 _dbus_assert (!_dbus_keyring_validate_context (&context)); 01081 _dbus_string_init_const (&context, "foo\x80"); 01082 _dbus_assert (!_dbus_keyring_validate_context (&context)); 01083 _dbus_string_init_const (&context, "foo\x7f"); 01084 _dbus_assert (_dbus_keyring_validate_context (&context)); 01085 _dbus_string_init_const (&context, "foo bar"); 01086 _dbus_assert (!_dbus_keyring_validate_context (&context)); 01087 01088 if (!_dbus_string_init (&context)) 01089 _dbus_assert_not_reached ("no memory"); 01090 if (!_dbus_string_append_byte (&context, '\0')) 01091 _dbus_assert_not_reached ("no memory"); 01092 _dbus_assert (!_dbus_keyring_validate_context (&context)); 01093 _dbus_string_free (&context); 01094 01095 /* Now verify that if we create a key in keyring 1, 01096 * it is properly loaded in keyring 2 01097 */ 01098 01099 _dbus_string_init_const (&context, "org_freedesktop_dbus_testsuite"); 01100 dbus_error_init (&error); 01101 ring1 = _dbus_keyring_new_homedir (NULL, &context, 01102 &error); 01103 _dbus_assert (ring1); 01104 _dbus_assert (error.name == NULL); 01105 01106 id = _dbus_keyring_get_best_key (ring1, &error); 01107 if (id < 0) 01108 { 01109 fprintf (stderr, "Could not load keyring: %s\n", error.message); 01110 dbus_error_free (&error); 01111 goto failure; 01112 } 01113 01114 ring2 = _dbus_keyring_new_homedir (NULL, &context, &error); 01115 _dbus_assert (ring2); 01116 _dbus_assert (error.name == NULL); 01117 01118 if (ring1->n_keys != ring2->n_keys) 01119 { 01120 fprintf (stderr, "Different number of keys in keyrings\n"); 01121 goto failure; 01122 } 01123 01124 /* We guarantee we load and save keeping keys in a fixed 01125 * order 01126 */ 01127 i = 0; 01128 while (i < ring1->n_keys) 01129 { 01130 if (ring1->keys[i].id != ring2->keys[i].id) 01131 { 01132 fprintf (stderr, "Keyring 1 has first key ID %d and keyring 2 has %d\n", 01133 ring1->keys[i].id, ring2->keys[i].id); 01134 goto failure; 01135 } 01136 01137 if (ring1->keys[i].creation_time != ring2->keys[i].creation_time) 01138 { 01139 fprintf (stderr, "Keyring 1 has first key time %ld and keyring 2 has %ld\n", 01140 ring1->keys[i].creation_time, ring2->keys[i].creation_time); 01141 goto failure; 01142 } 01143 01144 if (!_dbus_string_equal (&ring1->keys[i].secret, 01145 &ring2->keys[i].secret)) 01146 { 01147 fprintf (stderr, "Keyrings 1 and 2 have different secrets for same ID/timestamp\n"); 01148 goto failure; 01149 } 01150 01151 ++i; 01152 } 01153 01154 printf (" %d keys in test\n", ring1->n_keys); 01155 01156 /* Test ref/unref */ 01157 _dbus_keyring_ref (ring1); 01158 _dbus_keyring_ref (ring2); 01159 _dbus_keyring_unref (ring1); 01160 _dbus_keyring_unref (ring2); 01161 01162 01163 /* really unref */ 01164 _dbus_keyring_unref (ring1); 01165 _dbus_keyring_unref (ring2); 01166 01167 return TRUE; 01168 01169 failure: 01170 if (ring1) 01171 _dbus_keyring_unref (ring1); 01172 if (ring2) 01173 _dbus_keyring_unref (ring2); 01174 01175 return FALSE; 01176 } 01177 01178 #endif /* DBUS_BUILD_TESTS */ 01179

Generated on Mon Jun 27 07:48:21 2005 for D-BUS by doxygen 1.3.7