D-Bus  1.7.6
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  if (uid != DBUS_UID_UNSET)
148  info = _dbus_hash_table_lookup_uintptr (db->users, uid);
149  else
150  info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
151 
152  if (info)
153  {
154  _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
155  info->uid);
156  return info;
157  }
158  else
159  {
160  if (uid != DBUS_UID_UNSET)
161  _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
162  uid);
163  else
164  _dbus_verbose ("No cache for user \"%s\"\n",
165  _dbus_string_get_const_data (username));
166 
167  info = dbus_new0 (DBusUserInfo, 1);
168  if (info == NULL)
169  {
171  return NULL;
172  }
173 
174  if (uid != DBUS_UID_UNSET)
175  {
176  if (!_dbus_user_info_fill_uid (info, uid, error))
177  {
178  _DBUS_ASSERT_ERROR_IS_SET (error);
180  return NULL;
181  }
182  }
183  else
184  {
185  if (!_dbus_user_info_fill (info, username, error))
186  {
187  _DBUS_ASSERT_ERROR_IS_SET (error);
189  return NULL;
190  }
191  }
192 
193  /* be sure we don't use these after here */
194  uid = DBUS_UID_UNSET;
195  username = NULL;
196 
197  /* insert into hash */
198  if (!_dbus_hash_table_insert_uintptr (db->users, info->uid, info))
199  {
202  return NULL;
203  }
204 
205  if (!_dbus_hash_table_insert_string (db->users_by_name,
206  info->username,
207  info))
208  {
209  _dbus_hash_table_remove_uintptr (db->users, info->uid);
211  return NULL;
212  }
213 
214  return info;
215  }
216 }
217 
218 static dbus_bool_t database_locked = FALSE;
219 static DBusUserDatabase *system_db = NULL;
220 static DBusString process_username;
221 static DBusString process_homedir;
222 
223 static void
224 shutdown_system_db (void *data)
225 {
226  if (system_db != NULL)
227  _dbus_user_database_unref (system_db);
228  system_db = NULL;
229  _dbus_string_free (&process_username);
230  _dbus_string_free (&process_homedir);
231 }
232 
233 static dbus_bool_t
234 init_system_db (void)
235 {
236  _dbus_assert (database_locked);
237 
238  if (system_db == NULL)
239  {
240  DBusError error = DBUS_ERROR_INIT;
241  const DBusUserInfo *info;
242 
243  system_db = _dbus_user_database_new ();
244  if (system_db == NULL)
245  return FALSE;
246 
247  if (!_dbus_user_database_get_uid (system_db,
248  _dbus_getuid (),
249  &info,
250  &error))
251  {
252  _dbus_user_database_unref (system_db);
253  system_db = NULL;
254 
256  {
257  dbus_error_free (&error);
258  return FALSE;
259  }
260  else
261  {
262  /* This really should not happen. */
263  _dbus_warn ("Could not get password database information for UID of current process: %s\n",
264  error.message);
265  dbus_error_free (&error);
266  return FALSE;
267  }
268  }
269 
270  if (!_dbus_string_init (&process_username))
271  {
272  _dbus_user_database_unref (system_db);
273  system_db = NULL;
274  return FALSE;
275  }
276 
277  if (!_dbus_string_init (&process_homedir))
278  {
279  _dbus_string_free (&process_username);
280  _dbus_user_database_unref (system_db);
281  system_db = NULL;
282  return FALSE;
283  }
284 
285  if (!_dbus_string_append (&process_username,
286  info->username) ||
287  !_dbus_string_append (&process_homedir,
288  info->homedir) ||
289  !_dbus_register_shutdown_func (shutdown_system_db, NULL))
290  {
291  _dbus_string_free (&process_username);
292  _dbus_string_free (&process_homedir);
293  _dbus_user_database_unref (system_db);
294  system_db = NULL;
295  return FALSE;
296  }
297  }
298 
299  return TRUE;
300 }
301 
307 {
308  if (_DBUS_LOCK (system_users))
309  {
310  database_locked = TRUE;
311  return TRUE;
312  }
313  else
314  {
315  return FALSE;
316  }
317 }
318 
322 void
324 {
325  database_locked = FALSE;
326  _DBUS_UNLOCK (system_users);
327 }
328 
335 DBusUserDatabase*
337 {
338  _dbus_assert (database_locked);
339 
340  init_system_db ();
341 
342  return system_db;
343 }
344 
348 void
350 {
352  {
353  /* nothing to flush */
354  return;
355  }
356 
357  if (system_db != NULL)
358  _dbus_user_database_flush (system_db);
359 
361 }
362 
372 {
374  return FALSE;
375 
376  if (!init_system_db ())
377  {
379  return FALSE;
380  }
381  *username = &process_username;
383 
384  return TRUE;
385 }
386 
396 {
398  return FALSE;
399 
400  if (!init_system_db ())
401  {
403  return FALSE;
404  }
405  *homedir = &process_homedir;
407 
408  return TRUE;
409 }
410 
420  DBusString *homedir)
421 {
422  DBusUserDatabase *db;
423  const DBusUserInfo *info;
424 
425  /* FIXME: this can't distinguish ENOMEM from other errors */
427  return FALSE;
428 
430  if (db == NULL)
431  {
433  return FALSE;
434  }
435 
436  if (!_dbus_user_database_get_username (db, username,
437  &info, NULL))
438  {
440  return FALSE;
441  }
442 
443  if (!_dbus_string_append (homedir, info->homedir))
444  {
446  return FALSE;
447  }
448 
450  return TRUE;
451 }
452 
462  DBusString *homedir)
463 {
464  DBusUserDatabase *db;
465  const DBusUserInfo *info;
466 
467  /* FIXME: this can't distinguish ENOMEM from other errors */
469  return FALSE;
470 
472  if (db == NULL)
473  {
475  return FALSE;
476  }
477 
478  if (!_dbus_user_database_get_uid (db, uid,
479  &info, NULL))
480  {
482  return FALSE;
483  }
484 
485  if (!_dbus_string_append (homedir, info->homedir))
486  {
488  return FALSE;
489  }
490 
492  return TRUE;
493 }
494 
511  const DBusString *username)
512 {
513  DBusUserDatabase *db;
514  const DBusUserInfo *info;
515 
516  /* FIXME: this can't distinguish ENOMEM from other errors */
518  return FALSE;
519 
521  if (db == NULL)
522  {
524  return FALSE;
525  }
526 
527  if (!_dbus_user_database_get_username (db, username,
528  &info, NULL))
529  {
531  return FALSE;
532  }
533 
534  if (!_dbus_credentials_add_unix_uid(credentials, info->uid))
535  {
537  return FALSE;
538  }
539 
541  return TRUE;
542 }
543 
549 DBusUserDatabase*
551 {
552  DBusUserDatabase *db;
553 
554  db = dbus_new0 (DBusUserDatabase, 1);
555  if (db == NULL)
556  return NULL;
557 
558  db->refcount = 1;
559 
562 
563  if (db->users == NULL)
564  goto failed;
565 
568 
569  if (db->groups == NULL)
570  goto failed;
571 
572  db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
573  NULL, NULL);
574  if (db->users_by_name == NULL)
575  goto failed;
576 
577  db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
578  NULL, NULL);
579  if (db->groups_by_name == NULL)
580  goto failed;
581 
582  return db;
583 
584  failed:
586  return NULL;
587 }
588 
592 void
593 _dbus_user_database_flush (DBusUserDatabase *db)
594 {
595  _dbus_hash_table_remove_all(db->users_by_name);
596  _dbus_hash_table_remove_all(db->groups_by_name);
597  _dbus_hash_table_remove_all(db->users);
598  _dbus_hash_table_remove_all(db->groups);
599 }
600 
601 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
602 
607 DBusUserDatabase *
608 _dbus_user_database_ref (DBusUserDatabase *db)
609 {
610  _dbus_assert (db->refcount > 0);
611 
612  db->refcount += 1;
613 
614  return db;
615 }
616 #endif /* DBUS_ENABLE_EMBEDDED_TESTS */
617 
622 void
623 _dbus_user_database_unref (DBusUserDatabase *db)
624 {
625  _dbus_assert (db->refcount > 0);
626 
627  db->refcount -= 1;
628  if (db->refcount == 0)
629  {
630  if (db->users)
631  _dbus_hash_table_unref (db->users);
632 
633  if (db->groups)
634  _dbus_hash_table_unref (db->groups);
635 
636  if (db->users_by_name)
637  _dbus_hash_table_unref (db->users_by_name);
638 
639  if (db->groups_by_name)
640  _dbus_hash_table_unref (db->groups_by_name);
641 
642  dbus_free (db);
643  }
644 }
645 
657 _dbus_user_database_get_uid (DBusUserDatabase *db,
658  dbus_uid_t uid,
659  const DBusUserInfo **info,
660  DBusError *error)
661 {
662  *info = _dbus_user_database_lookup (db, uid, NULL, error);
663  return *info != NULL;
664 }
665 
676 _dbus_user_database_get_username (DBusUserDatabase *db,
677  const DBusString *username,
678  const DBusUserInfo **info,
679  DBusError *error)
680 {
681  *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
682  return *info != NULL;
683 }
684 
687 /* Tests in dbus-userdb-util.c */