D-Bus  1.12.16
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",
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  if (uid == _dbus_getuid () && uid == _dbus_geteuid ())
468  {
469  const char *from_environment;
470 
471  from_environment = _dbus_getenv ("HOME");
472 
473  if (from_environment != NULL)
474  return _dbus_string_append (homedir, from_environment);
475  }
476 
477  /* FIXME: this can't distinguish ENOMEM from other errors */
479  return FALSE;
480 
482  if (db == NULL)
483  {
485  return FALSE;
486  }
487 
488  if (!_dbus_user_database_get_uid (db, uid,
489  &info, NULL))
490  {
492  return FALSE;
493  }
494 
495  if (!_dbus_string_append (homedir, info->homedir))
496  {
498  return FALSE;
499  }
500 
502  return TRUE;
503 }
504 
521  const DBusString *username)
522 {
523  DBusUserDatabase *db;
524  const DBusUserInfo *info;
525 
526  /* FIXME: this can't distinguish ENOMEM from other errors */
528  return FALSE;
529 
531  if (db == NULL)
532  {
534  return FALSE;
535  }
536 
537  if (!_dbus_user_database_get_username (db, username,
538  &info, NULL))
539  {
541  return FALSE;
542  }
543 
544  if (!_dbus_credentials_add_unix_uid(credentials, info->uid))
545  {
547  return FALSE;
548  }
549 
551  return TRUE;
552 }
553 
559 DBusUserDatabase*
561 {
562  DBusUserDatabase *db;
563 
564  db = dbus_new0 (DBusUserDatabase, 1);
565  if (db == NULL)
566  return NULL;
567 
568  db->refcount = 1;
569 
572 
573  if (db->users == NULL)
574  goto failed;
575 
578 
579  if (db->groups == NULL)
580  goto failed;
581 
582  db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
583  NULL, NULL);
584  if (db->users_by_name == NULL)
585  goto failed;
586 
587  db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
588  NULL, NULL);
589  if (db->groups_by_name == NULL)
590  goto failed;
591 
592  return db;
593 
594  failed:
596  return NULL;
597 }
598 
602 void
603 _dbus_user_database_flush (DBusUserDatabase *db)
604 {
605  _dbus_hash_table_remove_all(db->users_by_name);
606  _dbus_hash_table_remove_all(db->groups_by_name);
607  _dbus_hash_table_remove_all(db->users);
608  _dbus_hash_table_remove_all(db->groups);
609 }
610 
611 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
612 
617 DBusUserDatabase *
618 _dbus_user_database_ref (DBusUserDatabase *db)
619 {
620  _dbus_assert (db->refcount > 0);
621 
622  db->refcount += 1;
623 
624  return db;
625 }
626 #endif /* DBUS_ENABLE_EMBEDDED_TESTS */
627 
632 void
633 _dbus_user_database_unref (DBusUserDatabase *db)
634 {
635  _dbus_assert (db->refcount > 0);
636 
637  db->refcount -= 1;
638  if (db->refcount == 0)
639  {
640  if (db->users)
641  _dbus_hash_table_unref (db->users);
642 
643  if (db->groups)
644  _dbus_hash_table_unref (db->groups);
645 
646  if (db->users_by_name)
647  _dbus_hash_table_unref (db->users_by_name);
648 
649  if (db->groups_by_name)
650  _dbus_hash_table_unref (db->groups_by_name);
651 
652  dbus_free (db);
653  }
654 }
655 
667 _dbus_user_database_get_uid (DBusUserDatabase *db,
668  dbus_uid_t uid,
669  const DBusUserInfo **info,
670  DBusError *error)
671 {
672  *info = _dbus_user_database_lookup (db, uid, NULL, error);
673  return *info != NULL;
674 }
675 
686 _dbus_user_database_get_username (DBusUserDatabase *db,
687  const DBusString *username,
688  const DBusUserInfo **info,
689  DBusError *error)
690 {
691  *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
692  return *info != NULL;
693 }
694 
697 /* Tests in dbus-userdb-util.c */