D-Bus  1.6.12
dbus-userdb.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
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., 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-hash.h"
27 #include "dbus-test.h"
28 #include "dbus-internals.h"
29 #include "dbus-protocol.h"
30 #include "dbus-credentials.h"
31 #include <string.h>
32 
44 void
46 {
47  if (info == NULL) /* hash table will pass NULL */
48  return;
49 
50  _dbus_user_info_free (info);
51  dbus_free (info);
52 }
53 
60 void
62 {
63  if (info == NULL) /* hash table will pass NULL */
64  return;
65 
66  _dbus_group_info_free (info);
67  dbus_free (info);
68 }
69 
75 void
77 {
78  dbus_free (info->group_ids);
79  dbus_free (info->username);
80  dbus_free (info->homedir);
81 }
82 
88 void
90 {
91  dbus_free (info->groupname);
92 }
93 
104  unsigned long *num)
105 {
106  int end;
107 
108  if (_dbus_string_parse_uint (str, 0, num, &end) &&
109  end == _dbus_string_get_length (str))
110  return TRUE;
111  else
112  return FALSE;
113 }
114 
128 _dbus_user_database_lookup (DBusUserDatabase *db,
129  dbus_uid_t uid,
130  const DBusString *username,
131  DBusError *error)
132 {
133  DBusUserInfo *info;
134 
135  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
136  _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
137 
138  /* See if the username is really a number */
139  if (uid == DBUS_UID_UNSET)
140  {
141  unsigned long n;
142 
143  if (_dbus_is_a_number (username, &n))
144  uid = n;
145  }
146 
147 #ifdef DBUS_ENABLE_USERDB_CACHE
148  if (uid != DBUS_UID_UNSET)
149  info = _dbus_hash_table_lookup_uintptr (db->users, uid);
150  else
151  info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
152 
153  if (info)
154  {
155  _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
156  info->uid);
157  return info;
158  }
159  else
160 #else
161  if (1)
162 #endif
163  {
164  if (uid != DBUS_UID_UNSET)
165  _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
166  uid);
167  else
168  _dbus_verbose ("No cache for user \"%s\"\n",
169  _dbus_string_get_const_data (username));
170 
171  info = dbus_new0 (DBusUserInfo, 1);
172  if (info == NULL)
173  {
175  return NULL;
176  }
177 
178  if (uid != DBUS_UID_UNSET)
179  {
180  if (!_dbus_user_info_fill_uid (info, uid, error))
181  {
182  _DBUS_ASSERT_ERROR_IS_SET (error);
184  return NULL;
185  }
186  }
187  else
188  {
189  if (!_dbus_user_info_fill (info, username, error))
190  {
191  _DBUS_ASSERT_ERROR_IS_SET (error);
193  return NULL;
194  }
195  }
196 
197  /* be sure we don't use these after here */
198  uid = DBUS_UID_UNSET;
199  username = NULL;
200 
201  /* insert into hash */
202  if (!_dbus_hash_table_insert_uintptr (db->users, info->uid, info))
203  {
206  return NULL;
207  }
208 
209  if (!_dbus_hash_table_insert_string (db->users_by_name,
210  info->username,
211  info))
212  {
213  _dbus_hash_table_remove_uintptr (db->users, info->uid);
215  return NULL;
216  }
217 
218  return info;
219  }
220 }
221 
222 static dbus_bool_t database_locked = FALSE;
223 static DBusUserDatabase *system_db = NULL;
224 static DBusString process_username;
225 static DBusString process_homedir;
226 
227 static void
228 shutdown_system_db (void *data)
229 {
230  if (system_db != NULL)
231  _dbus_user_database_unref (system_db);
232  system_db = NULL;
233  _dbus_string_free (&process_username);
234  _dbus_string_free (&process_homedir);
235 }
236 
237 static dbus_bool_t
238 init_system_db (void)
239 {
240  _dbus_assert (database_locked);
241 
242  if (system_db == NULL)
243  {
244  DBusError error = DBUS_ERROR_INIT;
245  const DBusUserInfo *info;
246 
247  system_db = _dbus_user_database_new ();
248  if (system_db == NULL)
249  return FALSE;
250 
251  if (!_dbus_user_database_get_uid (system_db,
252  _dbus_getuid (),
253  &info,
254  &error))
255  {
256  _dbus_user_database_unref (system_db);
257  system_db = NULL;
258 
260  {
261  dbus_error_free (&error);
262  return FALSE;
263  }
264  else
265  {
266  /* This really should not happen. */
267  _dbus_warn ("Could not get password database information for UID of current process: %s\n",
268  error.message);
269  dbus_error_free (&error);
270  return FALSE;
271  }
272  }
273 
274  if (!_dbus_string_init (&process_username))
275  {
276  _dbus_user_database_unref (system_db);
277  system_db = NULL;
278  return FALSE;
279  }
280 
281  if (!_dbus_string_init (&process_homedir))
282  {
283  _dbus_string_free (&process_username);
284  _dbus_user_database_unref (system_db);
285  system_db = NULL;
286  return FALSE;
287  }
288 
289  if (!_dbus_string_append (&process_username,
290  info->username) ||
291  !_dbus_string_append (&process_homedir,
292  info->homedir) ||
293  !_dbus_register_shutdown_func (shutdown_system_db, NULL))
294  {
295  _dbus_string_free (&process_username);
296  _dbus_string_free (&process_homedir);
297  _dbus_user_database_unref (system_db);
298  system_db = NULL;
299  return FALSE;
300  }
301  }
302 
303  return TRUE;
304 }
305 
311 {
312  if (_DBUS_LOCK (system_users))
313  {
314  database_locked = TRUE;
315  return TRUE;
316  }
317  else
318  {
319  return FALSE;
320  }
321 }
322 
326 void
328 {
329  database_locked = FALSE;
330  _DBUS_UNLOCK (system_users);
331 }
332 
339 DBusUserDatabase*
341 {
342  _dbus_assert (database_locked);
343 
344  init_system_db ();
345 
346  return system_db;
347 }
348 
352 void
354 {
356  {
357  /* nothing to flush */
358  return;
359  }
360 
361  if (system_db != NULL)
362  _dbus_user_database_flush (system_db);
363 
365 }
366 
376 {
378  return FALSE;
379 
380  if (!init_system_db ())
381  {
383  return FALSE;
384  }
385  *username = &process_username;
387 
388  return TRUE;
389 }
390 
400 {
402  return FALSE;
403 
404  if (!init_system_db ())
405  {
407  return FALSE;
408  }
409  *homedir = &process_homedir;
411 
412  return TRUE;
413 }
414 
424  DBusString *homedir)
425 {
426  DBusUserDatabase *db;
427  const DBusUserInfo *info;
428 
429  /* FIXME: this can't distinguish ENOMEM from other errors */
431  return FALSE;
432 
434  if (db == NULL)
435  {
437  return FALSE;
438  }
439 
440  if (!_dbus_user_database_get_username (db, username,
441  &info, NULL))
442  {
444  return FALSE;
445  }
446 
447  if (!_dbus_string_append (homedir, info->homedir))
448  {
450  return FALSE;
451  }
452 
454  return TRUE;
455 }
456 
466  DBusString *homedir)
467 {
468  DBusUserDatabase *db;
469  const DBusUserInfo *info;
470 
471  /* FIXME: this can't distinguish ENOMEM from other errors */
473  return FALSE;
474 
476  if (db == NULL)
477  {
479  return FALSE;
480  }
481 
482  if (!_dbus_user_database_get_uid (db, uid,
483  &info, NULL))
484  {
486  return FALSE;
487  }
488 
489  if (!_dbus_string_append (homedir, info->homedir))
490  {
492  return FALSE;
493  }
494 
496  return TRUE;
497 }
498 
515  const DBusString *username)
516 {
517  DBusUserDatabase *db;
518  const DBusUserInfo *info;
519 
520  /* FIXME: this can't distinguish ENOMEM from other errors */
522  return FALSE;
523 
525  if (db == NULL)
526  {
528  return FALSE;
529  }
530 
531  if (!_dbus_user_database_get_username (db, username,
532  &info, NULL))
533  {
535  return FALSE;
536  }
537 
538  if (!_dbus_credentials_add_unix_uid(credentials, info->uid))
539  {
541  return FALSE;
542  }
543 
545  return TRUE;
546 }
547 
553 DBusUserDatabase*
555 {
556  DBusUserDatabase *db;
557 
558  db = dbus_new0 (DBusUserDatabase, 1);
559  if (db == NULL)
560  return NULL;
561 
562  db->refcount = 1;
563 
566 
567  if (db->users == NULL)
568  goto failed;
569 
572 
573  if (db->groups == NULL)
574  goto failed;
575 
576  db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
577  NULL, NULL);
578  if (db->users_by_name == NULL)
579  goto failed;
580 
581  db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
582  NULL, NULL);
583  if (db->groups_by_name == NULL)
584  goto failed;
585 
586  return db;
587 
588  failed:
590  return NULL;
591 }
592 
596 void
597 _dbus_user_database_flush (DBusUserDatabase *db)
598 {
599  _dbus_hash_table_remove_all(db->users_by_name);
600  _dbus_hash_table_remove_all(db->groups_by_name);
601  _dbus_hash_table_remove_all(db->users);
602  _dbus_hash_table_remove_all(db->groups);
603 }
604 
605 #ifdef DBUS_BUILD_TESTS
606 
611 DBusUserDatabase *
612 _dbus_user_database_ref (DBusUserDatabase *db)
613 {
614  _dbus_assert (db->refcount > 0);
615 
616  db->refcount += 1;
617 
618  return db;
619 }
620 #endif /* DBUS_BUILD_TESTS */
621 
626 void
627 _dbus_user_database_unref (DBusUserDatabase *db)
628 {
629  _dbus_assert (db->refcount > 0);
630 
631  db->refcount -= 1;
632  if (db->refcount == 0)
633  {
634  if (db->users)
635  _dbus_hash_table_unref (db->users);
636 
637  if (db->groups)
638  _dbus_hash_table_unref (db->groups);
639 
640  if (db->users_by_name)
641  _dbus_hash_table_unref (db->users_by_name);
642 
643  if (db->groups_by_name)
644  _dbus_hash_table_unref (db->groups_by_name);
645 
646  dbus_free (db);
647  }
648 }
649 
661 _dbus_user_database_get_uid (DBusUserDatabase *db,
662  dbus_uid_t uid,
663  const DBusUserInfo **info,
664  DBusError *error)
665 {
666  *info = _dbus_user_database_lookup (db, uid, NULL, error);
667  return *info != NULL;
668 }
669 
680 _dbus_user_database_get_username (DBusUserDatabase *db,
681  const DBusString *username,
682  const DBusUserInfo **info,
683  DBusError *error)
684 {
685  *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
686  return *info != NULL;
687 }
688 
691 /* Tests in dbus-userdb-util.c */