dbus-userdb.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
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 <string.h>
00030 
00042 void
00043 _dbus_user_info_free_allocated (DBusUserInfo *info)
00044 {
00045   if (info == NULL) /* hash table will pass NULL */
00046     return;
00047 
00048   _dbus_user_info_free (info);
00049   dbus_free (info);
00050 }
00051 
00058 void
00059 _dbus_group_info_free_allocated (DBusGroupInfo *info)
00060 {
00061   if (info == NULL) /* hash table will pass NULL */
00062     return;
00063 
00064   _dbus_group_info_free (info);
00065   dbus_free (info);
00066 }
00067 
00076 dbus_bool_t
00077 _dbus_is_a_number (const DBusString *str,
00078                    unsigned long    *num)
00079 {
00080   int end;
00081 
00082   if (_dbus_string_parse_int (str, 0, num, &end) &&
00083       end == _dbus_string_get_length (str))
00084     return TRUE;
00085   else
00086     return FALSE;
00087 }
00088 
00101 DBusUserInfo*
00102 _dbus_user_database_lookup (DBusUserDatabase *db,
00103                             dbus_uid_t        uid,
00104                             const DBusString *username,
00105                             DBusError        *error)
00106 {
00107   DBusUserInfo *info;
00108 
00109   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00110   _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
00111 
00112   /* See if the username is really a number */
00113   if (uid == DBUS_UID_UNSET)
00114     {
00115       unsigned long n;
00116 
00117       if (_dbus_is_a_number (username, &n))
00118         uid = n;
00119     }
00120 
00121   if (uid != DBUS_UID_UNSET)
00122     info = _dbus_hash_table_lookup_ulong (db->users, uid);
00123   else
00124     info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
00125   
00126   if (info)
00127     {
00128       _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
00129                      info->uid);
00130       return info;
00131     }
00132   else
00133     {
00134       if (uid != DBUS_UID_UNSET)
00135         _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
00136                        uid);
00137       else
00138         _dbus_verbose ("No cache for user \"%s\"\n",
00139                        _dbus_string_get_const_data (username));
00140       
00141       info = dbus_new0 (DBusUserInfo, 1);
00142       if (info == NULL)
00143         {
00144           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00145           return NULL;
00146         }
00147 
00148       if (uid != DBUS_UID_UNSET)
00149         {
00150           if (!_dbus_user_info_fill_uid (info, uid, error))
00151             {
00152               _DBUS_ASSERT_ERROR_IS_SET (error);
00153               _dbus_user_info_free_allocated (info);
00154               return NULL;
00155             }
00156         }
00157       else
00158         {
00159           if (!_dbus_user_info_fill (info, username, error))
00160             {
00161               _DBUS_ASSERT_ERROR_IS_SET (error);
00162               _dbus_user_info_free_allocated (info);
00163               return NULL;
00164             }
00165         }
00166 
00167       /* be sure we don't use these after here */
00168       uid = DBUS_UID_UNSET;
00169       username = NULL;
00170 
00171       /* insert into hash */
00172       if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
00173         {
00174           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00175           _dbus_user_info_free_allocated (info);
00176           return NULL;
00177         }
00178 
00179       if (!_dbus_hash_table_insert_string (db->users_by_name,
00180                                            info->username,
00181                                            info))
00182         {
00183           _dbus_hash_table_remove_ulong (db->users, info->uid);
00184           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00185           return NULL;
00186         }
00187       
00188       return info;
00189     }
00190 }
00191 
00192 _DBUS_DEFINE_GLOBAL_LOCK(system_users);
00193 static dbus_bool_t database_locked = FALSE;
00194 static DBusUserDatabase *system_db = NULL;
00195 static DBusString process_username;
00196 static DBusString process_homedir;
00197       
00198 static void
00199 shutdown_system_db (void *data)
00200 {
00201   _dbus_user_database_unref (system_db);
00202   system_db = NULL;
00203   _dbus_string_free (&process_username);
00204   _dbus_string_free (&process_homedir);
00205 }
00206 
00207 static dbus_bool_t
00208 init_system_db (void)
00209 {
00210   _dbus_assert (database_locked);
00211     
00212   if (system_db == NULL)
00213     {
00214       DBusError error;
00215       const DBusUserInfo *info;
00216       
00217       system_db = _dbus_user_database_new ();
00218       if (system_db == NULL)
00219         return FALSE;
00220 
00221       dbus_error_init (&error);
00222 
00223       if (!_dbus_user_database_get_uid (system_db,
00224                                         _dbus_getuid (),
00225                                         &info,
00226                                         &error))
00227         {
00228           _dbus_user_database_unref (system_db);
00229           system_db = NULL;
00230           
00231           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
00232             {
00233               dbus_error_free (&error);
00234               return FALSE;
00235             }
00236           else
00237             {
00238               /* This really should not happen. */
00239               _dbus_warn ("Could not get password database information for UID of current process: %s\n",
00240                           error.message);
00241               dbus_error_free (&error);
00242               return FALSE;
00243             }
00244         }
00245 
00246       if (!_dbus_string_init (&process_username))
00247         {
00248           _dbus_user_database_unref (system_db);
00249           system_db = NULL;
00250           return FALSE;
00251         }
00252 
00253       if (!_dbus_string_init (&process_homedir))
00254         {
00255           _dbus_string_free (&process_username);
00256           _dbus_user_database_unref (system_db);
00257           system_db = NULL;
00258           return FALSE;
00259         }
00260 
00261       if (!_dbus_string_append (&process_username,
00262                                 info->username) ||
00263           !_dbus_string_append (&process_homedir,
00264                                 info->homedir) ||
00265           !_dbus_register_shutdown_func (shutdown_system_db, NULL))
00266         {
00267           _dbus_string_free (&process_username);
00268           _dbus_string_free (&process_homedir);
00269           _dbus_user_database_unref (system_db);
00270           system_db = NULL;
00271           return FALSE;
00272         }
00273     }
00274 
00275   return TRUE;
00276 }
00277 
00281 void
00282 _dbus_user_database_lock_system (void)
00283 {
00284   _DBUS_LOCK (system_users);
00285   database_locked = TRUE;
00286 }
00287 
00291 void
00292 _dbus_user_database_unlock_system (void)
00293 {
00294   database_locked = FALSE;
00295   _DBUS_UNLOCK (system_users);
00296 }
00297 
00304 DBusUserDatabase*
00305 _dbus_user_database_get_system (void)
00306 {
00307   _dbus_assert (database_locked);
00308 
00309   init_system_db ();
00310   
00311   return system_db;
00312 }
00313 
00321 dbus_bool_t
00322 _dbus_username_from_current_process (const DBusString **username)
00323 {
00324   _dbus_user_database_lock_system ();
00325   if (!init_system_db ())
00326     {
00327       _dbus_user_database_unlock_system ();
00328       return FALSE;
00329     }
00330   *username = &process_username;
00331   _dbus_user_database_unlock_system ();  
00332 
00333   return TRUE;
00334 }
00335 
00343 dbus_bool_t
00344 _dbus_homedir_from_current_process (const DBusString  **homedir)
00345 {
00346   _dbus_user_database_lock_system ();
00347   if (!init_system_db ())
00348     {
00349       _dbus_user_database_unlock_system ();
00350       return FALSE;
00351     }
00352   *homedir = &process_homedir;
00353   _dbus_user_database_unlock_system ();
00354 
00355   return TRUE;
00356 }
00357 
00365 dbus_bool_t
00366 _dbus_homedir_from_username (const DBusString *username,
00367                              DBusString       *homedir)
00368 {
00369   DBusUserDatabase *db;
00370   const DBusUserInfo *info;
00371   _dbus_user_database_lock_system ();
00372 
00373   db = _dbus_user_database_get_system ();
00374   if (db == NULL)
00375     {
00376       _dbus_user_database_unlock_system ();
00377       return FALSE;
00378     }
00379 
00380   if (!_dbus_user_database_get_username (db, username,
00381                                          &info, NULL))
00382     {
00383       _dbus_user_database_unlock_system ();
00384       return FALSE;
00385     }
00386 
00387   if (!_dbus_string_append (homedir, info->homedir))
00388     {
00389       _dbus_user_database_unlock_system ();
00390       return FALSE;
00391     }
00392   
00393   _dbus_user_database_unlock_system ();
00394   return TRUE;
00395 }
00396 
00404 dbus_bool_t
00405 _dbus_credentials_from_username (const DBusString *username,
00406                                  DBusCredentials  *credentials)
00407 {
00408   DBusUserDatabase *db;
00409   const DBusUserInfo *info;
00410   _dbus_user_database_lock_system ();
00411 
00412   db = _dbus_user_database_get_system ();
00413   if (db == NULL)
00414     {
00415       _dbus_user_database_unlock_system ();
00416       return FALSE;
00417     }
00418 
00419   if (!_dbus_user_database_get_username (db, username,
00420                                          &info, NULL))
00421     {
00422       _dbus_user_database_unlock_system ();
00423       return FALSE;
00424     }
00425 
00426   credentials->pid = DBUS_PID_UNSET;
00427   credentials->uid = info->uid;
00428   credentials->gid = info->primary_gid;
00429   
00430   _dbus_user_database_unlock_system ();
00431   return TRUE;
00432 }
00433 
00439 DBusUserDatabase*
00440 _dbus_user_database_new (void)
00441 {
00442   DBusUserDatabase *db;
00443   
00444   db = dbus_new0 (DBusUserDatabase, 1);
00445   if (db == NULL)
00446     return NULL;
00447 
00448   db->refcount = 1;
00449 
00450   db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
00451                                     NULL, (DBusFreeFunction) _dbus_user_info_free_allocated);
00452   
00453   if (db->users == NULL)
00454     goto failed;
00455 
00456   db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
00457                                      NULL, (DBusFreeFunction) _dbus_group_info_free_allocated);
00458   
00459   if (db->groups == NULL)
00460     goto failed;
00461 
00462   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00463                                             NULL, NULL);
00464   if (db->users_by_name == NULL)
00465     goto failed;
00466   
00467   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00468                                              NULL, NULL);
00469   if (db->groups_by_name == NULL)
00470     goto failed;
00471   
00472   return db;
00473   
00474  failed:
00475   _dbus_user_database_unref (db);
00476   return NULL;
00477 }
00478 
00482 void
00483 _dbus_user_database_flush (DBusUserDatabase *db) 
00484 {
00485   _dbus_hash_table_remove_all(db->users_by_name);
00486   _dbus_hash_table_remove_all(db->groups_by_name);
00487   _dbus_hash_table_remove_all(db->users);
00488   _dbus_hash_table_remove_all(db->groups);
00489 }
00490 
00491 #ifdef DBUS_BUILD_TESTS
00492 
00497 DBusUserDatabase *
00498 _dbus_user_database_ref (DBusUserDatabase  *db)
00499 {
00500   _dbus_assert (db->refcount > 0);
00501 
00502   db->refcount += 1;
00503 
00504   return db;
00505 }
00506 #endif /* DBUS_BUILD_TESTS */
00507 
00512 void
00513 _dbus_user_database_unref (DBusUserDatabase  *db)
00514 {
00515   _dbus_assert (db->refcount > 0);
00516 
00517   db->refcount -= 1;
00518   if (db->refcount == 0)
00519     {
00520       if (db->users)
00521         _dbus_hash_table_unref (db->users);
00522 
00523       if (db->groups)
00524         _dbus_hash_table_unref (db->groups);
00525 
00526       if (db->users_by_name)
00527         _dbus_hash_table_unref (db->users_by_name);
00528 
00529       if (db->groups_by_name)
00530         _dbus_hash_table_unref (db->groups_by_name);
00531       
00532       dbus_free (db);
00533     }
00534 }
00535 
00546 dbus_bool_t
00547 _dbus_user_database_get_uid (DBusUserDatabase    *db,
00548                              dbus_uid_t           uid,
00549                              const DBusUserInfo **info,
00550                              DBusError           *error)
00551 {
00552   *info = _dbus_user_database_lookup (db, uid, NULL, error);
00553   return *info != NULL;
00554 }
00555 
00565 dbus_bool_t
00566 _dbus_user_database_get_username  (DBusUserDatabase     *db,
00567                                    const DBusString     *username,
00568                                    const DBusUserInfo  **info,
00569                                    DBusError            *error)
00570 {
00571   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
00572   return *info != NULL;
00573 }
00574 
00577 /* Tests in dbus-userdb-util.c */

Generated on Fri Sep 21 18:12:13 2007 for D-Bus by  doxygen 1.5.1