* global rename of function dbus_username_from_current_process to _dbus_append_desire...
[platform/upstream/dbus.git] / dbus / dbus-userdb.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-userdb.c User database abstraction
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 #define DBUS_USERDB_INCLUDES_PRIVATE 1
24 #include "dbus-userdb.h"
25 #include "dbus-hash.h"
26 #include "dbus-test.h"
27 #include "dbus-internals.h"
28 #include "dbus-protocol.h"
29 #include "dbus-credentials.h"
30 #include <string.h>
31
32 /**
33  * @addtogroup DBusInternalsUtils
34  * @{
35  */
36
37 /**
38  * Frees the given #DBusUserInfo's members with _dbus_user_info_free()
39  * and also calls dbus_free() on the block itself
40  *
41  * @param info the info
42  */
43 void
44 _dbus_user_info_free_allocated (DBusUserInfo *info)
45 {
46   if (info == NULL) /* hash table will pass NULL */
47     return;
48
49   _dbus_user_info_free (info);
50   dbus_free (info);
51 }
52
53 /**
54  * Frees the given #DBusGroupInfo's members with _dbus_group_info_free()
55  * and also calls dbus_free() on the block itself
56  *
57  * @param info the info
58  */
59 void
60 _dbus_group_info_free_allocated (DBusGroupInfo *info)
61 {
62   if (info == NULL) /* hash table will pass NULL */
63     return;
64
65   _dbus_group_info_free (info);
66   dbus_free (info);
67 }
68
69 /**
70  * Frees the members of info
71  * (but not info itself)
72  * @param info the user info struct
73  */
74 void
75 _dbus_user_info_free (DBusUserInfo *info)
76 {
77   dbus_free (info->group_ids);
78   dbus_free (info->username);
79   dbus_free (info->homedir);
80 }
81
82 /**
83  * Frees the members of info (but not info itself).
84  *
85  * @param info the group info
86  */
87 void
88 _dbus_group_info_free (DBusGroupInfo    *info)
89 {
90   dbus_free (info->groupname);
91 }
92
93 /**
94  * Checks if a given string is actually a number 
95  * and converts it if it is 
96  *
97  * @param str the string to check
98  * @param num the memory location of the unsigned long to fill in
99  * @returns TRUE if str is a number and num is filled in 
100  */
101 dbus_bool_t
102 _dbus_is_a_number (const DBusString *str,
103                    unsigned long    *num)
104 {
105   int end;
106
107   if (_dbus_string_parse_uint (str, 0, num, &end) &&
108       end == _dbus_string_get_length (str))
109     return TRUE;
110   else
111     return FALSE;
112 }
113
114 /**
115  * Looks up a uid or username in the user database.  Only one of name
116  * or UID can be provided. There are wrapper functions for this that
117  * are better to use, this one does no locking or anything on the
118  * database and otherwise sort of sucks.
119  *
120  * @param db the database
121  * @param uid the user ID or #DBUS_UID_UNSET
122  * @param username username or #NULL 
123  * @param error error to fill in
124  * @returns the entry in the database
125  */
126 DBusUserInfo*
127 _dbus_user_database_lookup (DBusUserDatabase *db,
128                             dbus_uid_t        uid,
129                             const DBusString *username,
130                             DBusError        *error)
131 {
132   DBusUserInfo *info;
133
134   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
135   _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
136
137   /* See if the username is really a number */
138   if (uid == DBUS_UID_UNSET)
139     {
140       unsigned long n;
141
142       if (_dbus_is_a_number (username, &n))
143         uid = n;
144     }
145
146 #ifdef DBUS_ENABLE_USER_CACHE  
147   if (uid != DBUS_UID_UNSET)
148     info = _dbus_hash_table_lookup_ulong (db->users, uid);
149   else
150     info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
151
152   if (info)
153     {
154       _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
155                      info->uid);
156       return info;
157     }
158   else
159 #else 
160   if (1)
161 #endif
162     {
163       if (uid != DBUS_UID_UNSET)
164         _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
165                        uid);
166       else
167         _dbus_verbose ("No cache for user \"%s\"\n",
168                        _dbus_string_get_const_data (username));
169       
170       info = dbus_new0 (DBusUserInfo, 1);
171       if (info == NULL)
172         {
173           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
174           return NULL;
175         }
176
177       if (uid != DBUS_UID_UNSET)
178         {
179           if (!_dbus_user_info_fill_uid (info, uid, error))
180             {
181               _DBUS_ASSERT_ERROR_IS_SET (error);
182               _dbus_user_info_free_allocated (info);
183               return NULL;
184             }
185         }
186       else
187         {
188           if (!_dbus_user_info_fill (info, username, error))
189             {
190               _DBUS_ASSERT_ERROR_IS_SET (error);
191               _dbus_user_info_free_allocated (info);
192               return NULL;
193             }
194         }
195
196       /* be sure we don't use these after here */
197       uid = DBUS_UID_UNSET;
198       username = NULL;
199
200       /* insert into hash */
201       if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
202         {
203           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
204           _dbus_user_info_free_allocated (info);
205           return NULL;
206         }
207
208       if (!_dbus_hash_table_insert_string (db->users_by_name,
209                                            info->username,
210                                            info))
211         {
212           _dbus_hash_table_remove_ulong (db->users, info->uid);
213           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
214           return NULL;
215         }
216       
217       return info;
218     }
219 }
220
221 _DBUS_DEFINE_GLOBAL_LOCK(system_users);
222 static dbus_bool_t database_locked = FALSE;
223 static DBusUserDatabase *system_db = NULL;
224 static DBusString process_username;
225 static DBusString process_homedir;
226       
227 static void
228 shutdown_system_db (void *data)
229 {
230   _dbus_user_database_unref (system_db);
231   system_db = NULL;
232   _dbus_string_free (&process_username);
233   _dbus_string_free (&process_homedir);
234 }
235
236 static dbus_bool_t
237 init_system_db (void)
238 {
239   _dbus_assert (database_locked);
240     
241   if (system_db == NULL)
242     {
243       DBusError error;
244       const DBusUserInfo *info;
245       
246       system_db = _dbus_user_database_new ();
247       if (system_db == NULL)
248         return FALSE;
249
250       dbus_error_init (&error);
251
252       if (!_dbus_user_database_get_uid (system_db,
253                                         _dbus_getuid (),
254                                         &info,
255                                         &error))
256         {
257           _dbus_user_database_unref (system_db);
258           system_db = NULL;
259           
260           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
261             {
262               dbus_error_free (&error);
263               return FALSE;
264             }
265           else
266             {
267               /* This really should not happen. */
268               _dbus_warn ("Could not get password database information for UID of current process: %s\n",
269                           error.message);
270               dbus_error_free (&error);
271               return FALSE;
272             }
273         }
274
275       if (!_dbus_string_init (&process_username))
276         {
277           _dbus_user_database_unref (system_db);
278           system_db = NULL;
279           return FALSE;
280         }
281
282       if (!_dbus_string_init (&process_homedir))
283         {
284           _dbus_string_free (&process_username);
285           _dbus_user_database_unref (system_db);
286           system_db = NULL;
287           return FALSE;
288         }
289
290       if (!_dbus_string_append (&process_username,
291                                 info->username) ||
292           !_dbus_string_append (&process_homedir,
293                                 info->homedir) ||
294           !_dbus_register_shutdown_func (shutdown_system_db, NULL))
295         {
296           _dbus_string_free (&process_username);
297           _dbus_string_free (&process_homedir);
298           _dbus_user_database_unref (system_db);
299           system_db = NULL;
300           return FALSE;
301         }
302     }
303
304   return TRUE;
305 }
306
307 /**
308  * Locks global system user database.
309  */
310 void
311 _dbus_user_database_lock_system (void)
312 {
313   _DBUS_LOCK (system_users);
314   database_locked = TRUE;
315 }
316
317 /**
318  * Unlocks global system user database.
319  */
320 void
321 _dbus_user_database_unlock_system (void)
322 {
323   database_locked = FALSE;
324   _DBUS_UNLOCK (system_users);
325 }
326
327 /**
328  * Gets the system global user database;
329  * must be called with lock held (_dbus_user_database_lock_system()).
330  *
331  * @returns the database or #NULL if no memory
332  */
333 DBusUserDatabase*
334 _dbus_user_database_get_system (void)
335 {
336   _dbus_assert (database_locked);
337
338   init_system_db ();
339   
340   return system_db;
341 }
342
343 /**
344  * Flushes the system global user database;
345  */
346 void
347 _dbus_user_database_flush_system (void)
348 {
349   _dbus_user_database_lock_system ();
350    
351   _dbus_user_database_flush (system_db);
352
353   _dbus_user_database_unlock_system ();
354 }
355
356 /**
357  * Gets username of user owning current process.  The returned string
358  * is valid until dbus_shutdown() is called.
359  *
360  * @param username place to store pointer to username
361  * @returns #FALSE if no memory
362  */
363 dbus_bool_t
364 _dbus_append_desired_identity (const DBusString **username)
365 {
366   _dbus_user_database_lock_system ();
367   if (!init_system_db ())
368     {
369       _dbus_user_database_unlock_system ();
370       return FALSE;
371     }
372   *username = &process_username;
373   _dbus_user_database_unlock_system ();  
374
375   return TRUE;
376 }
377
378 /**
379  * Gets homedir of user owning current process.  The returned string
380  * is valid until dbus_shutdown() is called.
381  *
382  * @param homedir place to store pointer to homedir
383  * @returns #FALSE if no memory
384  */
385 dbus_bool_t
386 _dbus_homedir_from_current_process (const DBusString  **homedir)
387 {
388   _dbus_user_database_lock_system ();
389   if (!init_system_db ())
390     {
391       _dbus_user_database_unlock_system ();
392       return FALSE;
393     }
394   *homedir = &process_homedir;
395   _dbus_user_database_unlock_system ();
396
397   return TRUE;
398 }
399
400 /**
401  * Gets the home directory for the given user.
402  *
403  * @param username the username
404  * @param homedir string to append home directory to
405  * @returns #TRUE if user existed and we appended their homedir
406  */
407 dbus_bool_t
408 _dbus_homedir_from_username (const DBusString *username,
409                              DBusString       *homedir)
410 {
411   DBusUserDatabase *db;
412   const DBusUserInfo *info;
413   _dbus_user_database_lock_system ();
414
415   db = _dbus_user_database_get_system ();
416   if (db == NULL)
417     {
418       _dbus_user_database_unlock_system ();
419       return FALSE;
420     }
421
422   if (!_dbus_user_database_get_username (db, username,
423                                          &info, NULL))
424     {
425       _dbus_user_database_unlock_system ();
426       return FALSE;
427     }
428
429   if (!_dbus_string_append (homedir, info->homedir))
430     {
431       _dbus_user_database_unlock_system ();
432       return FALSE;
433     }
434   
435   _dbus_user_database_unlock_system ();
436   return TRUE;
437 }
438
439 /**
440  * Adds the credentials corresponding to the given username.
441  *
442  * @param credentials credentials to fill in 
443  * @param username the username
444  * @returns #TRUE if the username existed and we got some credentials
445  */
446 dbus_bool_t
447 _dbus_credentials_add_from_username (DBusCredentials  *credentials,
448                                      const DBusString *username)
449 {
450   DBusUserDatabase *db;
451   const DBusUserInfo *info;
452
453   _dbus_user_database_lock_system ();
454
455   db = _dbus_user_database_get_system ();
456   if (db == NULL)
457     {
458       _dbus_user_database_unlock_system ();
459       return FALSE;
460     }
461
462   if (!_dbus_user_database_get_username (db, username,
463                                          &info, NULL))
464     {
465       _dbus_user_database_unlock_system ();
466       return FALSE;
467     }
468
469   if (!_dbus_credentials_add_unix_uid(credentials, info->uid))
470     {
471       _dbus_user_database_unlock_system ();
472       return FALSE;
473     }
474   
475   _dbus_user_database_unlock_system ();
476   return TRUE;
477 }
478
479 /**
480  * Creates a new user database object used to look up and
481  * cache user information.
482  * @returns new database, or #NULL on out of memory
483  */
484 DBusUserDatabase*
485 _dbus_user_database_new (void)
486 {
487   DBusUserDatabase *db;
488   
489   db = dbus_new0 (DBusUserDatabase, 1);
490   if (db == NULL)
491     return NULL;
492
493   db->refcount = 1;
494
495   db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
496                                     NULL, (DBusFreeFunction) _dbus_user_info_free_allocated);
497   
498   if (db->users == NULL)
499     goto failed;
500
501   db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
502                                      NULL, (DBusFreeFunction) _dbus_group_info_free_allocated);
503   
504   if (db->groups == NULL)
505     goto failed;
506
507   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
508                                             NULL, NULL);
509   if (db->users_by_name == NULL)
510     goto failed;
511   
512   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
513                                              NULL, NULL);
514   if (db->groups_by_name == NULL)
515     goto failed;
516   
517   return db;
518   
519  failed:
520   _dbus_user_database_unref (db);
521   return NULL;
522 }
523
524 /**
525  * Flush all information out of the user database. 
526  */
527 void
528 _dbus_user_database_flush (DBusUserDatabase *db) 
529 {
530   _dbus_hash_table_remove_all(db->users_by_name);
531   _dbus_hash_table_remove_all(db->groups_by_name);
532   _dbus_hash_table_remove_all(db->users);
533   _dbus_hash_table_remove_all(db->groups);
534 }
535
536 #ifdef DBUS_BUILD_TESTS
537 /**
538  * Increments refcount of user database.
539  * @param db the database
540  * @returns the database
541  */
542 DBusUserDatabase *
543 _dbus_user_database_ref (DBusUserDatabase  *db)
544 {
545   _dbus_assert (db->refcount > 0);
546
547   db->refcount += 1;
548
549   return db;
550 }
551 #endif /* DBUS_BUILD_TESTS */
552
553 /**
554  * Decrements refcount of user database.
555  * @param db the database
556  */
557 void
558 _dbus_user_database_unref (DBusUserDatabase  *db)
559 {
560   _dbus_assert (db->refcount > 0);
561
562   db->refcount -= 1;
563   if (db->refcount == 0)
564     {
565       if (db->users)
566         _dbus_hash_table_unref (db->users);
567
568       if (db->groups)
569         _dbus_hash_table_unref (db->groups);
570
571       if (db->users_by_name)
572         _dbus_hash_table_unref (db->users_by_name);
573
574       if (db->groups_by_name)
575         _dbus_hash_table_unref (db->groups_by_name);
576       
577       dbus_free (db);
578     }
579 }
580
581 /**
582  * Gets the user information for the given UID,
583  * returned user info should not be freed. 
584  *
585  * @param db user database
586  * @param uid the user ID
587  * @param info return location for const ref to user info
588  * @param error error location
589  * @returns #FALSE if error is set
590  */
591 dbus_bool_t
592 _dbus_user_database_get_uid (DBusUserDatabase    *db,
593                              dbus_uid_t           uid,
594                              const DBusUserInfo **info,
595                              DBusError           *error)
596 {
597   *info = _dbus_user_database_lookup (db, uid, NULL, error);
598   return *info != NULL;
599 }
600
601 /**
602  * Gets the user information for the given username.
603  *
604  * @param db user database
605  * @param username the user name
606  * @param info return location for const ref to user info
607  * @param error error location
608  * @returns #FALSE if error is set
609  */
610 dbus_bool_t
611 _dbus_user_database_get_username  (DBusUserDatabase     *db,
612                                    const DBusString     *username,
613                                    const DBusUserInfo  **info,
614                                    DBusError            *error)
615 {
616   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
617   return *info != NULL;
618 }
619
620 /** @} */
621
622 /* Tests in dbus-userdb-util.c */