D-Bus 1.4.20

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 #ifdef DBUS_ENABLE_USERDB_CACHE  
00148   if (uid != DBUS_UID_UNSET)
00149     info = _dbus_hash_table_lookup_uintptr (db->users, uid);
00150   else
00151     info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
00152 
00153   if (info)
00154     {
00155       _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
00156                      info->uid);
00157       return info;
00158     }
00159   else
00160 #else 
00161   if (1)
00162 #endif
00163     {
00164       if (uid != DBUS_UID_UNSET)
00165         _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
00166                        uid);
00167       else
00168         _dbus_verbose ("No cache for user \"%s\"\n",
00169                        _dbus_string_get_const_data (username));
00170       
00171       info = dbus_new0 (DBusUserInfo, 1);
00172       if (info == NULL)
00173         {
00174           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00175           return NULL;
00176         }
00177 
00178       if (uid != DBUS_UID_UNSET)
00179         {
00180           if (!_dbus_user_info_fill_uid (info, uid, error))
00181             {
00182               _DBUS_ASSERT_ERROR_IS_SET (error);
00183               _dbus_user_info_free_allocated (info);
00184               return NULL;
00185             }
00186         }
00187       else
00188         {
00189           if (!_dbus_user_info_fill (info, username, error))
00190             {
00191               _DBUS_ASSERT_ERROR_IS_SET (error);
00192               _dbus_user_info_free_allocated (info);
00193               return NULL;
00194             }
00195         }
00196 
00197       /* be sure we don't use these after here */
00198       uid = DBUS_UID_UNSET;
00199       username = NULL;
00200 
00201       /* insert into hash */
00202       if (!_dbus_hash_table_insert_uintptr (db->users, info->uid, info))
00203         {
00204           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00205           _dbus_user_info_free_allocated (info);
00206           return NULL;
00207         }
00208 
00209       if (!_dbus_hash_table_insert_string (db->users_by_name,
00210                                            info->username,
00211                                            info))
00212         {
00213           _dbus_hash_table_remove_uintptr (db->users, info->uid);
00214           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00215           return NULL;
00216         }
00217       
00218       return info;
00219     }
00220 }
00221 
00222 static dbus_bool_t database_locked = FALSE;
00223 static DBusUserDatabase *system_db = NULL;
00224 static DBusString process_username;
00225 static DBusString process_homedir;
00226       
00227 static void
00228 shutdown_system_db (void *data)
00229 {
00230   if (system_db != NULL)
00231     _dbus_user_database_unref (system_db);
00232   system_db = NULL;
00233   _dbus_string_free (&process_username);
00234   _dbus_string_free (&process_homedir);
00235 }
00236 
00237 static dbus_bool_t
00238 init_system_db (void)
00239 {
00240   _dbus_assert (database_locked);
00241     
00242   if (system_db == NULL)
00243     {
00244       DBusError error = DBUS_ERROR_INIT;
00245       const DBusUserInfo *info;
00246       
00247       system_db = _dbus_user_database_new ();
00248       if (system_db == NULL)
00249         return FALSE;
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    if (system_db != NULL)
00351     _dbus_user_database_flush (system_db);
00352 
00353   _dbus_user_database_unlock_system ();
00354 }
00355 
00363 dbus_bool_t
00364 _dbus_username_from_current_process (const DBusString **username)
00365 {
00366   _dbus_user_database_lock_system ();
00367   if (!init_system_db ())
00368     {
00369       _dbus_user_database_unlock_system ();
00370       return FALSE;
00371     }
00372   *username = &process_username;
00373   _dbus_user_database_unlock_system ();  
00374 
00375   return TRUE;
00376 }
00377 
00385 dbus_bool_t
00386 _dbus_homedir_from_current_process (const DBusString  **homedir)
00387 {
00388   _dbus_user_database_lock_system ();
00389   if (!init_system_db ())
00390     {
00391       _dbus_user_database_unlock_system ();
00392       return FALSE;
00393     }
00394   *homedir = &process_homedir;
00395   _dbus_user_database_unlock_system ();
00396 
00397   return TRUE;
00398 }
00399 
00407 dbus_bool_t
00408 _dbus_homedir_from_username (const DBusString *username,
00409                              DBusString       *homedir)
00410 {
00411   DBusUserDatabase *db;
00412   const DBusUserInfo *info;
00413   _dbus_user_database_lock_system ();
00414 
00415   db = _dbus_user_database_get_system ();
00416   if (db == NULL)
00417     {
00418       _dbus_user_database_unlock_system ();
00419       return FALSE;
00420     }
00421 
00422   if (!_dbus_user_database_get_username (db, username,
00423                                          &info, NULL))
00424     {
00425       _dbus_user_database_unlock_system ();
00426       return FALSE;
00427     }
00428 
00429   if (!_dbus_string_append (homedir, info->homedir))
00430     {
00431       _dbus_user_database_unlock_system ();
00432       return FALSE;
00433     }
00434   
00435   _dbus_user_database_unlock_system ();
00436   return TRUE;
00437 }
00438 
00446 dbus_bool_t
00447 _dbus_homedir_from_uid (dbus_uid_t         uid,
00448                         DBusString        *homedir)
00449 {
00450   DBusUserDatabase *db;
00451   const DBusUserInfo *info;
00452   _dbus_user_database_lock_system ();
00453 
00454   db = _dbus_user_database_get_system ();
00455   if (db == NULL)
00456     {
00457       _dbus_user_database_unlock_system ();
00458       return FALSE;
00459     }
00460 
00461   if (!_dbus_user_database_get_uid (db, uid,
00462                                     &info, NULL))
00463     {
00464       _dbus_user_database_unlock_system ();
00465       return FALSE;
00466     }
00467 
00468   if (!_dbus_string_append (homedir, info->homedir))
00469     {
00470       _dbus_user_database_unlock_system ();
00471       return FALSE;
00472     }
00473   
00474   _dbus_user_database_unlock_system ();
00475   return TRUE;
00476 }
00477 
00492 dbus_bool_t
00493 _dbus_credentials_add_from_user (DBusCredentials  *credentials,
00494                                  const DBusString *username)
00495 {
00496   DBusUserDatabase *db;
00497   const DBusUserInfo *info;
00498 
00499   _dbus_user_database_lock_system ();
00500 
00501   db = _dbus_user_database_get_system ();
00502   if (db == NULL)
00503     {
00504       _dbus_user_database_unlock_system ();
00505       return FALSE;
00506     }
00507 
00508   if (!_dbus_user_database_get_username (db, username,
00509                                          &info, NULL))
00510     {
00511       _dbus_user_database_unlock_system ();
00512       return FALSE;
00513     }
00514 
00515   if (!_dbus_credentials_add_unix_uid(credentials, info->uid))
00516     {
00517       _dbus_user_database_unlock_system ();
00518       return FALSE;
00519     }
00520   
00521   _dbus_user_database_unlock_system ();
00522   return TRUE;
00523 }
00524 
00530 DBusUserDatabase*
00531 _dbus_user_database_new (void)
00532 {
00533   DBusUserDatabase *db;
00534   
00535   db = dbus_new0 (DBusUserDatabase, 1);
00536   if (db == NULL)
00537     return NULL;
00538 
00539   db->refcount = 1;
00540 
00541   db->users = _dbus_hash_table_new (DBUS_HASH_UINTPTR,
00542                                     NULL, (DBusFreeFunction) _dbus_user_info_free_allocated);
00543   
00544   if (db->users == NULL)
00545     goto failed;
00546 
00547   db->groups = _dbus_hash_table_new (DBUS_HASH_UINTPTR,
00548                                      NULL, (DBusFreeFunction) _dbus_group_info_free_allocated);
00549   
00550   if (db->groups == NULL)
00551     goto failed;
00552 
00553   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00554                                             NULL, NULL);
00555   if (db->users_by_name == NULL)
00556     goto failed;
00557   
00558   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00559                                              NULL, NULL);
00560   if (db->groups_by_name == NULL)
00561     goto failed;
00562   
00563   return db;
00564   
00565  failed:
00566   _dbus_user_database_unref (db);
00567   return NULL;
00568 }
00569 
00573 void
00574 _dbus_user_database_flush (DBusUserDatabase *db) 
00575 {
00576   _dbus_hash_table_remove_all(db->users_by_name);
00577   _dbus_hash_table_remove_all(db->groups_by_name);
00578   _dbus_hash_table_remove_all(db->users);
00579   _dbus_hash_table_remove_all(db->groups);
00580 }
00581 
00582 #ifdef DBUS_BUILD_TESTS
00583 
00588 DBusUserDatabase *
00589 _dbus_user_database_ref (DBusUserDatabase  *db)
00590 {
00591   _dbus_assert (db->refcount > 0);
00592 
00593   db->refcount += 1;
00594 
00595   return db;
00596 }
00597 #endif /* DBUS_BUILD_TESTS */
00598 
00603 void
00604 _dbus_user_database_unref (DBusUserDatabase  *db)
00605 {
00606   _dbus_assert (db->refcount > 0);
00607 
00608   db->refcount -= 1;
00609   if (db->refcount == 0)
00610     {
00611       if (db->users)
00612         _dbus_hash_table_unref (db->users);
00613 
00614       if (db->groups)
00615         _dbus_hash_table_unref (db->groups);
00616 
00617       if (db->users_by_name)
00618         _dbus_hash_table_unref (db->users_by_name);
00619 
00620       if (db->groups_by_name)
00621         _dbus_hash_table_unref (db->groups_by_name);
00622       
00623       dbus_free (db);
00624     }
00625 }
00626 
00637 dbus_bool_t
00638 _dbus_user_database_get_uid (DBusUserDatabase    *db,
00639                              dbus_uid_t           uid,
00640                              const DBusUserInfo **info,
00641                              DBusError           *error)
00642 {
00643   *info = _dbus_user_database_lookup (db, uid, NULL, error);
00644   return *info != NULL;
00645 }
00646 
00656 dbus_bool_t
00657 _dbus_user_database_get_username  (DBusUserDatabase     *db,
00658                                    const DBusString     *username,
00659                                    const DBusUserInfo  **info,
00660                                    DBusError            *error)
00661 {
00662   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
00663   return *info != NULL;
00664 }
00665 
00668 /* Tests in dbus-userdb-util.c */