1 /* -*- mode: C; c-file-style: "gnu" -*- */ 2 /* dbus-userdb.c User database abstraction 3 * 4 * Copyright (C) 2003, 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 */ 23 #define DBUS_USERDB_INCLUDES_PRIVATE 1 24 #include "dbus-userdb.h" 25 #include "dbus-hash.h" 26 #include "dbus-test.h" 27 #include "dbus-internals.h" 28 #include "dbus-protocol.h" 29 #include <string.h> 30 31 /** 32 * @addtogroup DBusInternalsUtils 33 * @{ 34 */ 35 36 /** 37 * Frees the given #DBusUserInfo's members with _dbus_user_info_free() 38 * and also calls dbus_free() on the block itself 39 * 40 * @param info the info 41 */ 42 void 43 _dbus_user_info_free_allocated (DBusUserInfo *info) 44 { 45 if (info == NULL) /* hash table will pass NULL */ 46 return; 47 48 _dbus_user_info_free (info); 49 dbus_free (info); 50 } 51 52 /** 53 * Frees the given #DBusGroupInfo's members with _dbus_group_info_free() 54 * and also calls dbus_free() on the block itself 55 * 56 * @param info the info 57 */ 58 void 59 _dbus_group_info_free_allocated (DBusGroupInfo *info) 60 { 61 if (info == NULL) /* hash table will pass NULL */ 62 return; 63 64 _dbus_group_info_free (info); 65 dbus_free (info); 66 } 67 68 /** 69 * Checks if a given string is actually a number 70 * and converts it if it is 71 * 72 * @param str the string to check 73 * @param num the memory location of the unsigned long to fill in 74 * @returns TRUE if str is a number and num is filled in 75 */ 76 dbus_bool_t 77 _dbus_is_a_number (const DBusString *str, 78 unsigned long *num) 79 { 80 int end; 81 82 if (_dbus_string_parse_int (str, 0, num, &end) && 83 end == _dbus_string_get_length (str)) 84 return TRUE; 85 else 86 return FALSE; 87 } 88 89 /** 90 * Looks up a uid or username in the user database. Only one of name 91 * or UID can be provided. There are wrapper functions for this that 92 * are better to use, this one does no locking or anything on the 93 * database and otherwise sort of sucks. 94 * 95 * @param db the database 96 * @param uid the user ID or #DBUS_UID_UNSET 97 * @param username username or #NULL 98 * @param error error to fill in 99 * @returns the entry in the database 100 */ 101 DBusUserInfo* 102 _dbus_user_database_lookup (DBusUserDatabase *db, 103 dbus_uid_t uid, 104 const DBusString *username, 105 DBusError *error) 106 { 107 DBusUserInfo *info; 108 109 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 110 _dbus_assert (uid != DBUS_UID_UNSET || username != NULL); 111 112 /* See if the username is really a number */ 113 if (uid == DBUS_UID_UNSET) 114 { 115 unsigned long n; 116 117 if (_dbus_is_a_number (username, &n)) 118 uid = n; 119 } 120 121 if (uid != DBUS_UID_UNSET) 122 info = _dbus_hash_table_lookup_ulong (db->users, uid); 123 else 124 info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username)); 125 126 if (info) 127 { 128 _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n", 129 info->uid); 130 return info; 131 } 132 else 133 { 134 if (uid != DBUS_UID_UNSET) 135 _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n", 136 uid); 137 else 138 _dbus_verbose ("No cache for user \"%s\"\n", 139 _dbus_string_get_const_data (username)); 140 141 info = dbus_new0 (DBusUserInfo, 1); 142 if (info == NULL) 143 { 144 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 145 return NULL; 146 } 147 148 if (uid != DBUS_UID_UNSET) 149 { 150 if (!_dbus_user_info_fill_uid (info, uid, error)) 151 { 152 _DBUS_ASSERT_ERROR_IS_SET (error); 153 _dbus_user_info_free_allocated (info); 154 return NULL; 155 } 156 } 157 else 158 { 159 if (!_dbus_user_info_fill (info, username, error)) 160 { 161 _DBUS_ASSERT_ERROR_IS_SET (error); 162 _dbus_user_info_free_allocated (info); 163 return NULL; 164 } 165 } 166 167 /* be sure we don't use these after here */ 168 uid = DBUS_UID_UNSET; 169 username = NULL; 170 171 /* insert into hash */ 172 if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info)) 173 { 174 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 175 _dbus_user_info_free_allocated (info); 176 return NULL; 177 } 178 179 if (!_dbus_hash_table_insert_string (db->users_by_name, 180 info->username, 181 info)) 182 { 183 _dbus_hash_table_remove_ulong (db->users, info->uid); 184 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 185 return NULL; 186 } 187 188 return info; 189 } 190 } 191 192 _DBUS_DEFINE_GLOBAL_LOCK(system_users); 193 static dbus_bool_t database_locked = FALSE; 194 static DBusUserDatabase *system_db = NULL; 195 static DBusString process_username; 196 static DBusString process_homedir; 197 198 static void 199 shutdown_system_db (void *data) 200 { 201 _dbus_user_database_unref (system_db); 202 system_db = NULL; 203 _dbus_string_free (&process_username); 204 _dbus_string_free (&process_homedir); 205 } 206 207 static dbus_bool_t 208 init_system_db (void) 209 { 210 _dbus_assert (database_locked); 211 212 if (system_db == NULL) 213 { 214 DBusError error; 215 const DBusUserInfo *info; 216 217 system_db = _dbus_user_database_new (); 218 if (system_db == NULL) 219 return FALSE; 220 221 dbus_error_init (&error); 222 223 if (!_dbus_user_database_get_uid (system_db, 224 _dbus_getuid (), 225 &info, 226 &error)) 227 { 228 _dbus_user_database_unref (system_db); 229 system_db = NULL; 230 231 if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) 232 { 233 dbus_error_free (&error); 234 return FALSE; 235 } 236 else 237 { 238 /* This really should not happen. */ 239 _dbus_warn ("Could not get password database information for UID of current process: %s\n", 240 error.message); 241 dbus_error_free (&error); 242 return FALSE; 243 } 244 } 245 246 if (!_dbus_string_init (&process_username)) 247 { 248 _dbus_user_database_unref (system_db); 249 system_db = NULL; 250 return FALSE; 251 } 252 253 if (!_dbus_string_init (&process_homedir)) 254 { 255 _dbus_string_free (&process_username); 256 _dbus_user_database_unref (system_db); 257 system_db = NULL; 258 return FALSE; 259 } 260 261 if (!_dbus_string_append (&process_username, 262 info->username) || 263 !_dbus_string_append (&process_homedir, 264 info->homedir) || 265 !_dbus_register_shutdown_func (shutdown_system_db, NULL)) 266 { 267 _dbus_string_free (&process_username); 268 _dbus_string_free (&process_homedir); 269 _dbus_user_database_unref (system_db); 270 system_db = NULL; 271 return FALSE; 272 } 273 } 274 275 return TRUE; 276 } 277 278 /** 279 * Locks global system user database. 280 */ 281 void 282 _dbus_user_database_lock_system (void) 283 { 284 _DBUS_LOCK (system_users); 285 database_locked = TRUE; 286 } 287 288 /** 289 * Unlocks global system user database. 290 */ 291 void 292 _dbus_user_database_unlock_system (void) 293 { 294 database_locked = FALSE; 295 _DBUS_UNLOCK (system_users); 296 } 297 298 /** 299 * Gets the system global user database; 300 * must be called with lock held (_dbus_user_database_lock_system()). 301 * 302 * @returns the database or #NULL if no memory 303 */ 304 DBusUserDatabase* 305 _dbus_user_database_get_system (void) 306 { 307 _dbus_assert (database_locked); 308 309 init_system_db (); 310 311 return system_db; 312 } 313 314 /** 315 * Gets username of user owning current process. The returned string 316 * is valid until dbus_shutdown() is called. 317 * 318 * @param username place to store pointer to username 319 * @returns #FALSE if no memory 320 */ 321 dbus_bool_t 322 _dbus_username_from_current_process (const DBusString **username) 323 { 324 _dbus_user_database_lock_system (); 325 if (!init_system_db ()) 326 { 327 _dbus_user_database_unlock_system (); 328 return FALSE; 329 } 330 *username = &process_username; 331 _dbus_user_database_unlock_system (); 332 333 return TRUE; 334 } 335 336 /** 337 * Gets homedir of user owning current process. The returned string 338 * is valid until dbus_shutdown() is called. 339 * 340 * @param homedir place to store pointer to homedir 341 * @returns #FALSE if no memory 342 */ 343 dbus_bool_t 344 _dbus_homedir_from_current_process (const DBusString **homedir) 345 { 346 _dbus_user_database_lock_system (); 347 if (!init_system_db ()) 348 { 349 _dbus_user_database_unlock_system (); 350 return FALSE; 351 } 352 *homedir = &process_homedir; 353 _dbus_user_database_unlock_system (); 354 355 return TRUE; 356 } 357 358 /** 359 * Gets the home directory for the given user. 360 * 361 * @param username the username 362 * @param homedir string to append home directory to 363 * @returns #TRUE if user existed and we appended their homedir 364 */ 365 dbus_bool_t 366 _dbus_homedir_from_username (const DBusString *username, 367 DBusString *homedir) 368 { 369 DBusUserDatabase *db; 370 const DBusUserInfo *info; 371 _dbus_user_database_lock_system (); 372 373 db = _dbus_user_database_get_system (); 374 if (db == NULL) 375 { 376 _dbus_user_database_unlock_system (); 377 return FALSE; 378 } 379 380 if (!_dbus_user_database_get_username (db, username, 381 &info, NULL)) 382 { 383 _dbus_user_database_unlock_system (); 384 return FALSE; 385 } 386 387 if (!_dbus_string_append (homedir, info->homedir)) 388 { 389 _dbus_user_database_unlock_system (); 390 return FALSE; 391 } 392 393 _dbus_user_database_unlock_system (); 394 return TRUE; 395 } 396 397 /** 398 * Gets the credentials corresponding to the given username. 399 * 400 * @param username the username 401 * @param credentials credentials to fill in 402 * @returns #TRUE if the username existed and we got some credentials 403 */ 404 dbus_bool_t 405 _dbus_credentials_from_username (const DBusString *username, 406 DBusCredentials *credentials) 407 { 408 DBusUserDatabase *db; 409 const DBusUserInfo *info; 410 _dbus_user_database_lock_system (); 411 412 db = _dbus_user_database_get_system (); 413 if (db == NULL) 414 { 415 _dbus_user_database_unlock_system (); 416 return FALSE; 417 } 418 419 if (!_dbus_user_database_get_username (db, username, 420 &info, NULL)) 421 { 422 _dbus_user_database_unlock_system (); 423 return FALSE; 424 } 425 426 credentials->pid = DBUS_PID_UNSET; 427 credentials->uid = info->uid; 428 credentials->gid = info->primary_gid; 429 430 _dbus_user_database_unlock_system (); 431 return TRUE; 432 } 433 434 /** 435 * Creates a new user database object used to look up and 436 * cache user information. 437 * @returns new database, or #NULL on out of memory 438 */ 439 DBusUserDatabase* 440 _dbus_user_database_new (void) 441 { 442 DBusUserDatabase *db; 443 444 db = dbus_new0 (DBusUserDatabase, 1); 445 if (db == NULL) 446 return NULL; 447 448 db->refcount = 1; 449 450 db->users = _dbus_hash_table_new (DBUS_HASH_ULONG, 451 NULL, (DBusFreeFunction) _dbus_user_info_free_allocated); 452 453 if (db->users == NULL) 454 goto failed; 455 456 db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG, 457 NULL, (DBusFreeFunction) _dbus_group_info_free_allocated); 458 459 if (db->groups == NULL) 460 goto failed; 461 462 db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING, 463 NULL, NULL); 464 if (db->users_by_name == NULL) 465 goto failed; 466 467 db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING, 468 NULL, NULL); 469 if (db->groups_by_name == NULL) 470 goto failed; 471 472 return db; 473 474 failed: 475 _dbus_user_database_unref (db); 476 return NULL; 477 } 478 479 /** 480 * Flush all information out of the user database. 481 */ 482 void 483 _dbus_user_database_flush (DBusUserDatabase *db) 484 { 485 _dbus_hash_table_remove_all(db->users_by_name); 486 _dbus_hash_table_remove_all(db->groups_by_name); 487 _dbus_hash_table_remove_all(db->users); 488 _dbus_hash_table_remove_all(db->groups); 489 } 490 491 #ifdef DBUS_BUILD_TESTS 492 /** 493 * Increments refcount of user database. 494 * @param db the database 495 * @returns the database 496 */ 497 DBusUserDatabase * 498 _dbus_user_database_ref (DBusUserDatabase *db) 499 { 500 _dbus_assert (db->refcount > 0); 501 502 db->refcount += 1; 503 504 return db; 505 } 506 #endif /* DBUS_BUILD_TESTS */ 507 508 /** 509 * Decrements refcount of user database. 510 * @param db the database 511 */ 512 void 513 _dbus_user_database_unref (DBusUserDatabase *db) 514 { 515 _dbus_assert (db->refcount > 0); 516 517 db->refcount -= 1; 518 if (db->refcount == 0) 519 { 520 if (db->users) 521 _dbus_hash_table_unref (db->users); 522 523 if (db->groups) 524 _dbus_hash_table_unref (db->groups); 525 526 if (db->users_by_name) 527 _dbus_hash_table_unref (db->users_by_name); 528 529 if (db->groups_by_name) 530 _dbus_hash_table_unref (db->groups_by_name); 531 532 dbus_free (db); 533 } 534 } 535 536 /** 537 * Gets the user information for the given UID, 538 * returned user info should not be freed. 539 * 540 * @param db user database 541 * @param uid the user ID 542 * @param info return location for const ref to user info 543 * @param error error location 544 * @returns #FALSE if error is set 545 */ 546 dbus_bool_t 547 _dbus_user_database_get_uid (DBusUserDatabase *db, 548 dbus_uid_t uid, 549 const DBusUserInfo **info, 550 DBusError *error) 551 { 552 *info = _dbus_user_database_lookup (db, uid, NULL, error); 553 return *info != NULL; 554 } 555 556 /** 557 * Gets the user information for the given username. 558 * 559 * @param db user database 560 * @param username the user name 561 * @param info return location for const ref to user info 562 * @param error error location 563 * @returns #FALSE if error is set 564 */ 565 dbus_bool_t 566 _dbus_user_database_get_username (DBusUserDatabase *db, 567 const DBusString *username, 568 const DBusUserInfo **info, 569 DBusError *error) 570 { 571 *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error); 572 return *info != NULL; 573 } 574 575 /** @} */ 576 577 /* Tests in dbus-userdb-util.c */ 578