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;
00042 };
00043
00044 static void
00045 free_user_info (void *data)
00046 {
00047 DBusUserInfo *info = data;
00048
00049 if (info == 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)
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
00125 uid = DBUS_UID_UNSET;
00126 username = NULL;
00127
00128
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
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