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

Generated on Sat Sep 25 19:17:13 2004 for D-BUS by  doxygen 1.3.8-20040913