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