1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 2/* dbus-userdb-util.c Would be in dbus-userdb.c, but not used in libdbus 3 * 4 * Copyright (C) 2003, 2004, 2005 Red Hat, Inc. 5 * 6 * Licensed under the Academic Free License version 2.1 7 * 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. 12 * 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. 17 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23#include <config.h> 24#define DBUS_USERDB_INCLUDES_PRIVATE 1 25#include "dbus-userdb.h" 26#include "dbus-test.h" 27#include "dbus-internals.h" 28#include "dbus-protocol.h" 29#include <string.h> 30 31#if HAVE_SYSTEMD 32#include <systemd/sd-daemon.h> 33#include <systemd/sd-login.h> 34#endif 35 36/** 37 * @addtogroup DBusInternalsUtils 38 * @{ 39 */ 40 41/** 42 * Checks to see if the UID sent in is the console user 43 * 44 * @param uid UID of person to check 45 * @param error return location for errors 46 * @returns #TRUE if the UID is the same as the console user and there are no errors 47 */ 48dbus_bool_t 49_dbus_is_console_user (dbus_uid_t uid, 50 DBusError *error) 51{ 52 53 DBusUserDatabase *db; 54 const DBusUserInfo *info; 55 dbus_bool_t result = FALSE; 56 57#ifdef HAVE_SYSTEMD 58 if (sd_booted () > 0) 59 { 60 int r; 61 62 /* Check whether this user is logged in on at least one physical 63 seat */ 64 r = sd_uid_get_seats (uid, 0, NULL); 65 if (r < 0) 66 { 67 dbus_set_error (error, _dbus_error_from_errno (-r), 68 "Failed to determine seats of user \"" DBUS_UID_FORMAT "\": %s", 69 uid, 70 _dbus_strerror (-r)); 71 return FALSE; 72 } 73 74 return (r > 0); 75 } 76#endif 77 78#ifdef HAVE_CONSOLE_OWNER_FILE 79 80 DBusString f; 81 DBusStat st; 82 83 if (!_dbus_string_init (&f)) 84 { 85 _DBUS_SET_OOM (error); 86 return FALSE; 87 } 88 89 if (!_dbus_string_append(&f, DBUS_CONSOLE_OWNER_FILE)) 90 { 91 _dbus_string_free(&f); 92 _DBUS_SET_OOM (error); 93 return FALSE; 94 } 95 96 if (_dbus_stat(&f, &st, NULL) && (st.uid == uid)) 97 { 98 _dbus_string_free(&f); 99 return TRUE; 100 } 101 102 _dbus_string_free(&f); 103 104#endif /* HAVE_CONSOLE_OWNER_FILE */ 105 106 _dbus_user_database_lock_system (); 107 108 db = _dbus_user_database_get_system (); 109 if (db == NULL) 110 { 111 dbus_set_error (error, DBUS_ERROR_FAILED, "Could not get system database."); 112 _dbus_user_database_unlock_system (); 113 return FALSE; 114 } 115 116 /* TPTD: this should be cache-safe, we've locked the DB and 117 _dbus_user_at_console doesn't pass it on. */ 118 info = _dbus_user_database_lookup (db, uid, NULL, error); 119 120 if (info == NULL) 121 { 122 _dbus_user_database_unlock_system (); 123 return FALSE; 124 } 125 126 result = _dbus_user_at_console (info->username, error); 127 128 _dbus_user_database_unlock_system (); 129 130 return result; 131} 132 133/** 134 * Gets user ID given username 135 * 136 * @param username the username 137 * @param uid return location for UID 138 * @returns #TRUE if username existed and we got the UID 139 */ 140dbus_bool_t 141_dbus_get_user_id (const DBusString *username, 142 dbus_uid_t *uid) 143{ 144 return _dbus_get_user_id_and_primary_group (username, uid, NULL); 145} 146 147/** 148 * Gets group ID given groupname 149 * 150 * @param groupname the groupname 151 * @param gid return location for GID 152 * @returns #TRUE if group name existed and we got the GID 153 */ 154dbus_bool_t 155_dbus_get_group_id (const DBusString *groupname, 156 dbus_gid_t *gid) 157{ 158 DBusUserDatabase *db; 159 const DBusGroupInfo *info; 160 _dbus_user_database_lock_system (); 161 162 db = _dbus_user_database_get_system (); 163 if (db == NULL) 164 { 165 _dbus_user_database_unlock_system (); 166 return FALSE; 167 } 168 169 if (!_dbus_user_database_get_groupname (db, groupname, 170 &info, NULL)) 171 { 172 _dbus_user_database_unlock_system (); 173 return FALSE; 174 } 175 176 *gid = info->gid; 177 178 _dbus_user_database_unlock_system (); 179 return TRUE; 180} 181 182/** 183 * Gets user ID and primary group given username 184 * 185 * @param username the username 186 * @param uid_p return location for UID 187 * @param gid_p return location for GID 188 * @returns #TRUE if username existed and we got the UID and GID 189 */ 190dbus_bool_t 191_dbus_get_user_id_and_primary_group (const DBusString *username, 192 dbus_uid_t *uid_p, 193 dbus_gid_t *gid_p) 194{ 195 DBusUserDatabase *db; 196 const DBusUserInfo *info; 197 _dbus_user_database_lock_system (); 198 199 db = _dbus_user_database_get_system (); 200 if (db == NULL) 201 { 202 _dbus_user_database_unlock_system (); 203 return FALSE; 204 } 205 206 if (!_dbus_user_database_get_username (db, username, 207 &info, NULL)) 208 { 209 _dbus_user_database_unlock_system (); 210 return FALSE; 211 } 212 213 if (uid_p) 214 *uid_p = info->uid; 215 if (gid_p) 216 *gid_p = info->primary_gid; 217 218 _dbus_user_database_unlock_system (); 219 return TRUE; 220} 221 222/** 223 * Looks up a gid or group name in the user database. Only one of 224 * name or GID can be provided. There are wrapper functions for this 225 * that are better to use, this one does no locking or anything on the 226 * database and otherwise sort of sucks. 227 * 228 * @param db the database 229 * @param gid the group ID or #DBUS_GID_UNSET 230 * @param groupname group name or #NULL 231 * @param error error to fill in 232 * @returns the entry in the database 233 */ 234DBusGroupInfo* 235_dbus_user_database_lookup_group (DBusUserDatabase *db, 236 dbus_gid_t gid, 237 const DBusString *groupname, 238 DBusError *error) 239{ 240 DBusGroupInfo *info; 241 242 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 243 244 /* See if the group is really a number */ 245 if (gid == DBUS_UID_UNSET) 246 { 247 unsigned long n; 248 249 if (_dbus_is_a_number (groupname, &n)) 250 gid = n; 251 } 252 253#ifdef DBUS_ENABLE_USERDB_CACHE 254 if (gid != DBUS_GID_UNSET) 255 info = _dbus_hash_table_lookup_uintptr (db->groups, gid); 256 else 257 info = _dbus_hash_table_lookup_string (db->groups_by_name, 258 _dbus_string_get_const_data (groupname)); 259 if (info) 260 { 261 _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n", 262 info->gid); 263 return info; 264 } 265 else 266#else 267 if (1) 268#endif 269 { 270 if (gid != DBUS_GID_UNSET) 271 _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n", 272 gid); 273 else 274 _dbus_verbose ("No cache for groupname \"%s\"\n", 275 _dbus_string_get_const_data (groupname)); 276 277 info = dbus_new0 (DBusGroupInfo, 1); 278 if (info == NULL) 279 { 280 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 281 return NULL; 282 } 283 284 if (gid != DBUS_GID_UNSET) 285 { 286 if (!_dbus_group_info_fill_gid (info, gid, error)) 287 { 288 _DBUS_ASSERT_ERROR_IS_SET (error); 289 _dbus_group_info_free_allocated (info); 290 return NULL; 291 } 292 } 293 else 294 { 295 if (!_dbus_group_info_fill (info, groupname, error)) 296 { 297 _DBUS_ASSERT_ERROR_IS_SET (error); 298 _dbus_group_info_free_allocated (info); 299 return NULL; 300 } 301 } 302 303 /* don't use these past here */ 304 gid = DBUS_GID_UNSET; 305 groupname = NULL; 306 307 if (!_dbus_hash_table_insert_uintptr (db->groups, info->gid, info)) 308 { 309 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 310 _dbus_group_info_free_allocated (info); 311 return NULL; 312 } 313 314 315 if (!_dbus_hash_table_insert_string (db->groups_by_name, 316 info->groupname, 317 info)) 318 { 319 _dbus_hash_table_remove_uintptr (db->groups, info->gid); 320 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 321 return NULL; 322 } 323 324 return info; 325 } 326} 327 328 329/** 330 * Gets the user information for the given group name, 331 * returned group info should not be freed. 332 * 333 * @param db user database 334 * @param groupname the group name 335 * @param info return location for const ref to group info 336 * @param error error location 337 * @returns #FALSE if error is set 338 */ 339dbus_bool_t 340_dbus_user_database_get_groupname (DBusUserDatabase *db, 341 const DBusString *groupname, 342 const DBusGroupInfo **info, 343 DBusError *error) 344{ 345 *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error); 346 return *info != NULL; 347} 348 349/** 350 * Gets the user information for the given GID, 351 * returned group info should not be freed. 352 * 353 * @param db user database 354 * @param gid the group ID 355 * @param info return location for const ref to group info 356 * @param error error location 357 * @returns #FALSE if error is set 358 */ 359dbus_bool_t 360_dbus_user_database_get_gid (DBusUserDatabase *db, 361 dbus_gid_t gid, 362 const DBusGroupInfo **info, 363 DBusError *error) 364{ 365 *info = _dbus_user_database_lookup_group (db, gid, NULL, error); 366 return *info != NULL; 367} 368 369 370/** 371 * Gets all groups corresponding to the given UID. Returns #FALSE 372 * if no memory, or user isn't known, but always initializes 373 * group_ids to a NULL array. 374 * 375 * @param uid the UID 376 * @param group_ids return location for array of group IDs 377 * @param n_group_ids return location for length of returned array 378 * @returns #TRUE if the UID existed and we got some credentials 379 */ 380dbus_bool_t 381_dbus_groups_from_uid (dbus_uid_t uid, 382 dbus_gid_t **group_ids, 383 int *n_group_ids) 384{ 385 DBusUserDatabase *db; 386 const DBusUserInfo *info; 387 *group_ids = NULL; 388 *n_group_ids = 0; 389 390 _dbus_user_database_lock_system (); 391 392 db = _dbus_user_database_get_system (); 393 if (db == NULL) 394 { 395 _dbus_user_database_unlock_system (); 396 return FALSE; 397 } 398 399 if (!_dbus_user_database_get_uid (db, uid, 400 &info, NULL)) 401 { 402 _dbus_user_database_unlock_system (); 403 return FALSE; 404 } 405 406 _dbus_assert (info->uid == uid); 407 408 if (info->n_group_ids > 0) 409 { 410 *group_ids = dbus_new (dbus_gid_t, info->n_group_ids); 411 if (*group_ids == NULL) 412 { 413 _dbus_user_database_unlock_system (); 414 return FALSE; 415 } 416 417 *n_group_ids = info->n_group_ids; 418 419 memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t)); 420 } 421 422 _dbus_user_database_unlock_system (); 423 return TRUE; 424} 425/** @} */ 426 427#ifdef DBUS_BUILD_TESTS 428#include <stdio.h> 429 430/** 431 * Unit test for dbus-userdb.c. 432 * 433 * @returns #TRUE on success. 434 */ 435dbus_bool_t 436_dbus_userdb_test (const char *test_data_dir) 437{ 438 const DBusString *username; 439 const DBusString *homedir; 440 dbus_uid_t uid; 441 unsigned long *group_ids; 442 int n_group_ids, i; 443 DBusError error; 444 445 if (!_dbus_username_from_current_process (&username)) 446 _dbus_assert_not_reached ("didn't get username"); 447 448 if (!_dbus_homedir_from_current_process (&homedir)) 449 _dbus_assert_not_reached ("didn't get homedir"); 450 451 if (!_dbus_get_user_id (username, &uid)) 452 _dbus_assert_not_reached ("didn't get uid"); 453 454 if (!_dbus_groups_from_uid (uid, &group_ids, &n_group_ids)) 455 _dbus_assert_not_reached ("didn't get groups"); 456 457 printf (" Current user: %s homedir: %s gids:", 458 _dbus_string_get_const_data (username), 459 _dbus_string_get_const_data (homedir)); 460 461 for (i=0; i<n_group_ids; i++) 462 printf(" %ld", group_ids[i]); 463 464 printf ("\n"); 465 466 dbus_error_init (&error); 467 printf ("Is Console user: %i\n", 468 _dbus_is_console_user (uid, &error)); 469 printf ("Invocation was OK: %s\n", error.message ? error.message : "yes"); 470 dbus_error_free (&error); 471 printf ("Is Console user 4711: %i\n", 472 _dbus_is_console_user (4711, &error)); 473 printf ("Invocation was OK: %s\n", error.message ? error.message : "yes"); 474 dbus_error_free (&error); 475 476 dbus_free (group_ids); 477 478 return TRUE; 479} 480#endif /* DBUS_BUILD_TESTS */ 481