dbus-userdb.c

00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 /* dbus-userdb.c User database abstraction
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 #define DBUS_USERDB_INCLUDES_PRIVATE 1
00024 #include "dbus-userdb.h"
00025 #include "dbus-hash.h"
00026 #include "dbus-test.h"
00027 #include "dbus-internals.h"
00028 #include "dbus-protocol.h"
00029 #include "dbus-credentials.h"
00030 #include <string.h>
00031 
00043 void
00044 _dbus_user_info_free_allocated (DBusUserInfo *info)
00045 {
00046   if (info == NULL) /* hash table will pass NULL */
00047     return;
00048 
00049   _dbus_user_info_free (info);
00050   dbus_free (info);
00051 }
00052 
00059 void
00060 _dbus_group_info_free_allocated (DBusGroupInfo *info)
00061 {
00062   if (info == NULL) /* hash table will pass NULL */
00063     return;
00064 
00065   _dbus_group_info_free (info);
00066   dbus_free (info);
00067 }
00068 
00074 void
00075 _dbus_user_info_free (DBusUserInfo *info)
00076 {
00077   dbus_free (info->group_ids);
00078   dbus_free (info->username);
00079   dbus_free (info->homedir);
00080 }
00081 
00087 void
00088 _dbus_group_info_free (DBusGroupInfo    *info)
00089 {
00090   dbus_free (info->groupname);
00091 }
00092 
00101 dbus_bool_t
00102 _dbus_is_a_number (const DBusString *str,
00103                    unsigned long    *num)
00104 {
00105   int end;
00106 
00107   if (_dbus_string_parse_uint (str, 0, num, &end) &&
00108       end == _dbus_string_get_length (str))
00109     return TRUE;
00110   else
00111     return FALSE;
00112 }
00113 
00126 DBusUserInfo*
00127 _dbus_user_database_lookup (DBusUserDatabase *db,
00128                             dbus_uid_t        uid,
00129                             const DBusString *username,
00130                             DBusError        *error)
00131 {
00132   DBusUserInfo *info;
00133 
00134   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00135   _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
00136 
00137   /* See if the username is really a number */
00138   if (uid == DBUS_UID_UNSET)
00139     {
00140       unsigned long n;
00141 
00142       if (_dbus_is_a_number (username, &n))
00143         uid = n;
00144     }
00145 
00146 #ifdef DBUS_ENABLE_USER_CACHE  
00147   if (uid != DBUS_UID_UNSET)
00148     info = _dbus_hash_table_lookup_ulong (db->users, uid);
00149   else
00150     info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
00151 
00152   if (info)
00153     {
00154       _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
00155                      info->uid);
00156       return info;
00157     }
00158   else
00159 #else 
00160   if (1)
00161 #endif
00162     {
00163       if (uid != DBUS_UID_UNSET)
00164         _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
00165                        uid);
00166       else
00167         _dbus_verbose ("No cache for user \"%s\"\n",
00168                        _dbus_string_get_const_data (username));
00169       
00170       info = dbus_new0 (DBusUserInfo, 1);
00171       if (info == NULL)
00172         {
00173           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00174           return NULL;
00175         }
00176 
00177       if (uid != DBUS_UID_UNSET)
00178         {
00179           if (!_dbus_user_info_fill_uid (info, uid, error))
00180             {
00181               _DBUS_ASSERT_ERROR_IS_SET (error);
00182               _dbus_user_info_free_allocated (info);
00183               return NULL;
00184             }
00185         }
00186       else
00187         {
00188           if (!_dbus_user_info_fill (info, username, error))
00189             {
00190               _DBUS_ASSERT_ERROR_IS_SET (error);
00191               _dbus_user_info_free_allocated (info);
00192               return NULL;
00193             }
00194         }
00195 
00196       /* be sure we don't use these after here */
00197       uid = DBUS_UID_UNSET;
00198       username = NULL;
00199 
00200       /* insert into hash */
00201       if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
00202         {
00203           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00204           _dbus_user_info_free_allocated (info);
00205           return NULL;
00206         }
00207 
00208       if (!_dbus_hash_table_insert_string (db->users_by_name,
00209                                            info->username,
00210                                            info))
00211         {
00212           _dbus_hash_table_remove_ulong (db->users, info->uid);
00213           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00214           return NULL;
00215         }
00216       
00217       return info;
00218     }
00219 }
00220 
00221 static dbus_bool_t database_locked = FALSE;
00222 static DBusUserDatabase *system_db = NULL;
00223 static DBusString process_username;
00224 static DBusString process_homedir;
00225       
00226 static void
00227 shutdown_system_db (void *data)
00228 {
00229   _dbus_user_database_unref (system_db);
00230   system_db = NULL;
00231   _dbus_string_free (&process_username);
00232   _dbus_string_free (&process_homedir);
00233 }
00234 
00235 static dbus_bool_t
00236 init_system_db (void)
00237 {
00238   _dbus_assert (database_locked);
00239     
00240   if (system_db == NULL)
00241     {
00242       DBusError error;
00243       const DBusUserInfo *info;
00244       
00245       system_db = _dbus_user_database_new ();
00246       if (system_db == NULL)
00247         return FALSE;
00248 
00249       dbus_error_init (&error);
00250 
00251       if (!_dbus_user_database_get_uid (system_db,
00252                                         _dbus_getuid (),
00253                                         &info,
00254                                         &error))
00255         {
00256           _dbus_user_database_unref (system_db);
00257           system_db = NULL;
00258           
00259           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
00260             {
00261               dbus_error_free (&error);
00262               return FALSE;
00263             }
00264           else
00265             {
00266               /* This really should not happen. */
00267               _dbus_warn ("Could not get password database information for UID of current process: %s\n",
00268                           error.message);
00269               dbus_error_free (&error);
00270               return FALSE;
00271             }
00272         }
00273 
00274       if (!_dbus_string_init (&process_username))
00275         {
00276           _dbus_user_database_unref (system_db);
00277           system_db = NULL;
00278           return FALSE;
00279         }
00280 
00281       if (!_dbus_string_init (&process_homedir))
00282         {
00283           _dbus_string_free (&process_username);
00284           _dbus_user_database_unref (system_db);
00285           system_db = NULL;
00286           return FALSE;
00287         }
00288 
00289       if (!_dbus_string_append (&process_username,
00290                                 info->username) ||
00291           !_dbus_string_append (&process_homedir,
00292                                 info->homedir) ||
00293           !_dbus_register_shutdown_func (shutdown_system_db, NULL))
00294         {
00295           _dbus_string_free (&process_username);
00296           _dbus_string_free (&process_homedir);
00297           _dbus_user_database_unref (system_db);
00298           system_db = NULL;
00299           return FALSE;
00300         }
00301     }
00302 
00303   return TRUE;
00304 }
00305 
00309 void
00310 _dbus_user_database_lock_system (void)
00311 {
00312   _DBUS_LOCK (system_users);
00313   database_locked = TRUE;
00314 }
00315 
00319 void
00320 _dbus_user_database_unlock_system (void)
00321 {
00322   database_locked = FALSE;
00323   _DBUS_UNLOCK (system_users);
00324 }
00325 
00332 DBusUserDatabase*
00333 _dbus_user_database_get_system (void)
00334 {
00335   _dbus_assert (database_locked);
00336 
00337   init_system_db ();
00338   
00339   return system_db;
00340 }
00341 
00345 void
00346 _dbus_user_database_flush_system (void)
00347 {
00348   _dbus_user_database_lock_system ();
00349    
00350   _dbus_user_database_flush (system_db);
00351 
00352   _dbus_user_database_unlock_system ();
00353 }
00354 
00362 dbus_bool_t
00363 _dbus_username_from_current_process (const DBusString **username)
00364 {
00365   _dbus_user_database_lock_system ();
00366   if (!init_system_db ())
00367     {
00368       _dbus_user_database_unlock_system ();
00369       return FALSE;
00370     }
00371   *username = &process_username;
00372   _dbus_user_database_unlock_system ();  
00373 
00374   return TRUE;
00375 }
00376 
00384 dbus_bool_t
00385 _dbus_homedir_from_current_process (const DBusString  **homedir)
00386 {
00387   _dbus_user_database_lock_system ();
00388   if (!init_system_db ())
00389     {
00390       _dbus_user_database_unlock_system ();
00391       return FALSE;
00392     }
00393   *homedir = &process_homedir;
00394   _dbus_user_database_unlock_system ();
00395 
00396   return TRUE;
00397 }
00398 
00406 dbus_bool_t
00407 _dbus_homedir_from_username (const DBusString *username,
00408                              DBusString       *homedir)
00409 {
00410   DBusUserDatabase *db;
00411   const DBusUserInfo *info;
00412   _dbus_user_database_lock_system ();
00413 
00414   db = _dbus_user_database_get_system ();
00415   if (db == NULL)
00416     {
00417       _dbus_user_database_unlock_system ();
00418       return FALSE;
00419     }
00420 
00421   if (!_dbus_user_database_get_username (db, username,
00422                                          &info, NULL))
00423     {
00424       _dbus_user_database_unlock_system ();
00425       return FALSE;
00426     }
00427 
00428   if (!_dbus_string_append (homedir, info->homedir))
00429     {
00430       _dbus_user_database_unlock_system ();
00431       return FALSE;
00432     }
00433   
00434   _dbus_user_database_unlock_system ();
00435   return TRUE;
00436 }
00437 
00445 dbus_bool_t
00446 _dbus_homedir_from_uid (dbus_uid_t         uid,
00447                         DBusString        *homedir)
00448 {
00449   DBusUserDatabase *db;
00450   const DBusUserInfo *info;
00451   _dbus_user_database_lock_system ();
00452 
00453   db = _dbus_user_database_get_system ();
00454   if (db == NULL)
00455     {
00456       _dbus_user_database_unlock_system ();
00457       return FALSE;
00458     }
00459 
00460   if (!_dbus_user_database_get_uid (db, uid,
00461                                     &info, NULL))
00462     {
00463       _dbus_user_database_unlock_system ();
00464       return FALSE;
00465     }
00466 
00467   if (!_dbus_string_append (homedir, info->homedir))
00468     {
00469       _dbus_user_database_unlock_system ();
00470       return FALSE;
00471     }
00472   
00473   _dbus_user_database_unlock_system ();
00474   return TRUE;
00475 }
00476 
00491 dbus_bool_t
00492 _dbus_credentials_add_from_user (DBusCredentials  *credentials,
00493                                  const DBusString *username)
00494 {
00495   DBusUserDatabase *db;
00496   const DBusUserInfo *info;
00497 
00498   _dbus_user_database_lock_system ();
00499 
00500   db = _dbus_user_database_get_system ();
00501   if (db == NULL)
00502     {
00503       _dbus_user_database_unlock_system ();
00504       return FALSE;
00505     }
00506 
00507   if (!_dbus_user_database_get_username (db, username,
00508                                          &info, NULL))
00509     {
00510       _dbus_user_database_unlock_system ();
00511       return FALSE;
00512     }
00513 
00514   if (!_dbus_credentials_add_unix_uid(credentials, info->uid))
00515     {
00516       _dbus_user_database_unlock_system ();
00517       return FALSE;
00518     }
00519   
00520   _dbus_user_database_unlock_system ();
00521   return TRUE;
00522 }
00523 
00529 DBusUserDatabase*
00530 _dbus_user_database_new (void)
00531 {
00532   DBusUserDatabase *db;
00533   
00534   db = dbus_new0 (DBusUserDatabase, 1);
00535   if (db == NULL)
00536     return NULL;
00537 
00538   db->refcount = 1;
00539 
00540   db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
00541                                     NULL, (DBusFreeFunction) _dbus_user_info_free_allocated);
00542   
00543   if (db->users == NULL)
00544     goto failed;
00545 
00546   db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
00547                                      NULL, (DBusFreeFunction) _dbus_group_info_free_allocated);
00548   
00549   if (db->groups == NULL)
00550     goto failed;
00551 
00552   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00553                                             NULL, NULL);
00554   if (db->users_by_name == NULL)
00555     goto failed;
00556   
00557   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00558                                              NULL, NULL);
00559   if (db->groups_by_name == NULL)
00560     goto failed;
00561   
00562   return db;
00563   
00564  failed:
00565   _dbus_user_database_unref (db);
00566   return NULL;
00567 }
00568 
00572 void
00573 _dbus_user_database_flush (DBusUserDatabase *db) 
00574 {
00575   _dbus_hash_table_remove_all(db->users_by_name);
00576   _dbus_hash_table_remove_all(db->groups_by_name);
00577   _dbus_hash_table_remove_all(db->users);
00578   _dbus_hash_table_remove_all(db->groups);
00579 }
00580 
00581 #ifdef DBUS_BUILD_TESTS
00582 
00587 DBusUserDatabase *
00588 _dbus_user_database_ref (DBusUserDatabase  *db)
00589 {
00590   _dbus_assert (db->refcount > 0);
00591 
00592   db->refcount += 1;
00593 
00594   return db;
00595 }
00596 #endif /* DBUS_BUILD_TESTS */
00597 
00602 void
00603 _dbus_user_database_unref (DBusUserDatabase  *db)
00604 {
00605   _dbus_assert (db->refcount > 0);
00606 
00607   db->refcount -= 1;
00608   if (db->refcount == 0)
00609     {
00610       if (db->users)
00611         _dbus_hash_table_unref (db->users);
00612 
00613       if (db->groups)
00614         _dbus_hash_table_unref (db->groups);
00615 
00616       if (db->users_by_name)
00617         _dbus_hash_table_unref (db->users_by_name);
00618 
00619       if (db->groups_by_name)
00620         _dbus_hash_table_unref (db->groups_by_name);
00621       
00622       dbus_free (db);
00623     }
00624 }
00625 
00636 dbus_bool_t
00637 _dbus_user_database_get_uid (DBusUserDatabase    *db,
00638                              dbus_uid_t           uid,
00639                              const DBusUserInfo **info,
00640                              DBusError           *error)
00641 {
00642   *info = _dbus_user_database_lookup (db, uid, NULL, error);
00643   return *info != NULL;
00644 }
00645 
00655 dbus_bool_t
00656 _dbus_user_database_get_username  (DBusUserDatabase     *db,
00657                                    const DBusString     *username,
00658                                    const DBusUserInfo  **info,
00659                                    DBusError            *error)
00660 {
00661   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
00662   return *info != NULL;
00663 }
00664 
00667 /* Tests in dbus-userdb-util.c */

Generated on Mon Feb 1 19:15:26 2010 for D-Bus by  doxygen 1.4.7