1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-userdb.c User database abstraction
4 * Copyright (C) 2003 Red Hat, Inc.
6 * Licensed under the Academic Free License version 1.2
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "dbus-userdb.h"
24 #include "dbus-hash.h"
25 #include "dbus-test.h"
26 #include "dbus-internals.h"
29 struct DBusUserDatabase
34 DBusHashTable *groups;
35 DBusHashTable *users_by_name;
36 DBusHashTable *groups_by_name;
40 free_user_info (void *data)
42 DBusUserInfo *info = data;
44 if (info == NULL) /* hash table will pass NULL */
47 _dbus_user_info_free (info);
52 free_group_info (void *data)
54 DBusGroupInfo *info = data;
56 if (info == NULL) /* hash table will pass NULL */
59 _dbus_group_info_free (info);
64 _dbus_user_database_lookup (DBusUserDatabase *db,
66 const DBusString *username,
71 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
72 _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
74 if (uid != DBUS_UID_UNSET)
75 info = _dbus_hash_table_lookup_ulong (db->users, uid);
77 info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
81 _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
87 _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
90 info = dbus_new0 (DBusUserInfo, 1);
93 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
97 if (uid != DBUS_UID_UNSET)
99 if (!_dbus_user_info_fill_uid (info, uid, error))
101 _DBUS_ASSERT_ERROR_IS_SET (error);
102 free_user_info (info);
108 if (!_dbus_user_info_fill (info, username, error))
110 _DBUS_ASSERT_ERROR_IS_SET (error);
111 free_user_info (info);
116 /* be sure we don't use these after here */
117 uid = DBUS_UID_UNSET;
120 /* insert into hash */
121 if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
123 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
124 free_user_info (info);
128 if (!_dbus_hash_table_insert_string (db->users_by_name,
132 _dbus_hash_table_remove_ulong (db->users, info->uid);
133 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
141 static DBusGroupInfo*
142 _dbus_user_database_lookup_group (DBusUserDatabase *db,
144 const DBusString *groupname,
149 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
151 if (gid != DBUS_GID_UNSET)
152 info = _dbus_hash_table_lookup_ulong (db->groups, gid);
154 info = _dbus_hash_table_lookup_string (db->groups_by_name,
155 _dbus_string_get_const_data (groupname));
158 _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
164 _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
167 info = dbus_new0 (DBusGroupInfo, 1);
170 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
174 if (!_dbus_group_info_fill_gid (info, gid, error))
176 _DBUS_ASSERT_ERROR_IS_SET (error);
177 free_group_info (info);
181 if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info))
183 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
184 free_group_info (info);
189 if (!_dbus_hash_table_insert_string (db->groups_by_name,
193 _dbus_hash_table_remove_ulong (db->groups, info->gid);
194 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
202 _DBUS_DEFINE_GLOBAL_LOCK(system_users);
203 static dbus_bool_t database_locked = FALSE;
204 static DBusUserDatabase *system_db = NULL;
205 static DBusString process_username;
206 static DBusString process_homedir;
209 shutdown_system_db (void *data)
211 _dbus_user_database_unref (system_db);
213 _dbus_string_free (&process_username);
214 _dbus_string_free (&process_homedir);
218 init_system_db (void)
220 _dbus_assert (database_locked);
222 if (system_db == NULL)
225 const DBusUserInfo *info;
227 system_db = _dbus_user_database_new ();
228 if (system_db == NULL)
231 dbus_error_init (&error);
233 if (!_dbus_user_database_get_uid (system_db,
238 _dbus_user_database_unref (system_db);
241 if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
243 dbus_error_free (&error);
248 /* This really should not happen. */
249 _dbus_warn ("Could not get password database information for UID of current process: %s\n",
251 dbus_error_free (&error);
256 if (!_dbus_string_init (&process_username))
258 _dbus_user_database_unref (system_db);
263 if (!_dbus_string_init (&process_homedir))
265 _dbus_string_free (&process_username);
266 _dbus_user_database_unref (system_db);
271 if (!_dbus_string_append (&process_username,
273 !_dbus_string_append (&process_homedir,
275 !_dbus_register_shutdown_func (shutdown_system_db, NULL))
277 _dbus_string_free (&process_username);
278 _dbus_string_free (&process_homedir);
279 _dbus_user_database_unref (system_db);
289 * @addtogroup DBusInternalsUtils
294 * Locks global system user database.
297 _dbus_user_database_lock_system (void)
299 _DBUS_LOCK (system_users);
300 database_locked = TRUE;
304 * Unlocks global system user database.
307 _dbus_user_database_unlock_system (void)
309 database_locked = FALSE;
310 _DBUS_UNLOCK (system_users);
314 * Gets the system global user database;
315 * must be called with lock held (_dbus_user_database_lock_system()).
317 * @returns the database or #NULL if no memory
320 _dbus_user_database_get_system (void)
322 _dbus_assert (database_locked);
330 * Gets username of user owning current process. The returned string
331 * is valid until dbus_shutdown() is called.
333 * @param username place to store pointer to username
334 * @returns #FALSE if no memory
337 _dbus_username_from_current_process (const DBusString **username)
339 _dbus_user_database_lock_system ();
340 if (!init_system_db ())
342 _dbus_user_database_unlock_system ();
345 *username = &process_username;
346 _dbus_user_database_unlock_system ();
352 * Gets homedir of user owning current process. The returned string
353 * is valid until dbus_shutdown() is called.
355 * @param homedir place to store pointer to homedir
356 * @returns #FALSE if no memory
359 _dbus_homedir_from_current_process (const DBusString **homedir)
361 _dbus_user_database_lock_system ();
362 if (!init_system_db ())
364 _dbus_user_database_unlock_system ();
367 *homedir = &process_homedir;
368 _dbus_user_database_unlock_system ();
374 * Gets user ID given username
376 * @param username the username
377 * @param uid return location for UID
378 * @returns #TRUE if username existed and we got the UID
381 _dbus_get_user_id (const DBusString *username,
384 DBusCredentials creds;
386 if (!_dbus_credentials_from_username (username, &creds))
389 if (creds.uid == DBUS_UID_UNSET)
398 * Gets group ID given groupname
400 * @param groupname the groupname
401 * @param gid return location for GID
402 * @returns #TRUE if group name existed and we got the GID
405 _dbus_get_group_id (const DBusString *groupname,
408 DBusUserDatabase *db;
409 const DBusGroupInfo *info;
410 _dbus_user_database_lock_system ();
412 db = _dbus_user_database_get_system ();
415 _dbus_user_database_unlock_system ();
419 if (!_dbus_user_database_get_groupname (db, groupname,
422 _dbus_user_database_unlock_system ();
428 _dbus_user_database_unlock_system ();
433 * Gets the home directory for the given user.
435 * @param username the username
436 * @param homedir string to append home directory to
437 * @returns #TRUE if user existed and we appended their homedir
440 _dbus_homedir_from_username (const DBusString *username,
443 DBusUserDatabase *db;
444 const DBusUserInfo *info;
445 _dbus_user_database_lock_system ();
447 db = _dbus_user_database_get_system ();
450 _dbus_user_database_unlock_system ();
454 if (!_dbus_user_database_get_username (db, username,
457 _dbus_user_database_unlock_system ();
461 if (!_dbus_string_append (homedir, info->homedir))
463 _dbus_user_database_unlock_system ();
467 _dbus_user_database_unlock_system ();
472 * Gets a UID from a UID string.
474 * @param uid_str the UID in string form
475 * @param uid UID to fill in
476 * @returns #TRUE if successfully filled in UID
479 _dbus_uid_from_string (const DBusString *uid_str,
485 if (_dbus_string_get_length (uid_str) == 0)
487 _dbus_verbose ("UID string was zero length\n");
493 if (!_dbus_string_parse_int (uid_str, 0, &val,
496 _dbus_verbose ("could not parse string as a UID\n");
500 if (end != _dbus_string_get_length (uid_str))
502 _dbus_verbose ("string contained trailing stuff after UID\n");
512 * Gets the credentials corresponding to the given username.
514 * @param username the username
515 * @param credentials credentials to fill in
516 * @returns #TRUE if the username existed and we got some credentials
519 _dbus_credentials_from_username (const DBusString *username,
520 DBusCredentials *credentials)
522 DBusUserDatabase *db;
523 const DBusUserInfo *info;
524 _dbus_user_database_lock_system ();
526 db = _dbus_user_database_get_system ();
529 _dbus_user_database_unlock_system ();
533 if (!_dbus_user_database_get_username (db, username,
536 _dbus_user_database_unlock_system ();
540 credentials->pid = DBUS_PID_UNSET;
541 credentials->uid = info->uid;
542 credentials->gid = info->primary_gid;
544 _dbus_user_database_unlock_system ();
549 * Gets the credentials corresponding to the given UID.
552 * @param credentials credentials to fill in
553 * @returns #TRUE if the UID existed and we got some credentials
556 _dbus_credentials_from_uid (dbus_uid_t uid,
557 DBusCredentials *credentials)
559 DBusUserDatabase *db;
560 const DBusUserInfo *info;
561 _dbus_user_database_lock_system ();
563 db = _dbus_user_database_get_system ();
566 _dbus_user_database_unlock_system ();
570 if (!_dbus_user_database_get_uid (db, uid,
573 _dbus_user_database_unlock_system ();
577 _dbus_assert (info->uid == uid);
579 credentials->pid = DBUS_PID_UNSET;
580 credentials->uid = info->uid;
581 credentials->gid = info->primary_gid;
583 _dbus_user_database_unlock_system ();
588 * Creates a new user database object used to look up and
589 * cache user information.
590 * @returns new database, or #NULL on out of memory
593 _dbus_user_database_new (void)
595 DBusUserDatabase *db;
597 db = dbus_new0 (DBusUserDatabase, 1);
603 db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
604 NULL, free_user_info);
606 if (db->users == NULL)
609 db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
610 NULL, free_group_info);
612 if (db->groups == NULL)
615 db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
617 if (db->users_by_name == NULL)
620 db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
622 if (db->groups_by_name == NULL)
628 _dbus_user_database_unref (db);
633 * Increments refcount of user database.
634 * @param db the database
637 _dbus_user_database_ref (DBusUserDatabase *db)
639 _dbus_assert (db->refcount > 0);
645 * Decrements refcount of user database.
646 * @param db the database
649 _dbus_user_database_unref (DBusUserDatabase *db)
651 _dbus_assert (db->refcount > 0);
654 if (db->refcount == 0)
657 _dbus_hash_table_unref (db->users);
660 _dbus_hash_table_unref (db->groups);
662 if (db->users_by_name)
663 _dbus_hash_table_unref (db->users_by_name);
665 if (db->groups_by_name)
666 _dbus_hash_table_unref (db->groups_by_name);
673 * Gets all groups for a particular user. Returns #FALSE
674 * if no memory, or user isn't known, but always initializes
675 * group_ids to a NULL array. Sets error to the reason
676 * for returning #FALSE.
678 * @param db the user database object
679 * @param uid the user ID
680 * @param group_ids return location for array of group IDs
681 * @param n_group_ids return location for length of returned array
682 * @param error return location for error
683 * @returns #TRUE on success
686 _dbus_user_database_get_groups (DBusUserDatabase *db,
688 dbus_gid_t **group_ids,
694 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
699 info = _dbus_user_database_lookup (db, uid, NULL, error);
702 _DBUS_ASSERT_ERROR_IS_SET (error);
706 if (info->n_group_ids > 0)
708 *group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
709 if (*group_ids == NULL)
711 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
715 *n_group_ids = info->n_group_ids;
717 memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
724 * Gets the user information for the given UID,
725 * returned user info should not be freed.
727 * @param db user database
728 * @param uid the user ID
729 * @param info return location for const ref to user info
730 * @param error error location
731 * @returns #FALSE if error is set
734 _dbus_user_database_get_uid (DBusUserDatabase *db,
736 const DBusUserInfo **info,
739 *info = _dbus_user_database_lookup (db, uid, NULL, error);
740 return *info != NULL;
744 * Gets the user information for the given GID,
745 * returned group info should not be freed.
747 * @param db user database
748 * @param gid the group ID
749 * @param info return location for const ref to group info
750 * @param error error location
751 * @returns #FALSE if error is set
754 _dbus_user_database_get_gid (DBusUserDatabase *db,
756 const DBusGroupInfo **info,
759 *info = _dbus_user_database_lookup_group (db, gid, NULL, error);
760 return *info != NULL;
764 * Gets the user information for the given username.
766 * @param db user database
767 * @param username the user name
768 * @param info return location for const ref to user info
769 * @param error error location
770 * @returns #FALSE if error is set
773 _dbus_user_database_get_username (DBusUserDatabase *db,
774 const DBusString *username,
775 const DBusUserInfo **info,
778 *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
779 return *info != NULL;
783 * Gets the user information for the given group name,
784 * returned group info should not be freed.
786 * @param db user database
787 * @param groupname the group name
788 * @param info return location for const ref to group info
789 * @param error error location
790 * @returns #FALSE if error is set
793 _dbus_user_database_get_groupname (DBusUserDatabase *db,
794 const DBusString *groupname,
795 const DBusGroupInfo **info,
798 *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
799 return *info != NULL;
804 #ifdef DBUS_BUILD_TESTS
808 * Unit test for dbus-userdb.c.
810 * @returns #TRUE on success.
813 _dbus_userdb_test (const char *test_data_dir)
815 const DBusString *username;
816 const DBusString *homedir;
818 if (!_dbus_username_from_current_process (&username))
819 _dbus_assert_not_reached ("didn't get username");
821 if (!_dbus_homedir_from_current_process (&homedir))
822 _dbus_assert_not_reached ("didn't get homedir");
824 printf (" Current user: %s homedir: %s\n",
825 _dbus_string_get_const_data (username),
826 _dbus_string_get_const_data (homedir));
830 #endif /* DBUS_BUILD_TESTS */