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 2.0
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 
00032 struct DBusUserDatabase
00033 {
00034   int refcount; 
00036   DBusHashTable *users; 
00037   DBusHashTable *groups; 
00038   DBusHashTable *users_by_name; 
00039   DBusHashTable *groups_by_name; 
00040 };
00041 
00042 static void
00043 free_user_info (void *data)
00044 {
00045   DBusUserInfo *info = data;
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 
00054 static void
00055 free_group_info (void *data)
00056 {
00057   DBusGroupInfo *info = data;
00058 
00059   if (info == NULL) /* hash table will pass NULL */
00060     return;
00061 
00062   _dbus_group_info_free (info);
00063   dbus_free (info);
00064 }
00065 
00066 static DBusUserInfo*
00067 _dbus_user_database_lookup (DBusUserDatabase *db,
00068                             dbus_uid_t        uid,
00069                             const DBusString *username,
00070                             DBusError        *error)
00071 {
00072   DBusUserInfo *info;
00073 
00074   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00075   _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
00076   
00077   if (uid != DBUS_UID_UNSET)
00078     info = _dbus_hash_table_lookup_ulong (db->users, uid);
00079   else
00080     info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
00081   
00082   if (info)
00083     {
00084       _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
00085                      uid);
00086       return info;
00087     }
00088   else
00089     {
00090       _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
00091                      uid);
00092       
00093       info = dbus_new0 (DBusUserInfo, 1);
00094       if (info == NULL)
00095         {
00096           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00097           return NULL;
00098         }
00099 
00100       if (uid != DBUS_UID_UNSET)
00101         {
00102           if (!_dbus_user_info_fill_uid (info, uid, error))
00103             {
00104               _DBUS_ASSERT_ERROR_IS_SET (error);
00105               free_user_info (info);
00106               return NULL;
00107             }
00108         }
00109       else
00110         {
00111           if (!_dbus_user_info_fill (info, username, error))
00112             {
00113               _DBUS_ASSERT_ERROR_IS_SET (error);
00114               free_user_info (info);
00115               return NULL;
00116             }
00117         }
00118 
00119       /* be sure we don't use these after here */
00120       uid = DBUS_UID_UNSET;
00121       username = NULL;
00122 
00123       /* insert into hash */
00124       if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
00125         {
00126           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00127           free_user_info (info);
00128           return NULL;
00129         }
00130 
00131       if (!_dbus_hash_table_insert_string (db->users_by_name,
00132                                            info->username,
00133                                            info))
00134         {
00135           _dbus_hash_table_remove_ulong (db->users, info->uid);
00136           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00137           return NULL;
00138         }
00139       
00140       return info;
00141     }
00142 }
00143 
00144 static DBusGroupInfo*
00145 _dbus_user_database_lookup_group (DBusUserDatabase *db,
00146                                   dbus_gid_t        gid,
00147                                   const DBusString *groupname,
00148                                   DBusError        *error)
00149 {
00150   DBusGroupInfo *info;
00151 
00152   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00153 
00154   if (gid != DBUS_GID_UNSET)
00155     info = _dbus_hash_table_lookup_ulong (db->groups, gid);
00156   else
00157     info = _dbus_hash_table_lookup_string (db->groups_by_name,
00158                                            _dbus_string_get_const_data (groupname));
00159   if (info)
00160     {
00161       _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
00162                      gid);
00163       return info;
00164     }
00165   else
00166     {
00167       _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
00168                      gid);
00169       
00170       info = dbus_new0 (DBusGroupInfo, 1);
00171       if (info == NULL)
00172         {
00173           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00174           return NULL;
00175         }
00176 
00177       if (!_dbus_group_info_fill_gid (info, gid, error))
00178         {
00179           _DBUS_ASSERT_ERROR_IS_SET (error);
00180           free_group_info (info);
00181           return NULL;
00182         }
00183 
00184       if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info))
00185         {
00186           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00187           free_group_info (info);
00188           return NULL;
00189         }
00190 
00191 
00192       if (!_dbus_hash_table_insert_string (db->groups_by_name,
00193                                            info->groupname,
00194                                            info))
00195         {
00196           _dbus_hash_table_remove_ulong (db->groups, info->gid);
00197           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00198           return NULL;
00199         }
00200       
00201       return info;
00202     }
00203 }
00204 
00205 _DBUS_DEFINE_GLOBAL_LOCK(system_users);
00206 static dbus_bool_t database_locked = FALSE;
00207 static DBusUserDatabase *system_db = NULL;
00208 static DBusString process_username;
00209 static DBusString process_homedir;
00210       
00211 static void
00212 shutdown_system_db (void *data)
00213 {
00214   _dbus_user_database_unref (system_db);
00215   system_db = NULL;
00216   _dbus_string_free (&process_username);
00217   _dbus_string_free (&process_homedir);
00218 }
00219 
00220 static dbus_bool_t
00221 init_system_db (void)
00222 {
00223   _dbus_assert (database_locked);
00224     
00225   if (system_db == NULL)
00226     {
00227       DBusError error;
00228       const DBusUserInfo *info;
00229       
00230       system_db = _dbus_user_database_new ();
00231       if (system_db == NULL)
00232         return FALSE;
00233 
00234       dbus_error_init (&error);
00235 
00236       if (!_dbus_user_database_get_uid (system_db,
00237                                         _dbus_getuid (),
00238                                         &info,
00239                                         &error))
00240         {
00241           _dbus_user_database_unref (system_db);
00242           system_db = NULL;
00243           
00244           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
00245             {
00246               dbus_error_free (&error);
00247               return FALSE;
00248             }
00249           else
00250             {
00251               /* This really should not happen. */
00252               _dbus_warn ("Could not get password database information for UID of current process: %s\n",
00253                           error.message);
00254               dbus_error_free (&error);
00255               return FALSE;
00256             }
00257         }
00258 
00259       if (!_dbus_string_init (&process_username))
00260         {
00261           _dbus_user_database_unref (system_db);
00262           system_db = NULL;
00263           return FALSE;
00264         }
00265 
00266       if (!_dbus_string_init (&process_homedir))
00267         {
00268           _dbus_string_free (&process_username);
00269           _dbus_user_database_unref (system_db);
00270           system_db = NULL;
00271           return FALSE;
00272         }
00273 
00274       if (!_dbus_string_append (&process_username,
00275                                 info->username) ||
00276           !_dbus_string_append (&process_homedir,
00277                                 info->homedir) ||
00278           !_dbus_register_shutdown_func (shutdown_system_db, NULL))
00279         {
00280           _dbus_string_free (&process_username);
00281           _dbus_string_free (&process_homedir);
00282           _dbus_user_database_unref (system_db);
00283           system_db = NULL;
00284           return FALSE;
00285         }
00286     }
00287 
00288   return TRUE;
00289 }
00290 
00299 void
00300 _dbus_user_database_lock_system (void)
00301 {
00302   _DBUS_LOCK (system_users);
00303   database_locked = TRUE;
00304 }
00305 
00309 void
00310 _dbus_user_database_unlock_system (void)
00311 {
00312   database_locked = FALSE;
00313   _DBUS_UNLOCK (system_users);
00314 }
00315 
00322 DBusUserDatabase*
00323 _dbus_user_database_get_system (void)
00324 {
00325   _dbus_assert (database_locked);
00326 
00327   init_system_db ();
00328   
00329   return system_db;
00330 }
00331 
00339 dbus_bool_t
00340 _dbus_username_from_current_process (const DBusString **username)
00341 {
00342   _dbus_user_database_lock_system ();
00343   if (!init_system_db ())
00344     {
00345       _dbus_user_database_unlock_system ();
00346       return FALSE;
00347     }
00348   *username = &process_username;
00349   _dbus_user_database_unlock_system ();  
00350 
00351   return TRUE;
00352 }
00353 
00361 dbus_bool_t
00362 _dbus_homedir_from_current_process (const DBusString  **homedir)
00363 {
00364   _dbus_user_database_lock_system ();
00365   if (!init_system_db ())
00366     {
00367       _dbus_user_database_unlock_system ();
00368       return FALSE;
00369     }
00370   *homedir = &process_homedir;
00371   _dbus_user_database_unlock_system ();
00372 
00373   return TRUE;
00374 }
00375 
00383 dbus_bool_t
00384 _dbus_get_user_id (const DBusString  *username,
00385                    dbus_uid_t        *uid)
00386 {
00387   DBusCredentials creds;
00388 
00389   if (!_dbus_credentials_from_username (username, &creds))
00390     return FALSE;
00391 
00392   if (creds.uid == DBUS_UID_UNSET)
00393     return FALSE;
00394 
00395   *uid = creds.uid;
00396 
00397   return TRUE;
00398 }
00399 
00407 dbus_bool_t
00408 _dbus_get_group_id (const DBusString  *groupname,
00409                     dbus_gid_t        *gid)
00410 {
00411   DBusUserDatabase *db;
00412   const DBusGroupInfo *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_groupname (db, groupname,
00423                                           &info, NULL))
00424     {
00425       _dbus_user_database_unlock_system ();
00426       return FALSE;
00427     }
00428 
00429   *gid = info->gid;
00430   
00431   _dbus_user_database_unlock_system ();
00432   return TRUE;
00433 }
00434 
00442 dbus_bool_t
00443 _dbus_homedir_from_username (const DBusString *username,
00444                              DBusString       *homedir)
00445 {
00446   DBusUserDatabase *db;
00447   const DBusUserInfo *info;
00448   _dbus_user_database_lock_system ();
00449 
00450   db = _dbus_user_database_get_system ();
00451   if (db == NULL)
00452     {
00453       _dbus_user_database_unlock_system ();
00454       return FALSE;
00455     }
00456 
00457   if (!_dbus_user_database_get_username (db, username,
00458                                          &info, NULL))
00459     {
00460       _dbus_user_database_unlock_system ();
00461       return FALSE;
00462     }
00463 
00464   if (!_dbus_string_append (homedir, info->homedir))
00465     {
00466       _dbus_user_database_unlock_system ();
00467       return FALSE;
00468     }
00469   
00470   _dbus_user_database_unlock_system ();
00471   return TRUE;
00472 }
00473 
00481 dbus_bool_t
00482 _dbus_uid_from_string (const DBusString      *uid_str,
00483                        dbus_uid_t            *uid)
00484 {
00485   int end;
00486   long val;
00487   
00488   if (_dbus_string_get_length (uid_str) == 0)
00489     {
00490       _dbus_verbose ("UID string was zero length\n");
00491       return FALSE;
00492     }
00493 
00494   val = -1;
00495   end = 0;
00496   if (!_dbus_string_parse_int (uid_str, 0, &val,
00497                                &end))
00498     {
00499       _dbus_verbose ("could not parse string as a UID\n");
00500       return FALSE;
00501     }
00502   
00503   if (end != _dbus_string_get_length (uid_str))
00504     {
00505       _dbus_verbose ("string contained trailing stuff after UID\n");
00506       return FALSE;
00507     }
00508 
00509   *uid = val;
00510 
00511   return TRUE;
00512 }
00513 
00521 dbus_bool_t
00522 _dbus_credentials_from_username (const DBusString *username,
00523                                  DBusCredentials  *credentials)
00524 {
00525   DBusUserDatabase *db;
00526   const DBusUserInfo *info;
00527   _dbus_user_database_lock_system ();
00528 
00529   db = _dbus_user_database_get_system ();
00530   if (db == NULL)
00531     {
00532       _dbus_user_database_unlock_system ();
00533       return FALSE;
00534     }
00535 
00536   if (!_dbus_user_database_get_username (db, username,
00537                                          &info, NULL))
00538     {
00539       _dbus_user_database_unlock_system ();
00540       return FALSE;
00541     }
00542 
00543   credentials->pid = DBUS_PID_UNSET;
00544   credentials->uid = info->uid;
00545   credentials->gid = info->primary_gid;
00546   
00547   _dbus_user_database_unlock_system ();
00548   return TRUE;
00549 }
00550 
00558 dbus_bool_t
00559 _dbus_credentials_from_uid (dbus_uid_t        uid,
00560                             DBusCredentials  *credentials)
00561 {
00562   DBusUserDatabase *db;
00563   const DBusUserInfo *info;
00564   _dbus_user_database_lock_system ();
00565 
00566   db = _dbus_user_database_get_system ();
00567   if (db == NULL)
00568     {
00569       _dbus_user_database_unlock_system ();
00570       return FALSE;
00571     }
00572 
00573   if (!_dbus_user_database_get_uid (db, uid,
00574                                     &info, NULL))
00575     {
00576       _dbus_user_database_unlock_system ();
00577       return FALSE;
00578     }
00579 
00580   _dbus_assert (info->uid == uid);
00581   
00582   credentials->pid = DBUS_PID_UNSET;
00583   credentials->uid = info->uid;
00584   credentials->gid = info->primary_gid;
00585   
00586   _dbus_user_database_unlock_system ();
00587   return TRUE;
00588 }
00589 
00595 DBusUserDatabase*
00596 _dbus_user_database_new (void)
00597 {
00598   DBusUserDatabase *db;
00599   
00600   db = dbus_new0 (DBusUserDatabase, 1);
00601   if (db == NULL)
00602     return NULL;
00603 
00604   db->refcount = 1;
00605 
00606   db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
00607                                     NULL, free_user_info);
00608   
00609   if (db->users == NULL)
00610     goto failed;
00611 
00612   db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
00613                                      NULL, free_group_info);
00614   
00615   if (db->groups == NULL)
00616     goto failed;
00617 
00618   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00619                                             NULL, NULL);
00620   if (db->users_by_name == NULL)
00621     goto failed;
00622   
00623   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00624                                              NULL, NULL);
00625   if (db->groups_by_name == NULL)
00626     goto failed;
00627   
00628   return db;
00629   
00630  failed:
00631   _dbus_user_database_unref (db);
00632   return NULL;
00633 }
00634 
00640 DBusUserDatabase *
00641 _dbus_user_database_ref (DBusUserDatabase  *db)
00642 {
00643   _dbus_assert (db->refcount > 0);
00644 
00645   db->refcount += 1;
00646 
00647   return db;
00648 }
00649 
00654 void
00655 _dbus_user_database_unref (DBusUserDatabase  *db)
00656 {
00657   _dbus_assert (db->refcount > 0);
00658 
00659   db->refcount -= 1;
00660   if (db->refcount == 0)
00661     {
00662       if (db->users)
00663         _dbus_hash_table_unref (db->users);
00664 
00665       if (db->groups)
00666         _dbus_hash_table_unref (db->groups);
00667 
00668       if (db->users_by_name)
00669         _dbus_hash_table_unref (db->users_by_name);
00670 
00671       if (db->groups_by_name)
00672         _dbus_hash_table_unref (db->groups_by_name);
00673       
00674       dbus_free (db);
00675     }
00676 }
00677 
00691 dbus_bool_t
00692 _dbus_user_database_get_groups (DBusUserDatabase  *db,
00693                                 dbus_uid_t         uid,
00694                                 dbus_gid_t       **group_ids,
00695                                 int               *n_group_ids,
00696                                 DBusError         *error)
00697 {
00698   DBusUserInfo *info;
00699   
00700   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00701 
00702   *group_ids = NULL;
00703   *n_group_ids = 0;
00704   
00705   info = _dbus_user_database_lookup (db, uid, NULL, error);
00706   if (info == NULL)
00707     {
00708       _DBUS_ASSERT_ERROR_IS_SET (error);
00709       return FALSE;
00710     }
00711 
00712   if (info->n_group_ids > 0)
00713     {
00714       *group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
00715       if (*group_ids == NULL)
00716         {
00717           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00718           return FALSE;
00719         }
00720 
00721       *n_group_ids = info->n_group_ids;
00722 
00723       memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
00724     }
00725 
00726   return TRUE;
00727 }
00728 
00739 dbus_bool_t
00740 _dbus_user_database_get_uid (DBusUserDatabase    *db,
00741                              dbus_uid_t           uid,
00742                              const DBusUserInfo **info,
00743                              DBusError           *error)
00744 {
00745   *info = _dbus_user_database_lookup (db, uid, NULL, error);
00746   return *info != NULL;
00747 }
00748 
00759 dbus_bool_t
00760 _dbus_user_database_get_gid (DBusUserDatabase     *db,
00761                              dbus_gid_t            gid,
00762                              const DBusGroupInfo **info,
00763                              DBusError            *error)
00764 {
00765   *info = _dbus_user_database_lookup_group (db, gid, NULL, error);
00766   return *info != NULL;
00767 }
00768 
00778 dbus_bool_t
00779 _dbus_user_database_get_username  (DBusUserDatabase     *db,
00780                                    const DBusString     *username,
00781                                    const DBusUserInfo  **info,
00782                                    DBusError            *error)
00783 {
00784   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
00785   return *info != NULL;
00786 }
00787 
00798 dbus_bool_t
00799 _dbus_user_database_get_groupname (DBusUserDatabase     *db,
00800                                    const DBusString     *groupname,
00801                                    const DBusGroupInfo **info,
00802                                    DBusError            *error)
00803 {
00804   *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
00805   return *info != NULL;
00806 }
00807 
00810 #ifdef DBUS_BUILD_TESTS
00811 #include <stdio.h>
00812 
00818 dbus_bool_t
00819 _dbus_userdb_test (const char *test_data_dir)
00820 {
00821   const DBusString *username;
00822   const DBusString *homedir;
00823 
00824   if (!_dbus_username_from_current_process (&username))
00825     _dbus_assert_not_reached ("didn't get username");
00826 
00827   if (!_dbus_homedir_from_current_process (&homedir))
00828     _dbus_assert_not_reached ("didn't get homedir");  
00829 
00830   printf ("    Current user: %s homedir: %s\n",
00831           _dbus_string_get_const_data (username),
00832           _dbus_string_get_const_data (homedir));
00833   
00834   return TRUE;
00835 }
00836 #endif /* DBUS_BUILD_TESTS */

Generated on Wed Jun 9 05:01:27 2004 for D-BUS by doxygen1.2.15