D-Bus  1.10.12
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00021  *
00022  */
00023 #include <config.h>
00024 #define DBUS_USERDB_INCLUDES_PRIVATE 1
00025 #include "dbus-userdb.h"
00026 #include "dbus-hash.h"
00027 #include "dbus-test.h"
00028 #include "dbus-internals.h"
00029 #include "dbus-protocol.h"
00030 #include "dbus-credentials.h"
00031 #include <string.h>
00032 
00044 void
00045 _dbus_user_info_free_allocated (DBusUserInfo *info)
00046 {
00047   if (info == NULL) /* hash table will pass NULL */
00048     return;
00049 
00050   _dbus_user_info_free (info);
00051   dbus_free (info);
00052 }
00053 
00060 void
00061 _dbus_group_info_free_allocated (DBusGroupInfo *info)
00062 {
00063   if (info == NULL) /* hash table will pass NULL */
00064     return;
00065 
00066   _dbus_group_info_free (info);
00067   dbus_free (info);
00068 }
00069 
00075 void
00076 _dbus_user_info_free (DBusUserInfo *info)
00077 {
00078   dbus_free (info->group_ids);
00079   dbus_free (info->username);
00080   dbus_free (info->homedir);
00081 }
00082 
00088 void
00089 _dbus_group_info_free (DBusGroupInfo    *info)
00090 {
00091   dbus_free (info->groupname);
00092 }
00093 
00102 dbus_bool_t
00103 _dbus_is_a_number (const DBusString *str,
00104                    unsigned long    *num)
00105 {
00106   int end;
00107 
00108   if (_dbus_string_parse_uint (str, 0, num, &end) &&
00109       end == _dbus_string_get_length (str))
00110     return TRUE;
00111   else
00112     return FALSE;
00113 }
00114 
00127 DBusUserInfo*
00128 _dbus_user_database_lookup (DBusUserDatabase *db,
00129                             dbus_uid_t        uid,
00130                             const DBusString *username,
00131                             DBusError        *error)
00132 {
00133   DBusUserInfo *info;
00134 
00135   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00136   _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
00137 
00138   /* See if the username is really a number */
00139   if (uid == DBUS_UID_UNSET)
00140     {
00141       unsigned long n;
00142 
00143       if (_dbus_is_a_number (username, &n))
00144         uid = n;
00145     }
00146 
00147   if (uid != DBUS_UID_UNSET)
00148     info = _dbus_hash_table_lookup_uintptr (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     {
00160       if (uid != DBUS_UID_UNSET)
00161         _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
00162                        uid);
00163       else
00164         _dbus_verbose ("No cache for user \"%s\"\n",
00165                        _dbus_string_get_const_data (username));
00166       
00167       info = dbus_new0 (DBusUserInfo, 1);
00168       if (info == NULL)
00169         {
00170           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00171           return NULL;
00172         }
00173 
00174       if (uid != DBUS_UID_UNSET)
00175         {
00176           if (!_dbus_user_info_fill_uid (info, uid, error))
00177             {
00178               _DBUS_ASSERT_ERROR_IS_SET (error);
00179               _dbus_user_info_free_allocated (info);
00180               return NULL;
00181             }
00182         }
00183       else
00184         {
00185           if (!_dbus_user_info_fill (info, username, error))
00186             {
00187               _DBUS_ASSERT_ERROR_IS_SET (error);
00188               _dbus_user_info_free_allocated (info);
00189               return NULL;
00190             }
00191         }
00192 
00193       /* be sure we don't use these after here */
00194       uid = DBUS_UID_UNSET;
00195       username = NULL;
00196 
00197       /* insert into hash */
00198       if (!_dbus_hash_table_insert_uintptr (db->users, info->uid, info))
00199         {
00200           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00201           _dbus_user_info_free_allocated (info);
00202           return NULL;
00203         }
00204 
00205       if (!_dbus_hash_table_insert_string (db->users_by_name,
00206                                            info->username,
00207                                            info))
00208         {
00209           _dbus_hash_table_remove_uintptr (db->users, info->uid);
00210           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00211           return NULL;
00212         }
00213       
00214       return info;
00215     }
00216 }
00217 
00218 static dbus_bool_t database_locked = FALSE;
00219 static DBusUserDatabase *system_db = NULL;
00220 static DBusString process_username;
00221 static DBusString process_homedir;
00222       
00223 static void
00224 shutdown_system_db (void *data)
00225 {
00226   if (system_db != NULL)
00227     _dbus_user_database_unref (system_db);
00228   system_db = NULL;
00229   _dbus_string_free (&process_username);
00230   _dbus_string_free (&process_homedir);
00231 }
00232 
00233 static dbus_bool_t
00234 init_system_db (void)
00235 {
00236   _dbus_assert (database_locked);
00237     
00238   if (system_db == NULL)
00239     {
00240       DBusError error = DBUS_ERROR_INIT;
00241       const DBusUserInfo *info;
00242       
00243       system_db = _dbus_user_database_new ();
00244       if (system_db == NULL)
00245         return FALSE;
00246 
00247       if (!_dbus_user_database_get_uid (system_db,
00248                                         _dbus_getuid (),
00249                                         &info,
00250                                         &error))
00251         {
00252           _dbus_user_database_unref (system_db);
00253           system_db = NULL;
00254           
00255           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
00256             {
00257               dbus_error_free (&error);
00258               return FALSE;
00259             }
00260           else
00261             {
00262               /* This really should not happen. */
00263               _dbus_warn ("Could not get password database information for UID of current process: %s\n",
00264                           error.message);
00265               dbus_error_free (&error);
00266               return FALSE;
00267             }
00268         }
00269 
00270       if (!_dbus_string_init (&process_username))
00271         {
00272           _dbus_user_database_unref (system_db);
00273           system_db = NULL;
00274           return FALSE;
00275         }
00276 
00277       if (!_dbus_string_init (&process_homedir))
00278         {
00279           _dbus_string_free (&process_username);
00280           _dbus_user_database_unref (system_db);
00281           system_db = NULL;
00282           return FALSE;
00283         }
00284 
00285       if (!_dbus_string_append (&process_username,
00286                                 info->username) ||
00287           !_dbus_string_append (&process_homedir,
00288                                 info->homedir) ||
00289           !_dbus_register_shutdown_func (shutdown_system_db, NULL))
00290         {
00291           _dbus_string_free (&process_username);
00292           _dbus_string_free (&process_homedir);
00293           _dbus_user_database_unref (system_db);
00294           system_db = NULL;
00295           return FALSE;
00296         }
00297     }
00298 
00299   return TRUE;
00300 }
00301 
00305 dbus_bool_t
00306 _dbus_user_database_lock_system (void)
00307 {
00308   if (_DBUS_LOCK (system_users))
00309     {
00310       database_locked = TRUE;
00311       return TRUE;
00312     }
00313   else
00314     {
00315       return FALSE;
00316     }
00317 }
00318 
00322 void
00323 _dbus_user_database_unlock_system (void)
00324 {
00325   database_locked = FALSE;
00326   _DBUS_UNLOCK (system_users);
00327 }
00328 
00335 DBusUserDatabase*
00336 _dbus_user_database_get_system (void)
00337 {
00338   _dbus_assert (database_locked);
00339 
00340   init_system_db ();
00341   
00342   return system_db;
00343 }
00344 
00348 void
00349 _dbus_user_database_flush_system (void)
00350 {
00351   if (!_dbus_user_database_lock_system ())
00352     {
00353       /* nothing to flush */
00354       return;
00355     }
00356 
00357    if (system_db != NULL)
00358     _dbus_user_database_flush (system_db);
00359 
00360   _dbus_user_database_unlock_system ();
00361 }
00362 
00370 dbus_bool_t
00371 _dbus_username_from_current_process (const DBusString **username)
00372 {
00373   if (!_dbus_user_database_lock_system ())
00374     return FALSE;
00375 
00376   if (!init_system_db ())
00377     {
00378       _dbus_user_database_unlock_system ();
00379       return FALSE;
00380     }
00381   *username = &process_username;
00382   _dbus_user_database_unlock_system ();  
00383 
00384   return TRUE;
00385 }
00386 
00394 dbus_bool_t
00395 _dbus_homedir_from_current_process (const DBusString  **homedir)
00396 {
00397   if (!_dbus_user_database_lock_system ())
00398     return FALSE;
00399 
00400   if (!init_system_db ())
00401     {
00402       _dbus_user_database_unlock_system ();
00403       return FALSE;
00404     }
00405   *homedir = &process_homedir;
00406   _dbus_user_database_unlock_system ();
00407 
00408   return TRUE;
00409 }
00410 
00418 dbus_bool_t
00419 _dbus_homedir_from_username (const DBusString *username,
00420                              DBusString       *homedir)
00421 {
00422   DBusUserDatabase *db;
00423   const DBusUserInfo *info;
00424 
00425   /* FIXME: this can't distinguish ENOMEM from other errors */
00426   if (!_dbus_user_database_lock_system ())
00427     return FALSE;
00428 
00429   db = _dbus_user_database_get_system ();
00430   if (db == NULL)
00431     {
00432       _dbus_user_database_unlock_system ();
00433       return FALSE;
00434     }
00435 
00436   if (!_dbus_user_database_get_username (db, username,
00437                                          &info, NULL))
00438     {
00439       _dbus_user_database_unlock_system ();
00440       return FALSE;
00441     }
00442 
00443   if (!_dbus_string_append (homedir, info->homedir))
00444     {
00445       _dbus_user_database_unlock_system ();
00446       return FALSE;
00447     }
00448   
00449   _dbus_user_database_unlock_system ();
00450   return TRUE;
00451 }
00452 
00460 dbus_bool_t
00461 _dbus_homedir_from_uid (dbus_uid_t         uid,
00462                         DBusString        *homedir)
00463 {
00464   DBusUserDatabase *db;
00465   const DBusUserInfo *info;
00466 
00467   /* FIXME: this can't distinguish ENOMEM from other errors */
00468   if (!_dbus_user_database_lock_system ())
00469     return FALSE;
00470 
00471   db = _dbus_user_database_get_system ();
00472   if (db == NULL)
00473     {
00474       _dbus_user_database_unlock_system ();
00475       return FALSE;
00476     }
00477 
00478   if (!_dbus_user_database_get_uid (db, uid,
00479                                     &info, NULL))
00480     {
00481       _dbus_user_database_unlock_system ();
00482       return FALSE;
00483     }
00484 
00485   if (!_dbus_string_append (homedir, info->homedir))
00486     {
00487       _dbus_user_database_unlock_system ();
00488       return FALSE;
00489     }
00490   
00491   _dbus_user_database_unlock_system ();
00492   return TRUE;
00493 }
00494 
00509 dbus_bool_t
00510 _dbus_credentials_add_from_user (DBusCredentials  *credentials,
00511                                  const DBusString *username)
00512 {
00513   DBusUserDatabase *db;
00514   const DBusUserInfo *info;
00515 
00516   /* FIXME: this can't distinguish ENOMEM from other errors */
00517   if (!_dbus_user_database_lock_system ())
00518     return FALSE;
00519 
00520   db = _dbus_user_database_get_system ();
00521   if (db == NULL)
00522     {
00523       _dbus_user_database_unlock_system ();
00524       return FALSE;
00525     }
00526 
00527   if (!_dbus_user_database_get_username (db, username,
00528                                          &info, NULL))
00529     {
00530       _dbus_user_database_unlock_system ();
00531       return FALSE;
00532     }
00533 
00534   if (!_dbus_credentials_add_unix_uid(credentials, info->uid))
00535     {
00536       _dbus_user_database_unlock_system ();
00537       return FALSE;
00538     }
00539   
00540   _dbus_user_database_unlock_system ();
00541   return TRUE;
00542 }
00543 
00549 DBusUserDatabase*
00550 _dbus_user_database_new (void)
00551 {
00552   DBusUserDatabase *db;
00553   
00554   db = dbus_new0 (DBusUserDatabase, 1);
00555   if (db == NULL)
00556     return NULL;
00557 
00558   db->refcount = 1;
00559 
00560   db->users = _dbus_hash_table_new (DBUS_HASH_UINTPTR,
00561                                     NULL, (DBusFreeFunction) _dbus_user_info_free_allocated);
00562   
00563   if (db->users == NULL)
00564     goto failed;
00565 
00566   db->groups = _dbus_hash_table_new (DBUS_HASH_UINTPTR,
00567                                      NULL, (DBusFreeFunction) _dbus_group_info_free_allocated);
00568   
00569   if (db->groups == NULL)
00570     goto failed;
00571 
00572   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00573                                             NULL, NULL);
00574   if (db->users_by_name == NULL)
00575     goto failed;
00576   
00577   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00578                                              NULL, NULL);
00579   if (db->groups_by_name == NULL)
00580     goto failed;
00581   
00582   return db;
00583   
00584  failed:
00585   _dbus_user_database_unref (db);
00586   return NULL;
00587 }
00588 
00592 void
00593 _dbus_user_database_flush (DBusUserDatabase *db) 
00594 {
00595   _dbus_hash_table_remove_all(db->users_by_name);
00596   _dbus_hash_table_remove_all(db->groups_by_name);
00597   _dbus_hash_table_remove_all(db->users);
00598   _dbus_hash_table_remove_all(db->groups);
00599 }
00600 
00601 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00602 
00607 DBusUserDatabase *
00608 _dbus_user_database_ref (DBusUserDatabase  *db)
00609 {
00610   _dbus_assert (db->refcount > 0);
00611 
00612   db->refcount += 1;
00613 
00614   return db;
00615 }
00616 #endif /* DBUS_ENABLE_EMBEDDED_TESTS */
00617 
00622 void
00623 _dbus_user_database_unref (DBusUserDatabase  *db)
00624 {
00625   _dbus_assert (db->refcount > 0);
00626 
00627   db->refcount -= 1;
00628   if (db->refcount == 0)
00629     {
00630       if (db->users)
00631         _dbus_hash_table_unref (db->users);
00632 
00633       if (db->groups)
00634         _dbus_hash_table_unref (db->groups);
00635 
00636       if (db->users_by_name)
00637         _dbus_hash_table_unref (db->users_by_name);
00638 
00639       if (db->groups_by_name)
00640         _dbus_hash_table_unref (db->groups_by_name);
00641       
00642       dbus_free (db);
00643     }
00644 }
00645 
00656 dbus_bool_t
00657 _dbus_user_database_get_uid (DBusUserDatabase    *db,
00658                              dbus_uid_t           uid,
00659                              const DBusUserInfo **info,
00660                              DBusError           *error)
00661 {
00662   *info = _dbus_user_database_lookup (db, uid, NULL, error);
00663   return *info != NULL;
00664 }
00665 
00675 dbus_bool_t
00676 _dbus_user_database_get_username  (DBusUserDatabase     *db,
00677                                    const DBusString     *username,
00678                                    const DBusUserInfo  **info,
00679                                    DBusError            *error)
00680 {
00681   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
00682   return *info != NULL;
00683 }
00684 
00687 /* Tests in dbus-userdb-util.c */