00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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)
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)
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
00121 uid = DBUS_UID_UNSET;
00122 username = NULL;
00123
00124
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
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