* reverted global rename of function _dbus_username_from_current_process.
[platform/upstream/dbus.git] / dbus / dbus-keyring.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-keyring.c Store secret cookies in your homedir
3  *
4  * Copyright (C) 2003, 2004  Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
7  * 
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.
12  *
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.
17  * 
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
21  *
22  */
23
24 #include "dbus-keyring.h"
25 #include "dbus-protocol.h"
26 #include <dbus/dbus-string.h>
27 #include <dbus/dbus-list.h>
28 #include <dbus/dbus-sysdeps.h>
29
30 /**
31  * @defgroup DBusKeyring keyring class
32  * @ingroup  DBusInternals
33  * @brief DBusKeyring data structure
34  *
35  * Types and functions related to DBusKeyring. DBusKeyring is intended
36  * to manage cookies used to authenticate clients to servers.  This is
37  * essentially the "verify that client can read the user's homedir"
38  * authentication mechanism.  Both client and server must have access
39  * to the homedir.
40  *
41  * The secret keys are not kept in locked memory, and are written to a
42  * file in the user's homedir. However they are transient (only used
43  * by a single server instance for a fixed period of time, then
44  * discarded). Also, the keys are not sent over the wire.
45  *
46  * @todo there's a memory leak on some codepath in here, I saw it once
47  * when running make check - probably some specific initial cookies
48  * present in the cookie file, then depending on what we do with them.
49  */
50
51 /**
52  * @defgroup DBusKeyringInternals DBusKeyring implementation details
53  * @ingroup  DBusInternals
54  * @brief DBusKeyring implementation details
55  *
56  * The guts of DBusKeyring.
57  *
58  * @{
59  */
60
61 /** The maximum age of a key before we create a new key to use in
62  * challenges.  This isn't super-reliably enforced, since system
63  * clocks can change or be wrong, but we make a best effort to only
64  * use keys for a short time.
65  */
66 #define NEW_KEY_TIMEOUT_SECONDS     (60*5)
67 /**
68  * The time after which we drop a key from the secrets file.
69  * The EXPIRE_KEYS_TIMEOUT_SECONDS - NEW_KEY_TIMEOUT_SECONDS is the minimum
70  * time window a client has to complete authentication.
71  */
72 #define EXPIRE_KEYS_TIMEOUT_SECONDS (NEW_KEY_TIMEOUT_SECONDS + (60*2))
73 /**
74  * The maximum amount of time a key can be in the future.
75  */
76 #define MAX_TIME_TRAVEL_SECONDS (60*5)
77
78 /**
79  * Maximum number of keys in the keyring before
80  * we just ignore the rest
81  */
82 #ifdef DBUS_BUILD_TESTS
83 #define MAX_KEYS_IN_FILE 10
84 #else
85 #define MAX_KEYS_IN_FILE 256
86 #endif
87
88 /**
89  * A single key from the cookie file
90  */
91 typedef struct
92 {
93   dbus_int32_t id; /**< identifier used to refer to the key */
94
95   long creation_time; /**< when the key was generated,
96                        *   as unix timestamp. signed long
97                        *   matches struct timeval.
98                        */
99   
100   DBusString secret; /**< the actual key */
101
102 } DBusKey;
103
104 /**
105  * @brief Internals of DBusKeyring.
106  * 
107  * DBusKeyring internals. DBusKeyring is an opaque object, it must be
108  * used via accessor functions.
109  */
110 struct DBusKeyring
111 {
112   int refcount;             /**< Reference count */
113   DBusString username;      /**< Username keyring is for */
114   DBusString directory;     /**< Directory the below two items are inside */
115   DBusString filename;      /**< Keyring filename */
116   DBusString filename_lock; /**< Name of lockfile */
117   DBusKey *keys; /**< Keys loaded from the file */
118   int n_keys;    /**< Number of keys */
119 };
120
121 static DBusKeyring*
122 _dbus_keyring_new (void)
123 {
124   DBusKeyring *keyring;
125
126   keyring = dbus_new0 (DBusKeyring, 1);
127   if (keyring == NULL)
128     goto out_0;
129   
130   if (!_dbus_string_init (&keyring->directory))
131     goto out_1;
132
133   if (!_dbus_string_init (&keyring->filename))
134     goto out_2;
135
136   if (!_dbus_string_init (&keyring->filename_lock))
137     goto out_3;
138
139   if (!_dbus_string_init (&keyring->username))
140     goto out_4;
141   
142   keyring->refcount = 1;
143   keyring->keys = NULL;
144   keyring->n_keys = 0;
145
146   return keyring;
147
148  out_4:
149   _dbus_string_free (&keyring->filename_lock);
150  out_3:
151   _dbus_string_free (&keyring->filename);
152  out_2:
153   _dbus_string_free (&keyring->directory);
154  out_1:
155   dbus_free (keyring);
156  out_0:
157   return NULL;
158 }
159
160 static void
161 free_keys (DBusKey *keys,
162            int      n_keys)
163 {
164   int i;
165
166   /* should be safe for args NULL, 0 */
167   
168   i = 0;
169   while (i < n_keys)
170     {
171       _dbus_string_free (&keys[i].secret);
172       ++i;
173     }
174
175   dbus_free (keys);
176 }
177
178 /* Our locking scheme is highly unreliable.  However, there is
179  * unfortunately no reliable locking scheme in user home directories;
180  * between bugs in Linux NFS, people using Tru64 or other total crap
181  * NFS, AFS, random-file-system-of-the-week, and so forth, fcntl() in
182  * homedirs simply generates tons of bug reports. This has been
183  * learned through hard experience with GConf, unfortunately.
184  *
185  * This bad hack might work better for the kind of lock we have here,
186  * which we don't expect to hold for any length of time.  Crashing
187  * while we hold it should be unlikely, and timing out such that we
188  * delete a stale lock should also be unlikely except when the
189  * filesystem is running really slowly.  Stuff might break in corner
190  * cases but as long as it's not a security-level breakage it should
191  * be OK.
192  */
193
194 /** Maximum number of timeouts waiting for lock before we decide it's stale */
195 #define MAX_LOCK_TIMEOUTS 32
196 /** Length of each timeout while waiting for a lock */
197 #define LOCK_TIMEOUT_MILLISECONDS 250
198
199 static dbus_bool_t
200 _dbus_keyring_lock (DBusKeyring *keyring)
201 {
202   int n_timeouts;
203   
204   n_timeouts = 0;
205   while (n_timeouts < MAX_LOCK_TIMEOUTS)
206     {
207       DBusError error;
208
209       dbus_error_init (&error);
210       if (_dbus_create_file_exclusively (&keyring->filename_lock,
211                                          &error))
212         break;
213
214       _dbus_verbose ("Did not get lock file, sleeping %d milliseconds (%s)\n",
215                      LOCK_TIMEOUT_MILLISECONDS, error.message);
216       dbus_error_free (&error);
217
218       _dbus_sleep_milliseconds (LOCK_TIMEOUT_MILLISECONDS);
219       
220       ++n_timeouts;
221     }
222
223   if (n_timeouts == MAX_LOCK_TIMEOUTS)
224     {
225       DBusError error;
226       
227       _dbus_verbose ("Lock file timed out %d times, assuming stale\n",
228                      n_timeouts);
229
230       dbus_error_init (&error);
231
232       if (!_dbus_delete_file (&keyring->filename_lock, &error))
233         {
234           _dbus_verbose ("Couldn't delete old lock file: %s\n",
235                          error.message);
236           dbus_error_free (&error);
237           return FALSE;
238         }
239
240       if (!_dbus_create_file_exclusively (&keyring->filename_lock,
241                                           &error))
242         {
243           _dbus_verbose ("Couldn't create lock file after deleting stale one: %s\n",
244                          error.message);
245           dbus_error_free (&error);
246           return FALSE;
247         }
248     }
249   
250   return TRUE;
251 }
252
253 static void
254 _dbus_keyring_unlock (DBusKeyring *keyring)
255 {
256   DBusError error;
257   dbus_error_init (&error);
258   if (!_dbus_delete_file (&keyring->filename_lock, &error))
259     {
260       _dbus_warn ("Failed to delete lock file: %s\n",
261                   error.message);
262       dbus_error_free (&error);
263     }
264 }
265
266 static DBusKey*
267 find_key_by_id (DBusKey *keys,
268                 int      n_keys,
269                 int      id)
270 {
271   int i;
272
273   i = 0;
274   while (i < n_keys)
275     {
276       if (keys[i].id == id)
277         return &keys[i];
278       
279       ++i;
280     }
281
282   return NULL;
283 }
284
285 static dbus_bool_t
286 add_new_key (DBusKey  **keys_p,
287              int       *n_keys_p,
288              DBusError *error)
289 {
290   DBusKey *new;
291   DBusString bytes;
292   int id;
293   long timestamp;
294   const unsigned char *s;
295   dbus_bool_t retval;
296   DBusKey *keys;
297   int n_keys;
298
299   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
300   
301   if (!_dbus_string_init (&bytes))
302     {
303       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
304       return FALSE;
305     }
306
307   keys = *keys_p;
308   n_keys = *n_keys_p;
309   retval = FALSE;
310       
311   /* Generate an integer ID and then the actual key. */
312  retry:
313       
314   if (!_dbus_generate_random_bytes (&bytes, 4))
315     {
316       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
317       goto out;
318     }
319
320   s = (const unsigned char*) _dbus_string_get_const_data (&bytes);
321       
322   id = s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24);
323   if (id < 0)
324     id = - id;
325   _dbus_assert (id >= 0);
326
327   if (find_key_by_id (keys, n_keys, id) != NULL)
328     {
329       _dbus_string_set_length (&bytes, 0);
330       _dbus_verbose ("Key ID %d already existed, trying another one\n",
331                      id);
332       goto retry;
333     }
334
335   _dbus_verbose ("Creating key with ID %d\n", id);
336       
337 #define KEY_LENGTH_BYTES 24
338   _dbus_string_set_length (&bytes, 0);
339   if (!_dbus_generate_random_bytes (&bytes, KEY_LENGTH_BYTES))
340     {
341       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
342       goto out;
343     }
344
345   new = dbus_realloc (keys, sizeof (DBusKey) * (n_keys + 1));
346   if (new == NULL)
347     {
348       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
349       goto out;
350     }
351
352   keys = new;
353   *keys_p = keys; /* otherwise *keys_p ends up invalid */
354   n_keys += 1;
355
356   if (!_dbus_string_init (&keys[n_keys-1].secret))
357     {
358       n_keys -= 1; /* we don't want to free the one we didn't init */
359       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
360       goto out;
361     }
362
363   _dbus_get_current_time (&timestamp, NULL);
364       
365   keys[n_keys-1].id = id;
366   keys[n_keys-1].creation_time = timestamp;
367   if (!_dbus_string_move (&bytes, 0,
368                           &keys[n_keys-1].secret,
369                           0))
370     {
371       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
372       _dbus_string_free (&keys[n_keys-1].secret);
373       n_keys -= 1;
374       goto out;
375     }
376   
377   retval = TRUE;
378   
379  out:
380   *n_keys_p = n_keys;
381   
382   _dbus_string_free (&bytes);
383   return retval;
384 }
385
386 /**
387  * Reloads the keyring file, optionally adds one new key to the file,
388  * removes all expired keys from the file iff a key was added, then
389  * resaves the file.  Stores the keys from the file in keyring->keys.
390  * Note that the file is only resaved (written to) if a key is added,
391  * this means that only servers ever write to the file and need to
392  * lock it, which avoids a lot of lock contention at login time and
393  * such.
394  *
395  * @param keyring the keyring
396  * @param add_new #TRUE to add a new key to the file, expire keys, and resave
397  * @param error return location for errors
398  * @returns #FALSE on failure
399  */
400 static dbus_bool_t
401 _dbus_keyring_reload (DBusKeyring *keyring,
402                       dbus_bool_t  add_new,
403                       DBusError   *error)
404 {
405   DBusString contents;
406   DBusString line;
407   dbus_bool_t retval;
408   dbus_bool_t have_lock;
409   DBusKey *keys;
410   int n_keys;
411   int i;
412   long now;
413   DBusError tmp_error;
414
415   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
416   
417   if (!_dbus_check_dir_is_private_to_user (&keyring->directory, error))
418     return FALSE;
419     
420   if (!_dbus_string_init (&contents))
421     {
422       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
423       return FALSE;
424     }
425
426   if (!_dbus_string_init (&line))
427     {
428       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
429       _dbus_string_free (&contents);
430       return FALSE;
431     }
432    
433   keys = NULL;
434   n_keys = 0;
435   retval = FALSE;
436   have_lock = FALSE;
437
438   _dbus_get_current_time (&now, NULL);
439   
440   if (add_new)
441     {
442       if (!_dbus_keyring_lock (keyring))
443         {
444           dbus_set_error (error, DBUS_ERROR_FAILED,
445                           "Could not lock keyring file to add to it");
446           goto out;
447         }
448
449       have_lock = TRUE;
450     }
451
452   dbus_error_init (&tmp_error);
453   if (!_dbus_file_get_contents (&contents, 
454                                 &keyring->filename,
455                                 &tmp_error))
456     {
457       _dbus_verbose ("Failed to load keyring file: %s\n",
458                      tmp_error.message);
459       /* continue with empty keyring file, so we recreate it */
460       dbus_error_free (&tmp_error);
461     }
462
463   if (!_dbus_string_validate_ascii (&contents, 0,
464                                     _dbus_string_get_length (&contents)))
465     {
466       _dbus_warn ("Secret keyring file contains non-ASCII! Ignoring existing contents\n");
467       _dbus_string_set_length (&contents, 0);
468     }
469
470   /* FIXME this is badly inefficient for large keyring files
471    * (not that large keyring files exist outside of test suites)
472    */
473   while (_dbus_string_pop_line (&contents, &line))
474     {
475       int next;
476       long val;
477       int id;
478       long timestamp;
479       int len;
480       int end;
481       DBusKey *new;
482
483       /* Don't load more than the max. */
484       if (n_keys >= (add_new ? MAX_KEYS_IN_FILE - 1 : MAX_KEYS_IN_FILE))
485         break;
486       
487       next = 0;
488       if (!_dbus_string_parse_int (&line, 0, &val, &next))
489         {
490           _dbus_verbose ("could not parse secret key ID at start of line\n");
491           continue;
492         }
493
494       if (val > _DBUS_INT32_MAX || val < 0)
495         {
496           _dbus_verbose ("invalid secret key ID at start of line\n");
497           continue;
498         }
499       
500       id = val;
501
502       _dbus_string_skip_blank (&line, next, &next);
503       
504       if (!_dbus_string_parse_int (&line, next, &timestamp, &next))
505         {
506           _dbus_verbose ("could not parse secret key timestamp\n");
507           continue;
508         }
509
510       if (timestamp < 0 ||
511           (now + MAX_TIME_TRAVEL_SECONDS) < timestamp ||
512           (now - EXPIRE_KEYS_TIMEOUT_SECONDS) > timestamp)
513         {
514           _dbus_verbose ("dropping/ignoring %ld-seconds old key with timestamp %ld as current time is %ld\n",
515                          now - timestamp, timestamp, now);
516           continue;
517         }
518       
519       _dbus_string_skip_blank (&line, next, &next);
520
521       len = _dbus_string_get_length (&line);
522
523       if ((len - next) == 0)
524         {
525           _dbus_verbose ("no secret key after ID and timestamp\n");
526           continue;
527         }
528       
529       /* We have all three parts */
530       new = dbus_realloc (keys, sizeof (DBusKey) * (n_keys + 1));
531       if (new == NULL)
532         {
533           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
534           goto out;
535         }
536
537       keys = new;
538       n_keys += 1;
539
540       if (!_dbus_string_init (&keys[n_keys-1].secret))
541         {
542           n_keys -= 1; /* we don't want to free the one we didn't init */
543           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
544           goto out;
545         }
546       
547       keys[n_keys-1].id = id;
548       keys[n_keys-1].creation_time = timestamp;
549       if (!_dbus_string_hex_decode (&line, next, &end,
550                                     &keys[n_keys-1].secret, 0))
551         {
552           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
553           goto out;
554         }
555
556       if (_dbus_string_get_length (&line) != end)
557         {
558           _dbus_verbose ("invalid hex encoding in keyring file\n");
559           _dbus_string_free (&keys[n_keys - 1].secret);
560           n_keys -= 1;
561           continue;
562         }
563     }
564
565   _dbus_verbose ("Successfully loaded %d existing keys\n",
566                  n_keys);
567
568   if (add_new)
569     {
570       if (!add_new_key (&keys, &n_keys, error))
571         {
572           _dbus_verbose ("Failed to generate new key: %s\n",
573                          error ? error->message : "(unknown)");
574           goto out;
575         }
576
577       _dbus_string_set_length (&contents, 0);
578
579       i = 0;
580       while (i < n_keys)
581         {
582           if (!_dbus_string_append_int (&contents,
583                                         keys[i].id))
584             goto nomem;
585
586           if (!_dbus_string_append_byte (&contents, ' '))
587             goto nomem;
588
589           if (!_dbus_string_append_int (&contents,
590                                         keys[i].creation_time))
591             goto nomem;
592
593           if (!_dbus_string_append_byte (&contents, ' '))
594             goto nomem;
595
596           if (!_dbus_string_hex_encode (&keys[i].secret, 0,
597                                         &contents,
598                                         _dbus_string_get_length (&contents)))
599             goto nomem;
600
601           if (!_dbus_string_append_byte (&contents, '\n'))
602             goto nomem;          
603           
604           ++i;
605           continue;
606
607         nomem:
608           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
609           goto out;
610         }
611       
612       if (!_dbus_string_save_to_file (&contents, &keyring->filename,
613                                       error))
614         goto out;
615     }
616
617   if (keyring->keys)
618     free_keys (keyring->keys, keyring->n_keys);
619   keyring->keys = keys;
620   keyring->n_keys = n_keys;
621   keys = NULL;
622   n_keys = 0;
623   
624   retval = TRUE;  
625   
626  out:
627   if (have_lock)
628     _dbus_keyring_unlock (keyring);
629   
630   if (! ((retval == TRUE && (error == NULL || error->name == NULL)) ||
631          (retval == FALSE && (error == NULL || error->name != NULL))))
632     {
633       if (error && error->name)
634         _dbus_verbose ("error is %s: %s\n", error->name, error->message);
635       _dbus_warn ("returning %d but error pointer %p name %s\n",
636                   retval, error, error->name ? error->name : "(none)");
637       _dbus_assert_not_reached ("didn't handle errors properly");
638     }
639   
640   if (keys != NULL)
641     {
642       i = 0;
643       while (i < n_keys)
644         {
645           _dbus_string_zero (&keys[i].secret);
646           _dbus_string_free (&keys[i].secret);
647           ++i;
648         }
649
650       dbus_free (keys);
651     }
652   
653   _dbus_string_free (&contents);
654   _dbus_string_free (&line);
655
656   return retval;
657 }
658
659 /** @} */ /* end of internals */
660
661 /**
662  * @addtogroup DBusKeyring
663  *
664  * @{
665  */
666
667 /**
668  * Increments reference count of the keyring
669  *
670  * @param keyring the keyring
671  * @returns the keyring
672  */
673 DBusKeyring *
674 _dbus_keyring_ref (DBusKeyring *keyring)
675 {
676   keyring->refcount += 1;
677
678   return keyring;
679 }
680
681 /**
682  * Decrements refcount and finalizes if it reaches
683  * zero.
684  *
685  * @param keyring the keyring
686  */
687 void
688 _dbus_keyring_unref (DBusKeyring *keyring)
689 {
690   keyring->refcount -= 1;
691
692   if (keyring->refcount == 0)
693     {
694       _dbus_string_free (&keyring->username);
695       _dbus_string_free (&keyring->filename);
696       _dbus_string_free (&keyring->filename_lock);
697       _dbus_string_free (&keyring->directory);
698       free_keys (keyring->keys, keyring->n_keys);
699       dbus_free (keyring);      
700     }
701 }
702
703 /**
704  * Creates a new keyring that lives in the ~/.dbus-keyrings
705  * directory of the given user. If the username is #NULL,
706  * uses the user owning the current process.
707  *
708  * @param username username to get keyring for, or #NULL
709  * @param context which keyring to get
710  * @param error return location for errors
711  * @returns the keyring or #NULL on error
712  */
713 DBusKeyring*
714 _dbus_keyring_new_homedir (const DBusString *username,
715                            const DBusString *context,
716                            DBusError        *error)
717 {
718   DBusString homedir;
719   DBusKeyring *keyring;
720   dbus_bool_t error_set;
721   DBusString dotdir;
722   DBusError tmp_error;
723
724   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
725   
726   keyring = NULL;
727   error_set = FALSE;
728   
729   if (!_dbus_string_init (&homedir))
730     {
731       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
732       return NULL;
733     }
734
735   _dbus_string_init_const (&dotdir, ".dbus-keyrings");
736   
737   if (username == NULL)
738     {
739       const DBusString *const_homedir;
740
741       if (!_dbus_username_from_current_process (&username) ||
742           !_dbus_homedir_from_current_process (&const_homedir))
743         goto failed;
744
745       if (!_dbus_string_copy (const_homedir, 0,
746                               &homedir, 0))
747         goto failed;
748     }
749   else
750     {
751       if (!_dbus_homedir_from_username (username, &homedir))
752         goto failed;
753     }
754
755 #ifdef DBUS_BUILD_TESTS
756  {
757    const char *override;
758
759    override = _dbus_getenv ("DBUS_TEST_HOMEDIR");
760    if (override != NULL && *override != '\0')
761      {
762        _dbus_string_set_length (&homedir, 0);
763        if (!_dbus_string_append (&homedir, override))
764          goto failed;
765
766        _dbus_verbose ("Using fake homedir for testing: %s\n",
767                       _dbus_string_get_const_data (&homedir));
768      }
769    else
770      {
771        static dbus_bool_t already_warned = FALSE;
772        if (!already_warned)
773          {
774            _dbus_warn ("Using your real home directory for testing, set DBUS_TEST_HOMEDIR to avoid\n");
775            already_warned = TRUE;
776          }
777      }
778  }
779 #endif
780   
781   _dbus_assert (username != NULL);    
782   
783   keyring = _dbus_keyring_new ();
784   if (keyring == NULL)
785     goto failed;
786
787   /* should have been validated already, but paranoia check here */
788   if (!_dbus_keyring_validate_context (context))
789     {
790       error_set = TRUE;
791       dbus_set_error_const (error,
792                             DBUS_ERROR_FAILED,
793                             "Invalid context in keyring creation");
794       goto failed;
795     }
796
797   if (!_dbus_string_copy (username, 0,
798                           &keyring->username, 0))
799     goto failed;
800   
801   if (!_dbus_string_copy (&homedir, 0,
802                           &keyring->directory, 0))
803     goto failed;
804   
805   if (!_dbus_concat_dir_and_file (&keyring->directory,
806                                   &dotdir))
807     goto failed;
808
809   if (!_dbus_string_copy (&keyring->directory, 0,
810                           &keyring->filename, 0))
811     goto failed;
812
813   if (!_dbus_concat_dir_and_file (&keyring->filename,
814                                   context))
815     goto failed;
816
817   if (!_dbus_string_copy (&keyring->filename, 0,
818                           &keyring->filename_lock, 0))
819     goto failed;
820
821   if (!_dbus_string_append (&keyring->filename_lock, ".lock"))
822     goto failed;
823
824   dbus_error_init (&tmp_error);
825   if (!_dbus_keyring_reload (keyring, FALSE, &tmp_error))
826     {
827       _dbus_verbose ("didn't load an existing keyring: %s\n",
828                      tmp_error.message);
829       dbus_error_free (&tmp_error);
830     }
831   
832   /* We don't fail fatally if we can't create the directory,
833    * but the keyring will probably always be empty
834    * unless someone else manages to create it
835    */
836   dbus_error_init (&tmp_error);
837   if (!_dbus_create_directory (&keyring->directory,
838                                &tmp_error))
839     {
840       _dbus_verbose ("Creating keyring directory: %s\n",
841                      tmp_error.message);
842       dbus_error_free (&tmp_error);
843     }
844
845   _dbus_string_free (&homedir);
846   
847   return keyring;
848   
849  failed:
850   if (!error_set)
851     dbus_set_error_const (error,
852                           DBUS_ERROR_NO_MEMORY,
853                           NULL);
854   if (keyring)
855     _dbus_keyring_unref (keyring);
856   _dbus_string_free (&homedir);
857   return NULL;
858
859 }
860
861 /**
862  * Checks whether the context is a valid context.
863  * Contexts that might cause confusion when used
864  * in filenames are not allowed (contexts can't
865  * start with a dot or contain dir separators).
866  *
867  * @todo this is the most inefficient implementation
868  * imaginable.
869  *
870  * @param context the context
871  * @returns #TRUE if valid
872  */
873 dbus_bool_t
874 _dbus_keyring_validate_context (const DBusString *context)
875 {
876   if (_dbus_string_get_length (context) == 0)
877     {
878       _dbus_verbose ("context is zero-length\n");
879       return FALSE;
880     }
881
882   if (!_dbus_string_validate_ascii (context, 0,
883                                     _dbus_string_get_length (context)))
884     {
885       _dbus_verbose ("context not valid ascii\n");
886       return FALSE;
887     }
888   
889   /* no directory separators */  
890   if (_dbus_string_find (context, 0, "/", NULL))
891     {
892       _dbus_verbose ("context contains a slash\n");
893       return FALSE;
894     }
895
896   if (_dbus_string_find (context, 0, "\\", NULL))
897     {
898       _dbus_verbose ("context contains a backslash\n");
899       return FALSE;
900     }
901
902   /* prevent attempts to use dotfiles or ".." or ".lock"
903    * all of which might allow some kind of attack
904    */
905   if (_dbus_string_find (context, 0, ".", NULL))
906     {
907       _dbus_verbose ("context contains a dot\n");
908       return FALSE;
909     }
910
911   /* no spaces/tabs, those are used for separators in the protocol */
912   if (_dbus_string_find_blank (context, 0, NULL))
913     {
914       _dbus_verbose ("context contains a blank\n");
915       return FALSE;
916     }
917
918   if (_dbus_string_find (context, 0, "\n", NULL))
919     {
920       _dbus_verbose ("context contains a newline\n");
921       return FALSE;
922     }
923
924   if (_dbus_string_find (context, 0, "\r", NULL))
925     {
926       _dbus_verbose ("context contains a carriage return\n");
927       return FALSE;
928     }
929   
930   return TRUE;
931 }
932
933 static DBusKey*
934 find_recent_key (DBusKeyring *keyring)
935 {
936   int i;
937   long tv_sec, tv_usec;
938
939   _dbus_get_current_time (&tv_sec, &tv_usec);
940   
941   i = 0;
942   while (i < keyring->n_keys)
943     {
944       DBusKey *key = &keyring->keys[i];
945
946       _dbus_verbose ("Key %d is %ld seconds old\n",
947                      i, tv_sec - key->creation_time);
948       
949       if ((tv_sec - NEW_KEY_TIMEOUT_SECONDS) < key->creation_time)
950         return key;
951       
952       ++i;
953     }
954
955   return NULL;
956 }
957
958 /**
959  * Gets a recent key to use for authentication.
960  * If no recent key exists, creates one. Returns
961  * the key ID. If a key can't be written to the keyring
962  * file so no recent key can be created, returns -1.
963  * All valid keys are > 0.
964  *
965  * @param keyring the keyring
966  * @param error error on failure
967  * @returns key ID to use for auth, or -1 on failure
968  */
969 int
970 _dbus_keyring_get_best_key (DBusKeyring  *keyring,
971                             DBusError    *error)
972 {
973   DBusKey *key;
974
975   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
976   
977   key = find_recent_key (keyring);
978   if (key)
979     return key->id;
980
981   /* All our keys are too old, or we've never loaded the
982    * keyring. Create a new one.
983    */
984   if (!_dbus_keyring_reload (keyring, TRUE,
985                              error))
986     return -1;
987
988   key = find_recent_key (keyring);
989   if (key)
990     return key->id;
991   else
992     {
993       dbus_set_error_const (error,
994                             DBUS_ERROR_FAILED,
995                             "No recent-enough key found in keyring, and unable to create a new key");
996       return -1;
997     }
998 }
999
1000 /**
1001  * Checks whether the keyring is for the given username.
1002  *
1003  * @param keyring the keyring
1004  * @param username the username to check
1005  *
1006  * @returns #TRUE if the keyring belongs to the given user
1007  */
1008 dbus_bool_t
1009 _dbus_keyring_is_for_user (DBusKeyring       *keyring,
1010                            const DBusString  *username)
1011 {
1012   return _dbus_string_equal (&keyring->username,
1013                              username);
1014 }
1015
1016 /**
1017  * Gets the hex-encoded secret key for the given ID.
1018  * Returns #FALSE if not enough memory. Returns #TRUE
1019  * but empty key on any other error such as unknown
1020  * key ID.
1021  *
1022  * @param keyring the keyring
1023  * @param key_id the key ID
1024  * @param hex_key string to append hex-encoded key to
1025  * @returns #TRUE if we had enough memory
1026  */
1027 dbus_bool_t
1028 _dbus_keyring_get_hex_key (DBusKeyring       *keyring,
1029                            int                key_id,
1030                            DBusString        *hex_key)
1031 {
1032   DBusKey *key;
1033
1034   key = find_key_by_id (keyring->keys,
1035                         keyring->n_keys,
1036                         key_id);
1037   if (key == NULL)
1038     return TRUE; /* had enough memory, so TRUE */
1039
1040   return _dbus_string_hex_encode (&key->secret, 0,
1041                                   hex_key,
1042                                   _dbus_string_get_length (hex_key));
1043 }
1044
1045 /** @} */ /* end of exposed API */
1046
1047 #ifdef DBUS_BUILD_TESTS
1048 #include "dbus-test.h"
1049 #include <stdio.h>
1050
1051 dbus_bool_t
1052 _dbus_keyring_test (void)
1053 {
1054   DBusString context;
1055   DBusKeyring *ring1;
1056   DBusKeyring *ring2;
1057   int id;
1058   DBusError error;
1059   int i;
1060
1061   ring1 = NULL;
1062   ring2 = NULL;
1063   
1064   /* Context validation */
1065   
1066   _dbus_string_init_const (&context, "foo");
1067   _dbus_assert (_dbus_keyring_validate_context (&context));
1068   _dbus_string_init_const (&context, "org_freedesktop_blah");
1069   _dbus_assert (_dbus_keyring_validate_context (&context));
1070   
1071   _dbus_string_init_const (&context, "");
1072   _dbus_assert (!_dbus_keyring_validate_context (&context));
1073   _dbus_string_init_const (&context, ".foo");
1074   _dbus_assert (!_dbus_keyring_validate_context (&context));
1075   _dbus_string_init_const (&context, "bar.foo");
1076   _dbus_assert (!_dbus_keyring_validate_context (&context));
1077   _dbus_string_init_const (&context, "bar/foo");
1078   _dbus_assert (!_dbus_keyring_validate_context (&context));
1079   _dbus_string_init_const (&context, "bar\\foo");
1080   _dbus_assert (!_dbus_keyring_validate_context (&context));
1081   _dbus_string_init_const (&context, "foo\xfa\xf0");
1082   _dbus_assert (!_dbus_keyring_validate_context (&context));
1083   _dbus_string_init_const (&context, "foo\x80");
1084   _dbus_assert (!_dbus_keyring_validate_context (&context));
1085   _dbus_string_init_const (&context, "foo\x7f");
1086   _dbus_assert (_dbus_keyring_validate_context (&context));
1087   _dbus_string_init_const (&context, "foo bar");
1088   _dbus_assert (!_dbus_keyring_validate_context (&context));
1089   
1090   if (!_dbus_string_init (&context))
1091     _dbus_assert_not_reached ("no memory");
1092   if (!_dbus_string_append_byte (&context, '\0'))
1093     _dbus_assert_not_reached ("no memory");
1094   _dbus_assert (!_dbus_keyring_validate_context (&context));
1095   _dbus_string_free (&context);
1096
1097   /* Now verify that if we create a key in keyring 1,
1098    * it is properly loaded in keyring 2
1099    */
1100
1101   _dbus_string_init_const (&context, "org_freedesktop_dbus_testsuite");
1102   dbus_error_init (&error);
1103   ring1 = _dbus_keyring_new_homedir (NULL, &context,
1104                                      &error);
1105   _dbus_assert (ring1);
1106   _dbus_assert (error.name == NULL);
1107
1108   id = _dbus_keyring_get_best_key (ring1, &error);
1109   if (id < 0)
1110     {
1111       fprintf (stderr, "Could not load keyring: %s\n", error.message);
1112       dbus_error_free (&error);
1113       goto failure;
1114     }
1115
1116   ring2 = _dbus_keyring_new_homedir (NULL, &context, &error);
1117   _dbus_assert (ring2);
1118   _dbus_assert (error.name == NULL);
1119   
1120   if (ring1->n_keys != ring2->n_keys)
1121     {
1122       fprintf (stderr, "Different number of keys in keyrings\n");
1123       goto failure;
1124     }
1125
1126   /* We guarantee we load and save keeping keys in a fixed
1127    * order
1128    */
1129   i = 0;
1130   while (i < ring1->n_keys)
1131     {
1132       if (ring1->keys[i].id != ring2->keys[i].id)
1133         {
1134           fprintf (stderr, "Keyring 1 has first key ID %d and keyring 2 has %d\n",
1135                    ring1->keys[i].id, ring2->keys[i].id);
1136           goto failure;
1137         }      
1138
1139       if (ring1->keys[i].creation_time != ring2->keys[i].creation_time)
1140         {
1141           fprintf (stderr, "Keyring 1 has first key time %ld and keyring 2 has %ld\n",
1142                    ring1->keys[i].creation_time, ring2->keys[i].creation_time);
1143           goto failure;
1144         }
1145
1146       if (!_dbus_string_equal (&ring1->keys[i].secret,
1147                                &ring2->keys[i].secret))
1148         {
1149           fprintf (stderr, "Keyrings 1 and 2 have different secrets for same ID/timestamp\n");
1150           goto failure;
1151         }
1152       
1153       ++i;
1154     }
1155
1156   printf (" %d keys in test\n", ring1->n_keys);
1157
1158   /* Test ref/unref */
1159   _dbus_keyring_ref (ring1);
1160   _dbus_keyring_ref (ring2);
1161   _dbus_keyring_unref (ring1);
1162   _dbus_keyring_unref (ring2);
1163
1164
1165   /* really unref */
1166   _dbus_keyring_unref (ring1);
1167   _dbus_keyring_unref (ring2);
1168   
1169   return TRUE;
1170
1171  failure:
1172   if (ring1)
1173     _dbus_keyring_unref (ring1);
1174   if (ring2)
1175     _dbus_keyring_unref (ring2);
1176
1177   return FALSE;
1178 }
1179
1180 #endif /* DBUS_BUILD_TESTS */
1181