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