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);
73 if (uid != DBUS_UID_UNSET)
74 info = _dbus_hash_table_lookup_ulong (db->users, uid);
76 info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
80 _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
86 _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
89 info = dbus_new0 (DBusUserInfo, 1);
92 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
96 if (!_dbus_user_info_fill_uid (info, uid, error))
98 _DBUS_ASSERT_ERROR_IS_SET (error);
99 free_user_info (info);
103 if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
105 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
106 free_user_info (info);
110 if (!_dbus_hash_table_insert_string (db->users_by_name,
114 _dbus_hash_table_remove_ulong (db->users, info->uid);
115 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
123 static DBusGroupInfo*
124 _dbus_user_database_lookup_group (DBusUserDatabase *db,
126 const DBusString *groupname,
131 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
133 if (gid != DBUS_GID_UNSET)
134 info = _dbus_hash_table_lookup_ulong (db->groups, gid);
136 info = _dbus_hash_table_lookup_string (db->groups_by_name,
137 _dbus_string_get_const_data (groupname));
140 _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
146 _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
149 info = dbus_new0 (DBusGroupInfo, 1);
152 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
156 if (!_dbus_group_info_fill_gid (info, gid, error))
158 _DBUS_ASSERT_ERROR_IS_SET (error);
159 free_group_info (info);
163 if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info))
165 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
166 free_group_info (info);
171 if (!_dbus_hash_table_insert_string (db->groups_by_name,
175 _dbus_hash_table_remove_ulong (db->groups, info->gid);
176 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
184 _DBUS_DEFINE_GLOBAL_LOCK(system_users);
185 static dbus_bool_t database_locked = FALSE;
186 static DBusUserDatabase *system_db = NULL;
187 static DBusString process_username;
188 static DBusString process_homedir;
191 shutdown_system_db (void *data)
193 _dbus_user_database_unref (system_db);
195 _dbus_string_free (&process_username);
196 _dbus_string_free (&process_homedir);
200 init_system_db (void)
202 _dbus_assert (database_locked);
204 if (system_db == NULL)
207 const DBusUserInfo *info;
209 system_db = _dbus_user_database_new ();
210 if (system_db == NULL)
213 dbus_error_init (&error);
215 if (!_dbus_user_database_get_uid (system_db,
220 _dbus_user_database_unref (system_db);
223 if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
225 dbus_error_free (&error);
230 /* This really should not happen. */
231 _dbus_warn ("Could not get password database information for UID of current process: %s\n",
233 dbus_error_free (&error);
238 if (!_dbus_string_init (&process_username))
240 _dbus_user_database_unref (system_db);
245 if (!_dbus_string_init (&process_homedir))
247 _dbus_string_free (&process_username);
248 _dbus_user_database_unref (system_db);
253 if (!_dbus_string_append (&process_username,
255 !_dbus_string_append (&process_homedir,
257 !_dbus_register_shutdown_func (shutdown_system_db, NULL))
259 _dbus_string_free (&process_username);
260 _dbus_string_free (&process_homedir);
261 _dbus_user_database_unref (system_db);
271 * @addtogroup DBusInternalsUtils
276 * Locks global system user database.
279 _dbus_user_database_lock_system (void)
281 _DBUS_LOCK (system_users);
282 database_locked = TRUE;
286 * Unlocks global system user database.
289 _dbus_user_database_unlock_system (void)
291 database_locked = FALSE;
292 _DBUS_UNLOCK (system_users);
296 * Gets the system global user database;
297 * must be called with lock held (_dbus_user_database_lock_system()).
299 * @returns the database or #NULL if no memory
302 _dbus_user_database_get_system (void)
304 _dbus_assert (database_locked);
312 * Gets username of user owning current process. The returned string
313 * is valid until dbus_shutdown() is called.
315 * @param username place to store pointer to username
316 * @returns #FALSE if no memory
319 _dbus_username_from_current_process (const DBusString **username)
321 _dbus_user_database_lock_system ();
322 if (!init_system_db ())
324 _dbus_user_database_unlock_system ();
327 *username = &process_username;
328 _dbus_user_database_unlock_system ();
334 * Gets homedir of user owning current process. The returned string
335 * is valid until dbus_shutdown() is called.
337 * @param homedir place to store pointer to homedir
338 * @returns #FALSE if no memory
341 _dbus_homedir_from_current_process (const DBusString **homedir)
343 _dbus_user_database_lock_system ();
344 if (!init_system_db ())
346 _dbus_user_database_unlock_system ();
349 *homedir = &process_homedir;
350 _dbus_user_database_unlock_system ();
356 * Gets user ID given username
358 * @param username the username
359 * @param uid return location for UID
360 * @returns #TRUE if username existed and we got the UID
363 _dbus_get_user_id (const DBusString *username,
366 DBusCredentials creds;
368 if (!_dbus_credentials_from_username (username, &creds))
371 if (creds.uid == DBUS_UID_UNSET)
380 * Gets group ID given groupname
382 * @param groupname the groupname
383 * @param gid return location for GID
384 * @returns #TRUE if group name existed and we got the GID
387 _dbus_get_group_id (const DBusString *groupname,
390 DBusUserDatabase *db;
391 const DBusGroupInfo *info;
392 _dbus_user_database_lock_system ();
394 db = _dbus_user_database_get_system ();
397 _dbus_user_database_unlock_system ();
401 if (!_dbus_user_database_get_groupname (db, groupname,
404 _dbus_user_database_unlock_system ();
410 _dbus_user_database_unlock_system ();
415 * Gets the home directory for the given user.
417 * @param username the username
418 * @param homedir string to append home directory to
419 * @returns #TRUE if user existed and we appended their homedir
422 _dbus_homedir_from_username (const DBusString *username,
425 DBusUserDatabase *db;
426 const DBusUserInfo *info;
427 _dbus_user_database_lock_system ();
429 db = _dbus_user_database_get_system ();
432 _dbus_user_database_unlock_system ();
436 if (!_dbus_user_database_get_username (db, username,
439 _dbus_user_database_unlock_system ();
443 if (!_dbus_string_append (homedir, info->homedir))
445 _dbus_user_database_unlock_system ();
449 _dbus_user_database_unlock_system ();
454 * Gets a UID from a UID string.
456 * @param uid_str the UID in string form
457 * @param uid UID to fill in
458 * @returns #TRUE if successfully filled in UID
461 _dbus_uid_from_string (const DBusString *uid_str,
467 if (_dbus_string_get_length (uid_str) == 0)
469 _dbus_verbose ("UID string was zero length\n");
475 if (!_dbus_string_parse_int (uid_str, 0, &val,
478 _dbus_verbose ("could not parse string as a UID\n");
482 if (end != _dbus_string_get_length (uid_str))
484 _dbus_verbose ("string contained trailing stuff after UID\n");
494 * Gets the credentials corresponding to the given username.
496 * @param username the username
497 * @param credentials credentials to fill in
498 * @returns #TRUE if the username existed and we got some credentials
501 _dbus_credentials_from_username (const DBusString *username,
502 DBusCredentials *credentials)
504 DBusUserDatabase *db;
505 const DBusUserInfo *info;
506 _dbus_user_database_lock_system ();
508 db = _dbus_user_database_get_system ();
511 _dbus_user_database_unlock_system ();
515 if (!_dbus_user_database_get_username (db, username,
518 _dbus_user_database_unlock_system ();
522 credentials->pid = DBUS_PID_UNSET;
523 credentials->uid = info->uid;
524 credentials->gid = info->primary_gid;
526 _dbus_user_database_unlock_system ();
531 * Gets the credentials corresponding to the given UID.
534 * @param credentials credentials to fill in
535 * @returns #TRUE if the UID existed and we got some credentials
538 _dbus_credentials_from_uid (dbus_uid_t uid,
539 DBusCredentials *credentials)
541 DBusUserDatabase *db;
542 const DBusUserInfo *info;
543 _dbus_user_database_lock_system ();
545 db = _dbus_user_database_get_system ();
548 _dbus_user_database_unlock_system ();
552 if (!_dbus_user_database_get_uid (db, uid,
555 _dbus_user_database_unlock_system ();
559 _dbus_assert (info->uid == uid);
561 credentials->pid = DBUS_PID_UNSET;
562 credentials->uid = info->uid;
563 credentials->gid = info->primary_gid;
565 _dbus_user_database_unlock_system ();
570 * Creates a new user database object used to look up and
571 * cache user information.
572 * @returns new database, or #NULL on out of memory
575 _dbus_user_database_new (void)
577 DBusUserDatabase *db;
579 db = dbus_new0 (DBusUserDatabase, 1);
585 db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
586 NULL, free_user_info);
588 if (db->users == NULL)
591 db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
592 NULL, free_group_info);
594 if (db->groups == NULL)
597 db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
599 if (db->users_by_name == NULL)
602 db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
604 if (db->groups_by_name == NULL)
610 _dbus_user_database_unref (db);
615 * Increments refcount of user database.
616 * @param db the database
619 _dbus_user_database_ref (DBusUserDatabase *db)
621 _dbus_assert (db->refcount > 0);
627 * Decrements refcount of user database.
628 * @param db the database
631 _dbus_user_database_unref (DBusUserDatabase *db)
633 _dbus_assert (db->refcount > 0);
636 if (db->refcount == 0)
639 _dbus_hash_table_unref (db->users);
642 _dbus_hash_table_unref (db->groups);
644 if (db->users_by_name)
645 _dbus_hash_table_unref (db->users_by_name);
647 if (db->groups_by_name)
648 _dbus_hash_table_unref (db->groups_by_name);
655 * Gets all groups for a particular user. Returns #FALSE
656 * if no memory, or user isn't known, but always initializes
657 * group_ids to a NULL array. Sets error to the reason
658 * for returning #FALSE.
660 * @param db the user database object
661 * @param uid the user ID
662 * @param group_ids return location for array of group IDs
663 * @param n_group_ids return location for length of returned array
664 * @param error return location for error
665 * @returns #TRUE on success
668 _dbus_user_database_get_groups (DBusUserDatabase *db,
670 dbus_gid_t **group_ids,
676 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
681 info = _dbus_user_database_lookup (db, uid, NULL, error);
684 _DBUS_ASSERT_ERROR_IS_SET (error);
688 if (info->n_group_ids > 0)
690 *group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
691 if (*group_ids == NULL)
693 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
697 *n_group_ids = info->n_group_ids;
699 memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
706 * Gets the user information for the given UID,
707 * returned user info should not be freed.
709 * @param db user database
710 * @param uid the user ID
711 * @param info return location for const ref to user info
712 * @param error error location
713 * @returns #FALSE if error is set
716 _dbus_user_database_get_uid (DBusUserDatabase *db,
718 const DBusUserInfo **info,
721 *info = _dbus_user_database_lookup (db, uid, NULL, error);
722 return *info != NULL;
726 * Gets the user information for the given GID,
727 * returned group info should not be freed.
729 * @param db user database
730 * @param gid the group ID
731 * @param info return location for const ref to group info
732 * @param error error location
733 * @returns #FALSE if error is set
736 _dbus_user_database_get_gid (DBusUserDatabase *db,
738 const DBusGroupInfo **info,
741 *info = _dbus_user_database_lookup_group (db, gid, NULL, error);
742 return *info != NULL;
746 * Gets the user information for the given username.
748 * @param db user database
749 * @param username the user name
750 * @param info return location for const ref to user info
751 * @param error error location
752 * @returns #FALSE if error is set
755 _dbus_user_database_get_username (DBusUserDatabase *db,
756 const DBusString *username,
757 const DBusUserInfo **info,
760 *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
761 return *info != NULL;
765 * Gets the user information for the given group name,
766 * returned group info should not be freed.
768 * @param db user database
769 * @param groupname the group name
770 * @param info return location for const ref to group info
771 * @param error error location
772 * @returns #FALSE if error is set
775 _dbus_user_database_get_groupname (DBusUserDatabase *db,
776 const DBusString *groupname,
777 const DBusGroupInfo **info,
780 *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
781 return *info != NULL;
786 #ifdef DBUS_BUILD_TESTS
790 * Unit test for dbus-userdb.c.
792 * @returns #TRUE on success.
795 _dbus_userdb_test (const char *test_data_dir)
797 const DBusString *username;
798 const DBusString *homedir;
800 if (!_dbus_username_from_current_process (&username))
801 _dbus_assert_not_reached ("didn't get username");
803 if (!_dbus_homedir_from_current_process (&homedir))
804 _dbus_assert_not_reached ("didn't get homedir");
806 printf (" Current user: %s homedir: %s\n",
807 _dbus_string_get_const_data (username),
808 _dbus_string_get_const_data (homedir));
812 #endif /* DBUS_BUILD_TESTS */