Main Page   Modules   Data Structures   File List   Data Fields   Related Pages  

dbus-userdb.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* dbus-userdb.c User database abstraction
00003  * 
00004  * Copyright (C) 2003  Red Hat, Inc.
00005  *
00006  * Licensed under the Academic Free License version 1.2
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 #include "dbus-userdb.h"
00024 #include "dbus-hash.h"
00025 #include "dbus-test.h"
00026 #include "dbus-internals.h"
00027 #include <string.h>
00028 
00029 struct DBusUserDatabase
00030 {
00031   int refcount;
00032 
00033   DBusHashTable *users;
00034   DBusHashTable *groups;
00035   DBusHashTable *users_by_name;
00036   DBusHashTable *groups_by_name;
00037 };
00038 
00039 static void
00040 free_user_info (void *data)
00041 {
00042   DBusUserInfo *info = data;
00043 
00044   if (info == NULL) /* hash table will pass NULL */
00045     return;
00046 
00047   _dbus_user_info_free (info);
00048   dbus_free (info);
00049 }
00050 
00051 static void
00052 free_group_info (void *data)
00053 {
00054   DBusGroupInfo *info = data;
00055 
00056   if (info == NULL) /* hash table will pass NULL */
00057     return;
00058 
00059   _dbus_group_info_free (info);
00060   dbus_free (info);
00061 }
00062 
00063 static DBusUserInfo*
00064 _dbus_user_database_lookup (DBusUserDatabase *db,
00065                             dbus_uid_t        uid,
00066                             const DBusString *username,
00067                             DBusError        *error)
00068 {
00069   DBusUserInfo *info;
00070 
00071   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00072   _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
00073   
00074   if (uid != DBUS_UID_UNSET)
00075     info = _dbus_hash_table_lookup_ulong (db->users, uid);
00076   else
00077     info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
00078   
00079   if (info)
00080     {
00081       _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
00082                      uid);
00083       return info;
00084     }
00085   else
00086     {
00087       _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
00088                      uid);
00089       
00090       info = dbus_new0 (DBusUserInfo, 1);
00091       if (info == NULL)
00092         {
00093           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00094           return NULL;
00095         }
00096 
00097       if (uid != DBUS_UID_UNSET)
00098         {
00099           if (!_dbus_user_info_fill_uid (info, uid, error))
00100             {
00101               _DBUS_ASSERT_ERROR_IS_SET (error);
00102               free_user_info (info);
00103               return NULL;
00104             }
00105         }
00106       else
00107         {
00108           if (!_dbus_user_info_fill (info, username, error))
00109             {
00110               _DBUS_ASSERT_ERROR_IS_SET (error);
00111               free_user_info (info);
00112               return NULL;
00113             }
00114         }
00115 
00116       /* be sure we don't use these after here */
00117       uid = DBUS_UID_UNSET;
00118       username = NULL;
00119 
00120       /* insert into hash */
00121       if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
00122         {
00123           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00124           free_user_info (info);
00125           return NULL;
00126         }
00127 
00128       if (!_dbus_hash_table_insert_string (db->users_by_name,
00129                                            info->username,
00130                                            info))
00131         {
00132           _dbus_hash_table_remove_ulong (db->users, info->uid);
00133           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00134           return NULL;
00135         }
00136       
00137       return info;
00138     }
00139 }
00140 
00141 static DBusGroupInfo*
00142 _dbus_user_database_lookup_group (DBusUserDatabase *db,
00143                                   dbus_gid_t        gid,
00144                                   const DBusString *groupname,
00145                                   DBusError        *error)
00146 {
00147   DBusGroupInfo *info;
00148 
00149   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00150 
00151   if (gid != DBUS_GID_UNSET)
00152     info = _dbus_hash_table_lookup_ulong (db->groups, gid);
00153   else
00154     info = _dbus_hash_table_lookup_string (db->groups_by_name,
00155                                            _dbus_string_get_const_data (groupname));
00156   if (info)
00157     {
00158       _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
00159                      gid);
00160       return info;
00161     }
00162   else
00163     {
00164       _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
00165                      gid);
00166       
00167       info = dbus_new0 (DBusGroupInfo, 1);
00168       if (info == NULL)
00169         {
00170           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00171           return NULL;
00172         }
00173 
00174       if (!_dbus_group_info_fill_gid (info, gid, error))
00175         {
00176           _DBUS_ASSERT_ERROR_IS_SET (error);
00177           free_group_info (info);
00178           return NULL;
00179         }
00180 
00181       if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info))
00182         {
00183           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00184           free_group_info (info);
00185           return NULL;
00186         }
00187 
00188 
00189       if (!_dbus_hash_table_insert_string (db->groups_by_name,
00190                                            info->groupname,
00191                                            info))
00192         {
00193           _dbus_hash_table_remove_ulong (db->groups, info->gid);
00194           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00195           return NULL;
00196         }
00197       
00198       return info;
00199     }
00200 }
00201 
00202 _DBUS_DEFINE_GLOBAL_LOCK(system_users);
00203 static dbus_bool_t database_locked = FALSE;
00204 static DBusUserDatabase *system_db = NULL;
00205 static DBusString process_username;
00206 static DBusString process_homedir;
00207       
00208 static void
00209 shutdown_system_db (void *data)
00210 {
00211   _dbus_user_database_unref (system_db);
00212   system_db = NULL;
00213   _dbus_string_free (&process_username);
00214   _dbus_string_free (&process_homedir);
00215 }
00216 
00217 static dbus_bool_t
00218 init_system_db (void)
00219 {
00220   _dbus_assert (database_locked);
00221     
00222   if (system_db == NULL)
00223     {
00224       DBusError error;
00225       const DBusUserInfo *info;
00226       
00227       system_db = _dbus_user_database_new ();
00228       if (system_db == NULL)
00229         return FALSE;
00230 
00231       dbus_error_init (&error);
00232 
00233       if (!_dbus_user_database_get_uid (system_db,
00234                                         _dbus_getuid (),
00235                                         &info,
00236                                         &error))
00237         {
00238           _dbus_user_database_unref (system_db);
00239           system_db = NULL;
00240           
00241           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
00242             {
00243               dbus_error_free (&error);
00244               return FALSE;
00245             }
00246           else
00247             {
00248               /* This really should not happen. */
00249               _dbus_warn ("Could not get password database information for UID of current process: %s\n",
00250                           error.message);
00251               dbus_error_free (&error);
00252               return FALSE;
00253             }
00254         }
00255 
00256       if (!_dbus_string_init (&process_username))
00257         {
00258           _dbus_user_database_unref (system_db);
00259           system_db = NULL;
00260           return FALSE;
00261         }
00262 
00263       if (!_dbus_string_init (&process_homedir))
00264         {
00265           _dbus_string_free (&process_username);
00266           _dbus_user_database_unref (system_db);
00267           system_db = NULL;
00268           return FALSE;
00269         }
00270 
00271       if (!_dbus_string_append (&process_username,
00272                                 info->username) ||
00273           !_dbus_string_append (&process_homedir,
00274                                 info->homedir) ||
00275           !_dbus_register_shutdown_func (shutdown_system_db, NULL))
00276         {
00277           _dbus_string_free (&process_username);
00278           _dbus_string_free (&process_homedir);
00279           _dbus_user_database_unref (system_db);
00280           system_db = NULL;
00281           return FALSE;
00282         }
00283     }
00284 
00285   return TRUE;
00286 }
00287 
00296 void
00297 _dbus_user_database_lock_system (void)
00298 {
00299   _DBUS_LOCK (system_users);
00300   database_locked = TRUE;
00301 }
00302 
00306 void
00307 _dbus_user_database_unlock_system (void)
00308 {
00309   database_locked = FALSE;
00310   _DBUS_UNLOCK (system_users);
00311 }
00312 
00319 DBusUserDatabase*
00320 _dbus_user_database_get_system (void)
00321 {
00322   _dbus_assert (database_locked);
00323 
00324   init_system_db ();
00325   
00326   return system_db;
00327 }
00328 
00336 dbus_bool_t
00337 _dbus_username_from_current_process (const DBusString **username)
00338 {
00339   _dbus_user_database_lock_system ();
00340   if (!init_system_db ())
00341     {
00342       _dbus_user_database_unlock_system ();
00343       return FALSE;
00344     }
00345   *username = &process_username;
00346   _dbus_user_database_unlock_system ();  
00347 
00348   return TRUE;
00349 }
00350 
00358 dbus_bool_t
00359 _dbus_homedir_from_current_process (const DBusString  **homedir)
00360 {
00361   _dbus_user_database_lock_system ();
00362   if (!init_system_db ())
00363     {
00364       _dbus_user_database_unlock_system ();
00365       return FALSE;
00366     }
00367   *homedir = &process_homedir;
00368   _dbus_user_database_unlock_system ();
00369 
00370   return TRUE;
00371 }
00372 
00380 dbus_bool_t
00381 _dbus_get_user_id (const DBusString  *username,
00382                    dbus_uid_t        *uid)
00383 {
00384   DBusCredentials creds;
00385 
00386   if (!_dbus_credentials_from_username (username, &creds))
00387     return FALSE;
00388 
00389   if (creds.uid == DBUS_UID_UNSET)
00390     return FALSE;
00391 
00392   *uid = creds.uid;
00393 
00394   return TRUE;
00395 }
00396 
00404 dbus_bool_t
00405 _dbus_get_group_id (const DBusString  *groupname,
00406                     dbus_gid_t        *gid)
00407 {
00408   DBusUserDatabase *db;
00409   const DBusGroupInfo *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_groupname (db, groupname,
00420                                           &info, NULL))
00421     {
00422       _dbus_user_database_unlock_system ();
00423       return FALSE;
00424     }
00425 
00426   *gid = info->gid;
00427   
00428   _dbus_user_database_unlock_system ();
00429   return TRUE;
00430 }
00431 
00439 dbus_bool_t
00440 _dbus_homedir_from_username (const DBusString *username,
00441                              DBusString       *homedir)
00442 {
00443   DBusUserDatabase *db;
00444   const DBusUserInfo *info;
00445   _dbus_user_database_lock_system ();
00446 
00447   db = _dbus_user_database_get_system ();
00448   if (db == NULL)
00449     {
00450       _dbus_user_database_unlock_system ();
00451       return FALSE;
00452     }
00453 
00454   if (!_dbus_user_database_get_username (db, username,
00455                                          &info, NULL))
00456     {
00457       _dbus_user_database_unlock_system ();
00458       return FALSE;
00459     }
00460 
00461   if (!_dbus_string_append (homedir, info->homedir))
00462     {
00463       _dbus_user_database_unlock_system ();
00464       return FALSE;
00465     }
00466   
00467   _dbus_user_database_unlock_system ();
00468   return TRUE;
00469 }
00470 
00478 dbus_bool_t
00479 _dbus_uid_from_string (const DBusString      *uid_str,
00480                        dbus_uid_t            *uid)
00481 {
00482   int end;
00483   long val;
00484   
00485   if (_dbus_string_get_length (uid_str) == 0)
00486     {
00487       _dbus_verbose ("UID string was zero length\n");
00488       return FALSE;
00489     }
00490 
00491   val = -1;
00492   end = 0;
00493   if (!_dbus_string_parse_int (uid_str, 0, &val,
00494                                &end))
00495     {
00496       _dbus_verbose ("could not parse string as a UID\n");
00497       return FALSE;
00498     }
00499   
00500   if (end != _dbus_string_get_length (uid_str))
00501     {
00502       _dbus_verbose ("string contained trailing stuff after UID\n");
00503       return FALSE;
00504     }
00505 
00506   *uid = val;
00507 
00508   return TRUE;
00509 }
00510 
00518 dbus_bool_t
00519 _dbus_credentials_from_username (const DBusString *username,
00520                                  DBusCredentials  *credentials)
00521 {
00522   DBusUserDatabase *db;
00523   const DBusUserInfo *info;
00524   _dbus_user_database_lock_system ();
00525 
00526   db = _dbus_user_database_get_system ();
00527   if (db == NULL)
00528     {
00529       _dbus_user_database_unlock_system ();
00530       return FALSE;
00531     }
00532 
00533   if (!_dbus_user_database_get_username (db, username,
00534                                          &info, NULL))
00535     {
00536       _dbus_user_database_unlock_system ();
00537       return FALSE;
00538     }
00539 
00540   credentials->pid = DBUS_PID_UNSET;
00541   credentials->uid = info->uid;
00542   credentials->gid = info->primary_gid;
00543   
00544   _dbus_user_database_unlock_system ();
00545   return TRUE;
00546 }
00547 
00555 dbus_bool_t
00556 _dbus_credentials_from_uid (dbus_uid_t        uid,
00557                             DBusCredentials  *credentials)
00558 {
00559   DBusUserDatabase *db;
00560   const DBusUserInfo *info;
00561   _dbus_user_database_lock_system ();
00562 
00563   db = _dbus_user_database_get_system ();
00564   if (db == NULL)
00565     {
00566       _dbus_user_database_unlock_system ();
00567       return FALSE;
00568     }
00569 
00570   if (!_dbus_user_database_get_uid (db, uid,
00571                                     &info, NULL))
00572     {
00573       _dbus_user_database_unlock_system ();
00574       return FALSE;
00575     }
00576 
00577   _dbus_assert (info->uid == uid);
00578   
00579   credentials->pid = DBUS_PID_UNSET;
00580   credentials->uid = info->uid;
00581   credentials->gid = info->primary_gid;
00582   
00583   _dbus_user_database_unlock_system ();
00584   return TRUE;
00585 }
00586 
00592 DBusUserDatabase*
00593 _dbus_user_database_new (void)
00594 {
00595   DBusUserDatabase *db;
00596   
00597   db = dbus_new0 (DBusUserDatabase, 1);
00598   if (db == NULL)
00599     return NULL;
00600 
00601   db->refcount = 1;
00602 
00603   db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
00604                                     NULL, free_user_info);
00605   
00606   if (db->users == NULL)
00607     goto failed;
00608 
00609   db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
00610                                      NULL, free_group_info);
00611   
00612   if (db->groups == NULL)
00613     goto failed;
00614 
00615   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00616                                             NULL, NULL);
00617   if (db->users_by_name == NULL)
00618     goto failed;
00619   
00620   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00621                                              NULL, NULL);
00622   if (db->groups_by_name == NULL)
00623     goto failed;
00624   
00625   return db;
00626   
00627  failed:
00628   _dbus_user_database_unref (db);
00629   return NULL;
00630 }
00631 
00636 void
00637 _dbus_user_database_ref (DBusUserDatabase  *db)
00638 {
00639   _dbus_assert (db->refcount > 0);
00640 
00641   db->refcount += 1;
00642 }
00643 
00648 void
00649 _dbus_user_database_unref (DBusUserDatabase  *db)
00650 {
00651   _dbus_assert (db->refcount > 0);
00652 
00653   db->refcount -= 1;
00654   if (db->refcount == 0)
00655     {
00656       if (db->users)
00657         _dbus_hash_table_unref (db->users);
00658 
00659       if (db->groups)
00660         _dbus_hash_table_unref (db->groups);
00661 
00662       if (db->users_by_name)
00663         _dbus_hash_table_unref (db->users_by_name);
00664 
00665       if (db->groups_by_name)
00666         _dbus_hash_table_unref (db->groups_by_name);
00667       
00668       dbus_free (db);
00669     }
00670 }
00671 
00685 dbus_bool_t
00686 _dbus_user_database_get_groups (DBusUserDatabase  *db,
00687                                 dbus_uid_t         uid,
00688                                 dbus_gid_t       **group_ids,
00689                                 int               *n_group_ids,
00690                                 DBusError         *error)
00691 {
00692   DBusUserInfo *info;
00693   
00694   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00695 
00696   *group_ids = NULL;
00697   *n_group_ids = 0;
00698   
00699   info = _dbus_user_database_lookup (db, uid, NULL, error);
00700   if (info == NULL)
00701     {
00702       _DBUS_ASSERT_ERROR_IS_SET (error);
00703       return FALSE;
00704     }
00705 
00706   if (info->n_group_ids > 0)
00707     {
00708       *group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
00709       if (*group_ids == NULL)
00710         {
00711           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00712           return FALSE;
00713         }
00714 
00715       *n_group_ids = info->n_group_ids;
00716 
00717       memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
00718     }
00719 
00720   return TRUE;
00721 }
00722 
00733 dbus_bool_t
00734 _dbus_user_database_get_uid (DBusUserDatabase    *db,
00735                              dbus_uid_t           uid,
00736                              const DBusUserInfo **info,
00737                              DBusError           *error)
00738 {
00739   *info = _dbus_user_database_lookup (db, uid, NULL, error);
00740   return *info != NULL;
00741 }
00742 
00753 dbus_bool_t
00754 _dbus_user_database_get_gid (DBusUserDatabase     *db,
00755                              dbus_gid_t            gid,
00756                              const DBusGroupInfo **info,
00757                              DBusError            *error)
00758 {
00759   *info = _dbus_user_database_lookup_group (db, gid, NULL, error);
00760   return *info != NULL;
00761 }
00762 
00772 dbus_bool_t
00773 _dbus_user_database_get_username  (DBusUserDatabase     *db,
00774                                    const DBusString     *username,
00775                                    const DBusUserInfo  **info,
00776                                    DBusError            *error)
00777 {
00778   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
00779   return *info != NULL;
00780 }
00781 
00792 dbus_bool_t
00793 _dbus_user_database_get_groupname (DBusUserDatabase     *db,
00794                                    const DBusString     *groupname,
00795                                    const DBusGroupInfo **info,
00796                                    DBusError            *error)
00797 {
00798   *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
00799   return *info != NULL;
00800 }
00801 
00804 #ifdef DBUS_BUILD_TESTS
00805 #include <stdio.h>
00806 
00812 dbus_bool_t
00813 _dbus_userdb_test (const char *test_data_dir)
00814 {
00815   const DBusString *username;
00816   const DBusString *homedir;
00817 
00818   if (!_dbus_username_from_current_process (&username))
00819     _dbus_assert_not_reached ("didn't get username");
00820 
00821   if (!_dbus_homedir_from_current_process (&homedir))
00822     _dbus_assert_not_reached ("didn't get homedir");  
00823 
00824   printf ("    Current user: %s homedir: %s\n",
00825           _dbus_string_get_const_data (username),
00826           _dbus_string_get_const_data (homedir));
00827   
00828   return TRUE;
00829 }
00830 #endif /* DBUS_BUILD_TESTS */

Generated on Wed Oct 22 14:05:05 2003 for D-BUS by doxygen1.3-rc3