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",
00087                      uid);
00088       return info;
00089     }
00090   else
00091     {
00092       _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
00093                      uid);
00094       
00095       info = dbus_new0 (DBusUserInfo, 1);
00096       if (info == NULL)
00097         {
00098           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00099           return NULL;
00100         }
00101 
00102       if (uid != DBUS_UID_UNSET)
00103         {
00104           if (!_dbus_user_info_fill_uid (info, uid, error))
00105             {
00106               _DBUS_ASSERT_ERROR_IS_SET (error);
00107               free_user_info (info);
00108               return NULL;
00109             }
00110         }
00111       else
00112         {
00113           if (!_dbus_user_info_fill (info, username, error))
00114             {
00115               _DBUS_ASSERT_ERROR_IS_SET (error);
00116               free_user_info (info);
00117               return NULL;
00118             }
00119         }
00120 
00121       /* be sure we don't use these after here */
00122       uid = DBUS_UID_UNSET;
00123       username = NULL;
00124 
00125       /* insert into hash */
00126       if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
00127         {
00128           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00129           free_user_info (info);
00130           return NULL;
00131         }
00132 
00133       if (!_dbus_hash_table_insert_string (db->users_by_name,
00134                                            info->username,
00135                                            info))
00136         {
00137           _dbus_hash_table_remove_ulong (db->users, info->uid);
00138           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00139           return NULL;
00140         }
00141       
00142       return info;
00143     }
00144 }
00145 
00146 static DBusGroupInfo*
00147 _dbus_user_database_lookup_group (DBusUserDatabase *db,
00148                                   dbus_gid_t        gid,
00149                                   const DBusString *groupname,
00150                                   DBusError        *error)
00151 {
00152   DBusGroupInfo *info;
00153 
00154   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00155 
00156   if (gid != DBUS_GID_UNSET)
00157     info = _dbus_hash_table_lookup_ulong (db->groups, gid);
00158   else
00159     info = _dbus_hash_table_lookup_string (db->groups_by_name,
00160                                            _dbus_string_get_const_data (groupname));
00161   if (info)
00162     {
00163       _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
00164                      gid);
00165       return info;
00166     }
00167   else
00168     {
00169       _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
00170                      gid);
00171       
00172       info = dbus_new0 (DBusGroupInfo, 1);
00173       if (info == NULL)
00174         {
00175           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00176           return NULL;
00177         }
00178 
00179 
00180       if (gid != DBUS_GID_UNSET)
00181         {
00182           if (!_dbus_group_info_fill_gid (info, gid, error))
00183             {
00184               _DBUS_ASSERT_ERROR_IS_SET (error);
00185               free_group_info (info);
00186               return NULL;
00187             }
00188         }
00189       else
00190         {
00191           if (!_dbus_group_info_fill (info, groupname, error))
00192            {
00193              _DBUS_ASSERT_ERROR_IS_SET (error);
00194              free_group_info (info);
00195              return NULL;
00196            }
00197         }
00198 
00199       if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info))
00200         {
00201           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00202           free_group_info (info);
00203           return NULL;
00204         }
00205 
00206 
00207       if (!_dbus_hash_table_insert_string (db->groups_by_name,
00208                                            info->groupname,
00209                                            info))
00210         {
00211           _dbus_hash_table_remove_ulong (db->groups, info->gid);
00212           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00213           return NULL;
00214         }
00215       
00216       return info;
00217     }
00218 }
00219 
00220 _DBUS_DEFINE_GLOBAL_LOCK(system_users);
00221 static dbus_bool_t database_locked = FALSE;
00222 static DBusUserDatabase *system_db = NULL;
00223 static DBusString process_username;
00224 static DBusString process_homedir;
00225       
00226 static void
00227 shutdown_system_db (void *data)
00228 {
00229   _dbus_user_database_unref (system_db);
00230   system_db = NULL;
00231   _dbus_string_free (&process_username);
00232   _dbus_string_free (&process_homedir);
00233 }
00234 
00235 static dbus_bool_t
00236 init_system_db (void)
00237 {
00238   _dbus_assert (database_locked);
00239     
00240   if (system_db == NULL)
00241     {
00242       DBusError error;
00243       const DBusUserInfo *info;
00244       
00245       system_db = _dbus_user_database_new ();
00246       if (system_db == NULL)
00247         return FALSE;
00248 
00249       dbus_error_init (&error);
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 
00314 void
00315 _dbus_user_database_lock_system (void)
00316 {
00317   _DBUS_LOCK (system_users);
00318   database_locked = TRUE;
00319 }
00320 
00324 void
00325 _dbus_user_database_unlock_system (void)
00326 {
00327   database_locked = FALSE;
00328   _DBUS_UNLOCK (system_users);
00329 }
00330 
00337 DBusUserDatabase*
00338 _dbus_user_database_get_system (void)
00339 {
00340   _dbus_assert (database_locked);
00341 
00342   init_system_db ();
00343   
00344   return system_db;
00345 }
00346 
00354 dbus_bool_t
00355 _dbus_username_from_current_process (const DBusString **username)
00356 {
00357   _dbus_user_database_lock_system ();
00358   if (!init_system_db ())
00359     {
00360       _dbus_user_database_unlock_system ();
00361       return FALSE;
00362     }
00363   *username = &process_username;
00364   _dbus_user_database_unlock_system ();  
00365 
00366   return TRUE;
00367 }
00368 
00376 dbus_bool_t
00377 _dbus_homedir_from_current_process (const DBusString  **homedir)
00378 {
00379   _dbus_user_database_lock_system ();
00380   if (!init_system_db ())
00381     {
00382       _dbus_user_database_unlock_system ();
00383       return FALSE;
00384     }
00385   *homedir = &process_homedir;
00386   _dbus_user_database_unlock_system ();
00387 
00388   return TRUE;
00389 }
00390 
00398 dbus_bool_t
00399 _dbus_get_user_id (const DBusString  *username,
00400                    dbus_uid_t        *uid)
00401 {
00402   DBusCredentials creds;
00403 
00404   if (!_dbus_credentials_from_username (username, &creds))
00405     return FALSE;
00406 
00407   if (creds.uid == DBUS_UID_UNSET)
00408     return FALSE;
00409 
00410   *uid = creds.uid;
00411 
00412   return TRUE;
00413 }
00414 
00422 dbus_bool_t
00423 _dbus_is_console_user (dbus_uid_t uid,
00424                        DBusError *error)
00425 {
00426 
00427   DBusUserDatabase *db;
00428   const DBusUserInfo *info;
00429   dbus_bool_t result = FALSE; 
00430 
00431   _dbus_user_database_lock_system ();
00432 
00433   db = _dbus_user_database_get_system ();
00434   if (db == NULL)
00435     {
00436       dbus_set_error (error, DBUS_ERROR_FAILED, "Could not get system database.");
00437       _dbus_user_database_unlock_system ();
00438       return FALSE;
00439     }
00440 
00441   info = _dbus_user_database_lookup (db, uid, NULL, error);
00442 
00443   if (info == NULL)
00444     {
00445       _dbus_user_database_unlock_system ();
00446        return FALSE;
00447     }
00448 
00449   result = _dbus_user_at_console (info->username, error);
00450 
00451   _dbus_user_database_unlock_system ();
00452 
00453   return result;
00454 }
00455 
00463 dbus_bool_t
00464 _dbus_get_group_id (const DBusString  *groupname,
00465                     dbus_gid_t        *gid)
00466 {
00467   DBusUserDatabase *db;
00468   const DBusGroupInfo *info;
00469   _dbus_user_database_lock_system ();
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_groupname (db, groupname,
00479                                           &info, NULL))
00480     {
00481       _dbus_user_database_unlock_system ();
00482       return FALSE;
00483     }
00484 
00485   *gid = info->gid;
00486   
00487   _dbus_user_database_unlock_system ();
00488   return TRUE;
00489 }
00490 
00498 dbus_bool_t
00499 _dbus_homedir_from_username (const DBusString *username,
00500                              DBusString       *homedir)
00501 {
00502   DBusUserDatabase *db;
00503   const DBusUserInfo *info;
00504   _dbus_user_database_lock_system ();
00505 
00506   db = _dbus_user_database_get_system ();
00507   if (db == NULL)
00508     {
00509       _dbus_user_database_unlock_system ();
00510       return FALSE;
00511     }
00512 
00513   if (!_dbus_user_database_get_username (db, username,
00514                                          &info, NULL))
00515     {
00516       _dbus_user_database_unlock_system ();
00517       return FALSE;
00518     }
00519 
00520   if (!_dbus_string_append (homedir, info->homedir))
00521     {
00522       _dbus_user_database_unlock_system ();
00523       return FALSE;
00524     }
00525   
00526   _dbus_user_database_unlock_system ();
00527   return TRUE;
00528 }
00529 
00537 dbus_bool_t
00538 _dbus_uid_from_string (const DBusString      *uid_str,
00539                        dbus_uid_t            *uid)
00540 {
00541   int end;
00542   long val;
00543   
00544   if (_dbus_string_get_length (uid_str) == 0)
00545     {
00546       _dbus_verbose ("UID string was zero length\n");
00547       return FALSE;
00548     }
00549 
00550   val = -1;
00551   end = 0;
00552   if (!_dbus_string_parse_int (uid_str, 0, &val,
00553                                &end))
00554     {
00555       _dbus_verbose ("could not parse string as a UID\n");
00556       return FALSE;
00557     }
00558   
00559   if (end != _dbus_string_get_length (uid_str))
00560     {
00561       _dbus_verbose ("string contained trailing stuff after UID\n");
00562       return FALSE;
00563     }
00564 
00565   *uid = val;
00566 
00567   return TRUE;
00568 }
00569 
00577 dbus_bool_t
00578 _dbus_credentials_from_username (const DBusString *username,
00579                                  DBusCredentials  *credentials)
00580 {
00581   DBusUserDatabase *db;
00582   const DBusUserInfo *info;
00583   _dbus_user_database_lock_system ();
00584 
00585   db = _dbus_user_database_get_system ();
00586   if (db == NULL)
00587     {
00588       _dbus_user_database_unlock_system ();
00589       return FALSE;
00590     }
00591 
00592   if (!_dbus_user_database_get_username (db, username,
00593                                          &info, NULL))
00594     {
00595       _dbus_user_database_unlock_system ();
00596       return FALSE;
00597     }
00598 
00599   credentials->pid = DBUS_PID_UNSET;
00600   credentials->uid = info->uid;
00601   credentials->gid = info->primary_gid;
00602   
00603   _dbus_user_database_unlock_system ();
00604   return TRUE;
00605 }
00606 
00614 dbus_bool_t
00615 _dbus_credentials_from_uid (dbus_uid_t        uid,
00616                             DBusCredentials  *credentials)
00617 {
00618   DBusUserDatabase *db;
00619   const DBusUserInfo *info;
00620   _dbus_user_database_lock_system ();
00621 
00622   db = _dbus_user_database_get_system ();
00623   if (db == NULL)
00624     {
00625       _dbus_user_database_unlock_system ();
00626       return FALSE;
00627     }
00628 
00629   if (!_dbus_user_database_get_uid (db, uid,
00630                                     &info, NULL))
00631     {
00632       _dbus_user_database_unlock_system ();
00633       return FALSE;
00634     }
00635 
00636   _dbus_assert (info->uid == uid);
00637   
00638   credentials->pid = DBUS_PID_UNSET;
00639   credentials->uid = info->uid;
00640   credentials->gid = info->primary_gid;
00641   
00642   _dbus_user_database_unlock_system ();
00643   return TRUE;
00644 }
00645 
00651 DBusUserDatabase*
00652 _dbus_user_database_new (void)
00653 {
00654   DBusUserDatabase *db;
00655   
00656   db = dbus_new0 (DBusUserDatabase, 1);
00657   if (db == NULL)
00658     return NULL;
00659 
00660   db->refcount = 1;
00661 
00662   db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
00663                                     NULL, free_user_info);
00664   
00665   if (db->users == NULL)
00666     goto failed;
00667 
00668   db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
00669                                      NULL, free_group_info);
00670   
00671   if (db->groups == NULL)
00672     goto failed;
00673 
00674   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00675                                             NULL, NULL);
00676   if (db->users_by_name == NULL)
00677     goto failed;
00678   
00679   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00680                                              NULL, NULL);
00681   if (db->groups_by_name == NULL)
00682     goto failed;
00683   
00684   return db;
00685   
00686  failed:
00687   _dbus_user_database_unref (db);
00688   return NULL;
00689 }
00690 
00696 DBusUserDatabase *
00697 _dbus_user_database_ref (DBusUserDatabase  *db)
00698 {
00699   _dbus_assert (db->refcount > 0);
00700 
00701   db->refcount += 1;
00702 
00703   return db;
00704 }
00705 
00710 void
00711 _dbus_user_database_unref (DBusUserDatabase  *db)
00712 {
00713   _dbus_assert (db->refcount > 0);
00714 
00715   db->refcount -= 1;
00716   if (db->refcount == 0)
00717     {
00718       if (db->users)
00719         _dbus_hash_table_unref (db->users);
00720 
00721       if (db->groups)
00722         _dbus_hash_table_unref (db->groups);
00723 
00724       if (db->users_by_name)
00725         _dbus_hash_table_unref (db->users_by_name);
00726 
00727       if (db->groups_by_name)
00728         _dbus_hash_table_unref (db->groups_by_name);
00729       
00730       dbus_free (db);
00731     }
00732 }
00733 
00747 dbus_bool_t
00748 _dbus_user_database_get_groups (DBusUserDatabase  *db,
00749                                 dbus_uid_t         uid,
00750                                 dbus_gid_t       **group_ids,
00751                                 int               *n_group_ids,
00752                                 DBusError         *error)
00753 {
00754   DBusUserInfo *info;
00755   
00756   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00757 
00758   *group_ids = NULL;
00759   *n_group_ids = 0;
00760   
00761   info = _dbus_user_database_lookup (db, uid, NULL, error);
00762   if (info == NULL)
00763     {
00764       _DBUS_ASSERT_ERROR_IS_SET (error);
00765       return FALSE;
00766     }
00767 
00768   if (info->n_group_ids > 0)
00769     {
00770       *group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
00771       if (*group_ids == NULL)
00772         {
00773           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00774           return FALSE;
00775         }
00776 
00777       *n_group_ids = info->n_group_ids;
00778 
00779       memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
00780     }
00781 
00782   return TRUE;
00783 }
00784 
00795 dbus_bool_t
00796 _dbus_user_database_get_uid (DBusUserDatabase    *db,
00797                              dbus_uid_t           uid,
00798                              const DBusUserInfo **info,
00799                              DBusError           *error)
00800 {
00801   *info = _dbus_user_database_lookup (db, uid, NULL, error);
00802   return *info != NULL;
00803 }
00804 
00815 dbus_bool_t
00816 _dbus_user_database_get_gid (DBusUserDatabase     *db,
00817                              dbus_gid_t            gid,
00818                              const DBusGroupInfo **info,
00819                              DBusError            *error)
00820 {
00821   *info = _dbus_user_database_lookup_group (db, gid, NULL, error);
00822   return *info != NULL;
00823 }
00824 
00834 dbus_bool_t
00835 _dbus_user_database_get_username  (DBusUserDatabase     *db,
00836                                    const DBusString     *username,
00837                                    const DBusUserInfo  **info,
00838                                    DBusError            *error)
00839 {
00840   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
00841   return *info != NULL;
00842 }
00843 
00854 dbus_bool_t
00855 _dbus_user_database_get_groupname (DBusUserDatabase     *db,
00856                                    const DBusString     *groupname,
00857                                    const DBusGroupInfo **info,
00858                                    DBusError            *error)
00859 {
00860   *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
00861   return *info != NULL;
00862 }
00863 
00866 #ifdef DBUS_BUILD_TESTS
00867 #include <stdio.h>
00868 
00874 dbus_bool_t
00875 _dbus_userdb_test (const char *test_data_dir)
00876 {
00877   const DBusString *username;
00878   const DBusString *homedir;
00879 
00880   if (!_dbus_username_from_current_process (&username))
00881     _dbus_assert_not_reached ("didn't get username");
00882 
00883   if (!_dbus_homedir_from_current_process (&homedir))
00884     _dbus_assert_not_reached ("didn't get homedir");  
00885 
00886   printf ("    Current user: %s homedir: %s\n",
00887           _dbus_string_get_const_data (username),
00888           _dbus_string_get_const_data (homedir));
00889   
00890   return TRUE;
00891 }
00892 #endif /* DBUS_BUILD_TESTS */

Generated on Mon Apr 4 04:40:47 2005 for D-BUS by  doxygen 1.4.0