Main Page | Modules | Data Structures | Directories | 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, 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 #include "dbus-userdb.h"
00024 #include "dbus-hash.h"
00025 #include "dbus-test.h"
00026 #include "dbus-internals.h"
00027 #include "dbus-protocol.h"
00028 #include <string.h>
00029 
00033 struct DBusUserDatabase
00034 {
00035   int refcount; 
00037   DBusHashTable *users; 
00038   DBusHashTable *groups; 
00039   DBusHashTable *users_by_name; 
00040   DBusHashTable *groups_by_name; 
00042 };
00043 
00044 static void
00045 free_user_info (void *data)
00046 {
00047   DBusUserInfo *info = data;
00048 
00049   if (info == NULL) /* hash table will pass NULL */
00050     return;
00051 
00052   _dbus_user_info_free (info);
00053   dbus_free (info);
00054 }
00055 
00056 static void
00057 free_group_info (void *data)
00058 {
00059   DBusGroupInfo *info = data;
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 
00068 static DBusUserInfo*
00069 _dbus_user_database_lookup (DBusUserDatabase *db,
00070                             dbus_uid_t        uid,
00071                             const DBusString *username,
00072                             DBusError        *error)
00073 {
00074   DBusUserInfo *info;
00075 
00076   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00077   _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
00078   
00079   if (uid != DBUS_UID_UNSET)
00080     info = _dbus_hash_table_lookup_ulong (db->users, uid);
00081   else 
00082     info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
00083   
00084   if (info)
00085     {
00086       _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n", info->uid);
00087       return info;
00088     }
00089   else
00090     {
00091   if (uid != DBUS_UID_UNSET)
00092       _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
00093                      uid);
00094         else
00095       _dbus_verbose ("No cache for user \"%s\"\n",
00096                      _dbus_string_get_const_data(username));
00097     
00098       info = dbus_new0 (DBusUserInfo, 1);
00099       if (info == NULL)
00100         {
00101           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00102           return NULL;
00103         }
00104 
00105       if (uid != DBUS_UID_UNSET)
00106         {
00107           if (!_dbus_user_info_fill_uid (info, uid, error))
00108             {
00109               _DBUS_ASSERT_ERROR_IS_SET (error);
00110               free_user_info (info);
00111               return NULL;
00112             }
00113         }
00114       else
00115         {
00116           if (!_dbus_user_info_fill (info, username, error))
00117             {
00118               _DBUS_ASSERT_ERROR_IS_SET (error);
00119               free_user_info (info);
00120               return NULL;
00121             }
00122         }
00123 
00124       /* be sure we don't use these after here */
00125       uid = DBUS_UID_UNSET;
00126       username = NULL;
00127 
00128       /* insert into hash */
00129       if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
00130         {
00131           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00132           free_user_info (info);
00133           return NULL;
00134         }
00135 
00136       if (!_dbus_hash_table_insert_string (db->users_by_name,
00137                                            info->username,
00138                                            info))
00139         {
00140           _dbus_hash_table_remove_ulong (db->users, info->uid);
00141           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00142           return NULL;
00143         }
00144       
00145       return info;
00146     }
00147 }
00148 
00149 static DBusGroupInfo*
00150 _dbus_user_database_lookup_group (DBusUserDatabase *db,
00151                                   dbus_gid_t        gid,
00152                                   const DBusString *groupname,
00153                                   DBusError        *error)
00154 {
00155   DBusGroupInfo *info;
00156 
00157   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00158 
00159   if (gid != DBUS_GID_UNSET)
00160     info = _dbus_hash_table_lookup_ulong (db->groups, gid);
00161   else
00162     info = _dbus_hash_table_lookup_string (db->groups_by_name,
00163                                            _dbus_string_get_const_data (groupname));
00164   if (info)
00165     {
00166       _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
00167                      info->gid);
00168       return info;
00169     }
00170   else
00171     {
00172       if (gid != DBUS_GID_UNSET)
00173         _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
00174                      gid);
00175           else
00176         _dbus_verbose ("No cache for group \"%s\"\n",
00177                      _dbus_string_get_const_data(groupname));
00178       
00179       info = dbus_new0 (DBusGroupInfo, 1);
00180       if (info == NULL)
00181         {
00182           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00183           return NULL;
00184         }
00185 
00186 
00187       if (gid != DBUS_GID_UNSET)
00188         {
00189           if (!_dbus_group_info_fill_gid (info, gid, error))
00190             {
00191               _DBUS_ASSERT_ERROR_IS_SET (error);
00192               free_group_info (info);
00193               return NULL;
00194             }
00195         }
00196       else
00197         {
00198           if (!_dbus_group_info_fill (info, groupname, error))
00199            {
00200              _DBUS_ASSERT_ERROR_IS_SET (error);
00201              free_group_info (info);
00202              return NULL;
00203            }
00204         }
00205 
00206       if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info))
00207         {
00208           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00209           free_group_info (info);
00210           return NULL;
00211         }
00212 
00213 
00214       if (!_dbus_hash_table_insert_string (db->groups_by_name,
00215                                            info->groupname,
00216                                            info))
00217         {
00218           _dbus_hash_table_remove_ulong (db->groups, info->gid);
00219           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00220           return NULL;
00221         }
00222       
00223       return info;
00224     }
00225 }
00226 
00227 _DBUS_DEFINE_GLOBAL_LOCK(system_users);
00228 static dbus_bool_t database_locked = FALSE;
00229 static DBusUserDatabase *system_db = NULL;
00230 static DBusString process_username;
00231 static DBusString process_homedir;
00232       
00233 static void
00234 shutdown_system_db (void *data)
00235 {
00236   _dbus_user_database_unref (system_db);
00237   system_db = NULL;
00238   _dbus_string_free (&process_username);
00239   _dbus_string_free (&process_homedir);
00240 }
00241 
00242 static dbus_bool_t
00243 init_system_db (void)
00244 {
00245   _dbus_assert (database_locked);
00246     
00247   if (system_db == NULL)
00248     {
00249       DBusError error;
00250       const DBusUserInfo *info;
00251       
00252       system_db = _dbus_user_database_new ();
00253       if (system_db == NULL)
00254         return FALSE;
00255 
00256       dbus_error_init (&error);
00257 
00258       if (!_dbus_user_database_get_uid (system_db,
00259                                         _dbus_getuid (),
00260                                         &info,
00261                                         &error))
00262         {
00263           _dbus_user_database_unref (system_db);
00264           system_db = NULL;
00265           
00266           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
00267             {
00268               dbus_error_free (&error);
00269               return FALSE;
00270             }
00271           else
00272             {
00273               /* This really should not happen. */
00274               _dbus_warn ("Could not get password database information for UID of current process: %s\n",
00275                           error.message);
00276               dbus_error_free (&error);
00277               return FALSE;
00278             }
00279         }
00280 
00281       if (!_dbus_string_init (&process_username))
00282         {
00283           _dbus_user_database_unref (system_db);
00284           system_db = NULL;
00285           return FALSE;
00286         }
00287 
00288       if (!_dbus_string_init (&process_homedir))
00289         {
00290           _dbus_string_free (&process_username);
00291           _dbus_user_database_unref (system_db);
00292           system_db = NULL;
00293           return FALSE;
00294         }
00295 
00296       if (!_dbus_string_append (&process_username,
00297                                 info->username) ||
00298           !_dbus_string_append (&process_homedir,
00299                                 info->homedir) ||
00300           !_dbus_register_shutdown_func (shutdown_system_db, NULL))
00301         {
00302           _dbus_string_free (&process_username);
00303           _dbus_string_free (&process_homedir);
00304           _dbus_user_database_unref (system_db);
00305           system_db = NULL;
00306           return FALSE;
00307         }
00308     }
00309 
00310   return TRUE;
00311 }
00312 
00321 void
00322 _dbus_user_database_lock_system (void)
00323 {
00324   _DBUS_LOCK (system_users);
00325   database_locked = TRUE;
00326 }
00327 
00331 void
00332 _dbus_user_database_unlock_system (void)
00333 {
00334   database_locked = FALSE;
00335   _DBUS_UNLOCK (system_users);
00336 }
00337 
00344 DBusUserDatabase*
00345 _dbus_user_database_get_system (void)
00346 {
00347   _dbus_assert (database_locked);
00348 
00349   init_system_db ();
00350   
00351   return system_db;
00352 }
00353 
00361 dbus_bool_t
00362 _dbus_username_from_current_process (const DBusString **username)
00363 {
00364   _dbus_user_database_lock_system ();
00365   if (!init_system_db ())
00366     {
00367       _dbus_user_database_unlock_system ();
00368       return FALSE;
00369     }
00370   *username = &process_username;
00371   _dbus_user_database_unlock_system ();  
00372 
00373   return TRUE;
00374 }
00375 
00383 dbus_bool_t
00384 _dbus_homedir_from_current_process (const DBusString  **homedir)
00385 {
00386   _dbus_user_database_lock_system ();
00387   if (!init_system_db ())
00388     {
00389       _dbus_user_database_unlock_system ();
00390       return FALSE;
00391     }
00392   *homedir = &process_homedir;
00393   _dbus_user_database_unlock_system ();
00394 
00395   return TRUE;
00396 }
00397 
00405 dbus_bool_t
00406 _dbus_get_user_id (const DBusString  *username,
00407                    dbus_uid_t        *uid)
00408 {
00409   DBusCredentials creds;
00410 
00411   if (!_dbus_credentials_from_username (username, &creds))
00412     return FALSE;
00413 
00414   if (creds.uid == DBUS_UID_UNSET)
00415     return FALSE;
00416 
00417   *uid = creds.uid;
00418 
00419   return TRUE;
00420 }
00421 
00429 dbus_bool_t
00430 _dbus_is_console_user (dbus_uid_t uid,
00431                        DBusError *error)
00432 {
00433 
00434   DBusUserDatabase *db;
00435   const DBusUserInfo *info;
00436   dbus_bool_t result = FALSE; 
00437 
00438   _dbus_user_database_lock_system ();
00439 
00440   db = _dbus_user_database_get_system ();
00441   if (db == NULL)
00442     {
00443       dbus_set_error (error, DBUS_ERROR_FAILED, "Could not get system database.");
00444       _dbus_user_database_unlock_system ();
00445       return FALSE;
00446     }
00447 
00448   info = _dbus_user_database_lookup (db, uid, NULL, error);
00449 
00450   if (info == NULL)
00451     {
00452       _dbus_user_database_unlock_system ();
00453        return FALSE;
00454     }
00455 
00456   result = _dbus_user_at_console (info->username, error);
00457 
00458   _dbus_user_database_unlock_system ();
00459 
00460   return result;
00461 }
00462 
00470 dbus_bool_t
00471 _dbus_get_group_id (const DBusString  *groupname,
00472                     dbus_gid_t        *gid)
00473 {
00474   DBusUserDatabase *db;
00475   const DBusGroupInfo *info;
00476   _dbus_user_database_lock_system ();
00477 
00478   db = _dbus_user_database_get_system ();
00479   if (db == NULL)
00480     {
00481       _dbus_user_database_unlock_system ();
00482       return FALSE;
00483     }
00484 
00485   if (!_dbus_user_database_get_groupname (db, groupname,
00486                                           &info, NULL))
00487     {
00488       _dbus_user_database_unlock_system ();
00489       return FALSE;
00490     }
00491 
00492   *gid = info->gid;
00493   
00494   _dbus_user_database_unlock_system ();
00495   return TRUE;
00496 }
00497 
00505 dbus_bool_t
00506 _dbus_homedir_from_username (const DBusString *username,
00507                              DBusString       *homedir)
00508 {
00509   DBusUserDatabase *db;
00510   const DBusUserInfo *info;
00511   _dbus_user_database_lock_system ();
00512 
00513   db = _dbus_user_database_get_system ();
00514   if (db == NULL)
00515     {
00516       _dbus_user_database_unlock_system ();
00517       return FALSE;
00518     }
00519 
00520   if (!_dbus_user_database_get_username (db, username,
00521                                          &info, NULL))
00522     {
00523       _dbus_user_database_unlock_system ();
00524       return FALSE;
00525     }
00526 
00527   if (!_dbus_string_append (homedir, info->homedir))
00528     {
00529       _dbus_user_database_unlock_system ();
00530       return FALSE;
00531     }
00532   
00533   _dbus_user_database_unlock_system ();
00534   return TRUE;
00535 }
00536 
00544 dbus_bool_t
00545 _dbus_uid_from_string (const DBusString      *uid_str,
00546                        dbus_uid_t            *uid)
00547 {
00548   int end;
00549   long val;
00550   
00551   if (_dbus_string_get_length (uid_str) == 0)
00552     {
00553       _dbus_verbose ("UID string was zero length\n");
00554       return FALSE;
00555     }
00556 
00557   val = -1;
00558   end = 0;
00559   if (!_dbus_string_parse_int (uid_str, 0, &val,
00560                                &end))
00561     {
00562       _dbus_verbose ("could not parse string as a UID\n");
00563       return FALSE;
00564     }
00565   
00566   if (end != _dbus_string_get_length (uid_str))
00567     {
00568       _dbus_verbose ("string contained trailing stuff after UID\n");
00569       return FALSE;
00570     }
00571 
00572   *uid = val;
00573 
00574   return TRUE;
00575 }
00576 
00584 dbus_bool_t
00585 _dbus_credentials_from_username (const DBusString *username,
00586                                  DBusCredentials  *credentials)
00587 {
00588   DBusUserDatabase *db;
00589   const DBusUserInfo *info;
00590   _dbus_user_database_lock_system ();
00591 
00592   db = _dbus_user_database_get_system ();
00593   if (db == NULL)
00594     {
00595       _dbus_user_database_unlock_system ();
00596       return FALSE;
00597     }
00598 
00599   if (!_dbus_user_database_get_username (db, username,
00600                                          &info, NULL))
00601     {
00602       _dbus_user_database_unlock_system ();
00603       return FALSE;
00604     }
00605 
00606   credentials->pid = DBUS_PID_UNSET;
00607   credentials->uid = info->uid;
00608   credentials->gid = info->primary_gid;
00609   
00610   _dbus_user_database_unlock_system ();
00611   return TRUE;
00612 }
00613 
00621 dbus_bool_t
00622 _dbus_credentials_from_uid (dbus_uid_t        uid,
00623                             DBusCredentials  *credentials)
00624 {
00625   DBusUserDatabase *db;
00626   const DBusUserInfo *info;
00627   _dbus_user_database_lock_system ();
00628 
00629   db = _dbus_user_database_get_system ();
00630   if (db == NULL)
00631     {
00632       _dbus_user_database_unlock_system ();
00633       return FALSE;
00634     }
00635 
00636   if (!_dbus_user_database_get_uid (db, uid,
00637                                     &info, NULL))
00638     {
00639       _dbus_user_database_unlock_system ();
00640       return FALSE;
00641     }
00642 
00643   _dbus_assert (info->uid == uid);
00644   
00645   credentials->pid = DBUS_PID_UNSET;
00646   credentials->uid = info->uid;
00647   credentials->gid = info->primary_gid;
00648   
00649   _dbus_user_database_unlock_system ();
00650   return TRUE;
00651 }
00652 
00658 DBusUserDatabase*
00659 _dbus_user_database_new (void)
00660 {
00661   DBusUserDatabase *db;
00662   
00663   db = dbus_new0 (DBusUserDatabase, 1);
00664   if (db == NULL)
00665     return NULL;
00666 
00667   db->refcount = 1;
00668 
00669   db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
00670                                     NULL, free_user_info);
00671   
00672   if (db->users == NULL)
00673     goto failed;
00674 
00675   db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
00676                                      NULL, free_group_info);
00677   
00678   if (db->groups == NULL)
00679     goto failed;
00680 
00681   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00682                                             NULL, NULL);
00683   if (db->users_by_name == NULL)
00684     goto failed;
00685   
00686   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00687                                              NULL, NULL);
00688   if (db->groups_by_name == NULL)
00689     goto failed;
00690   
00691   return db;
00692   
00693  failed:
00694   _dbus_user_database_unref (db);
00695   return NULL;
00696 }
00697 
00703 DBusUserDatabase *
00704 _dbus_user_database_ref (DBusUserDatabase  *db)
00705 {
00706   _dbus_assert (db->refcount > 0);
00707 
00708   db->refcount += 1;
00709 
00710   return db;
00711 }
00712 
00717 void
00718 _dbus_user_database_unref (DBusUserDatabase  *db)
00719 {
00720   _dbus_assert (db->refcount > 0);
00721 
00722   db->refcount -= 1;
00723   if (db->refcount == 0)
00724     {
00725       if (db->users)
00726         _dbus_hash_table_unref (db->users);
00727 
00728       if (db->groups)
00729         _dbus_hash_table_unref (db->groups);
00730 
00731       if (db->users_by_name)
00732         _dbus_hash_table_unref (db->users_by_name);
00733 
00734       if (db->groups_by_name)
00735         _dbus_hash_table_unref (db->groups_by_name);
00736       
00737       dbus_free (db);
00738     }
00739 }
00740 
00754 dbus_bool_t
00755 _dbus_user_database_get_groups (DBusUserDatabase  *db,
00756                                 dbus_uid_t         uid,
00757                                 dbus_gid_t       **group_ids,
00758                                 int               *n_group_ids,
00759                                 DBusError         *error)
00760 {
00761   DBusUserInfo *info;
00762   
00763   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00764 
00765   *group_ids = NULL;
00766   *n_group_ids = 0;
00767   
00768   info = _dbus_user_database_lookup (db, uid, NULL, error);
00769   if (info == NULL)
00770     {
00771       _DBUS_ASSERT_ERROR_IS_SET (error);
00772       return FALSE;
00773     }
00774 
00775   if (info->n_group_ids > 0)
00776     {
00777       *group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
00778       if (*group_ids == NULL)
00779         {
00780           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00781           return FALSE;
00782         }
00783 
00784       *n_group_ids = info->n_group_ids;
00785 
00786       memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
00787     }
00788 
00789   return TRUE;
00790 }
00791 
00802 dbus_bool_t
00803 _dbus_user_database_get_uid (DBusUserDatabase    *db,
00804                              dbus_uid_t           uid,
00805                              const DBusUserInfo **info,
00806                              DBusError           *error)
00807 {
00808   *info = _dbus_user_database_lookup (db, uid, NULL, error);
00809   return *info != NULL;
00810 }
00811 
00822 dbus_bool_t
00823 _dbus_user_database_get_gid (DBusUserDatabase     *db,
00824                              dbus_gid_t            gid,
00825                              const DBusGroupInfo **info,
00826                              DBusError            *error)
00827 {
00828   *info = _dbus_user_database_lookup_group (db, gid, NULL, error);
00829   return *info != NULL;
00830 }
00831 
00841 dbus_bool_t
00842 _dbus_user_database_get_username  (DBusUserDatabase     *db,
00843                                    const DBusString     *username,
00844                                    const DBusUserInfo  **info,
00845                                    DBusError            *error)
00846 {
00847   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
00848   return *info != NULL;
00849 }
00850 
00861 dbus_bool_t
00862 _dbus_user_database_get_groupname (DBusUserDatabase     *db,
00863                                    const DBusString     *groupname,
00864                                    const DBusGroupInfo **info,
00865                                    DBusError            *error)
00866 {
00867   *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
00868   return *info != NULL;
00869 }
00870 
00873 #ifdef DBUS_BUILD_TESTS
00874 #include <stdio.h>
00875 
00881 dbus_bool_t
00882 _dbus_userdb_test (const char *test_data_dir)
00883 {
00884   const DBusString *username;
00885   const DBusString *homedir;
00886 
00887   if (!_dbus_username_from_current_process (&username))
00888     _dbus_assert_not_reached ("didn't get username");
00889 
00890   if (!_dbus_homedir_from_current_process (&homedir))
00891     _dbus_assert_not_reached ("didn't get homedir");  
00892 
00893   printf ("    Current user: %s homedir: %s\n",
00894           _dbus_string_get_const_data (username),
00895           _dbus_string_get_const_data (homedir));
00896   
00897   return TRUE;
00898 }
00899 #endif /* DBUS_BUILD_TESTS */

Generated on Tue Dec 20 14:16:43 2005 for D-BUS by  doxygen 1.4.2