dbus-userdb-util.c 12.8 KB
Newer Older
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* dbus-userdb-util.c Would be in dbus-userdb.c, but not used in libdbus
 * 
 * Copyright (C) 2003, 2004, 2005  Red Hat, Inc.
 *
 * Licensed under the Academic Free License version 2.1
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * 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
#include <unistd.h>
25 26 27 28 29 30 31
#define DBUS_USERDB_INCLUDES_PRIVATE 1
#include "dbus-userdb.h"
#include "dbus-test.h"
#include "dbus-internals.h"
#include "dbus-protocol.h"
#include <string.h>

32 33 34 35
#if HAVE_SYSTEMD
#include <systemd/sd-login.h>
#endif

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
/**
 * @addtogroup DBusInternalsUtils
 * @{
 */

/**
 * Checks to see if the UID sent in is the console user
 *
 * @param uid UID of person to check 
 * @param error return location for errors
 * @returns #TRUE if the UID is the same as the console user and there are no errors
 */
dbus_bool_t
_dbus_is_console_user (dbus_uid_t uid,
		       DBusError *error)
{

  DBusUserDatabase *db;
  const DBusUserInfo *info;
55 56 57
  dbus_bool_t result = FALSE;

#ifdef HAVE_SYSTEMD
58 59
  /* check if we have logind */
  if (access ("/run/systemd/seats/", F_OK) >= 0)
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
    {
      int r;

      /* Check whether this user is logged in on at least one physical
         seat */
      r = sd_uid_get_seats (uid, 0, NULL);
      if (r < 0)
        {
          dbus_set_error (error, _dbus_error_from_errno (-r),
                          "Failed to determine seats of user \"" DBUS_UID_FORMAT "\": %s",
                          uid,
                          _dbus_strerror (-r));
          return FALSE;
        }

      return (r > 0);
    }
#endif
78

John Palmieri's avatar
John Palmieri committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
#ifdef HAVE_CONSOLE_OWNER_FILE

  DBusString f;
  DBusStat st;

  if (!_dbus_string_init (&f))
    {
      _DBUS_SET_OOM (error);
      return FALSE;
    }

  if (!_dbus_string_append(&f, DBUS_CONSOLE_OWNER_FILE))
    {
      _dbus_string_free(&f);
      _DBUS_SET_OOM (error);
      return FALSE;
    }

  if (_dbus_stat(&f, &st, NULL) && (st.uid == uid))
    {
      _dbus_string_free(&f);
      return TRUE;
    }

  _dbus_string_free(&f);

#endif /* HAVE_CONSOLE_OWNER_FILE */

107 108 109 110 111
  if (!_dbus_user_database_lock_system ())
    {
      _DBUS_SET_OOM (error);
      return FALSE;
    }
112 113 114 115 116 117 118 119 120

  db = _dbus_user_database_get_system ();
  if (db == NULL)
    {
      dbus_set_error (error, DBUS_ERROR_FAILED, "Could not get system database.");
      _dbus_user_database_unlock_system ();
      return FALSE;
    }

Tim Dijkstra's avatar
Tim Dijkstra committed
121 122
  /* TPTD: this should be cache-safe, we've locked the DB and
    _dbus_user_at_console doesn't pass it on. */
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
  info = _dbus_user_database_lookup (db, uid, NULL, error);

  if (info == NULL)
    {
      _dbus_user_database_unlock_system ();
       return FALSE;
    }

  result = _dbus_user_at_console (info->username, error);

  _dbus_user_database_unlock_system ();

  return result;
}

138 139 140 141 142 143 144 145 146 147 148 149 150
/**
 * Gets user ID given username
 *
 * @param username the username
 * @param uid return location for UID
 * @returns #TRUE if username existed and we got the UID
 */
dbus_bool_t
_dbus_get_user_id (const DBusString  *username,
                   dbus_uid_t        *uid)
{
  return _dbus_get_user_id_and_primary_group (username, uid, NULL);
}
151 152

/**
153
 * Gets group ID given groupname
154
 *
155 156 157
 * @param groupname the groupname
 * @param gid return location for GID
 * @returns #TRUE if group name existed and we got the GID
158 159
 */
dbus_bool_t
160 161
_dbus_get_group_id (const DBusString  *groupname,
                    dbus_gid_t        *gid)
162 163
{
  DBusUserDatabase *db;
164
  const DBusGroupInfo *info;
165 166 167 168

  /* FIXME: this can't distinguish ENOMEM from other errors */
  if (!_dbus_user_database_lock_system ())
    return FALSE;
169 170 171 172 173 174 175 176

  db = _dbus_user_database_get_system ();
  if (db == NULL)
    {
      _dbus_user_database_unlock_system ();
      return FALSE;
    }

177 178
  if (!_dbus_user_database_get_groupname (db, groupname,
                                          &info, NULL))
179 180 181 182 183
    {
      _dbus_user_database_unlock_system ();
      return FALSE;
    }

184
  *gid = info->gid;
185 186 187 188 189 190
  
  _dbus_user_database_unlock_system ();
  return TRUE;
}

/**
191
 * Gets user ID and primary group given username
192 193
 *
 * @param username the username
194 195 196
 * @param uid_p return location for UID
 * @param gid_p return location for GID
 * @returns #TRUE if username existed and we got the UID and GID
197 198
 */
dbus_bool_t
199 200 201
_dbus_get_user_id_and_primary_group (const DBusString  *username,
                                     dbus_uid_t        *uid_p,
                                     dbus_gid_t        *gid_p)
202 203
{
  DBusUserDatabase *db;
204
  const DBusUserInfo *info;
205 206 207 208

  /* FIXME: this can't distinguish ENOMEM from other errors */
  if (!_dbus_user_database_lock_system ())
    return FALSE;
209 210 211 212 213 214 215 216

  db = _dbus_user_database_get_system ();
  if (db == NULL)
    {
      _dbus_user_database_unlock_system ();
      return FALSE;
    }

217 218
  if (!_dbus_user_database_get_username (db, username,
                                         &info, NULL))
219 220 221 222 223
    {
      _dbus_user_database_unlock_system ();
      return FALSE;
    }

224 225 226 227
  if (uid_p)
    *uid_p = info->uid;
  if (gid_p)
    *gid_p = info->primary_gid;
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
  
  _dbus_user_database_unlock_system ();
  return TRUE;
}

/**
 * Looks up a gid or group name in the user database.  Only one of
 * name or GID can be provided. There are wrapper functions for this
 * that are better to use, this one does no locking or anything on the
 * database and otherwise sort of sucks.
 *
 * @param db the database
 * @param gid the group ID or #DBUS_GID_UNSET
 * @param groupname group name or #NULL 
 * @param error error to fill in
 * @returns the entry in the database
 */
DBusGroupInfo*
_dbus_user_database_lookup_group (DBusUserDatabase *db,
                                  dbus_gid_t        gid,
                                  const DBusString *groupname,
                                  DBusError        *error)
{
  DBusGroupInfo *info;

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

255 256 257 258 259 260 261 262 263
   /* See if the group is really a number */
   if (gid == DBUS_UID_UNSET)
    {
      unsigned long n;

      if (_dbus_is_a_number (groupname, &n))
        gid = n;
    }

264
  if (gid != DBUS_GID_UNSET)
265
    info = _dbus_hash_table_lookup_uintptr (db->groups, gid);
266 267 268 269 270 271
  else
    info = _dbus_hash_table_lookup_string (db->groups_by_name,
                                           _dbus_string_get_const_data (groupname));
  if (info)
    {
      _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
272
                     info->gid);
273 274 275 276
      return info;
    }
  else
    {
277 278 279 280 281 282
      if (gid != DBUS_GID_UNSET)
	_dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
		       gid);
      else
	_dbus_verbose ("No cache for groupname \"%s\"\n",
		       _dbus_string_get_const_data (groupname));
283 284 285 286 287 288 289 290
      
      info = dbus_new0 (DBusGroupInfo, 1);
      if (info == NULL)
        {
          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
          return NULL;
        }

291
      if (gid != DBUS_GID_UNSET)
292
        {
293 294 295 296 297 298
          if (!_dbus_group_info_fill_gid (info, gid, error))
            {
              _DBUS_ASSERT_ERROR_IS_SET (error);
              _dbus_group_info_free_allocated (info);
              return NULL;
            }
299
        }
300 301 302 303 304 305 306 307 308 309 310 311 312
      else
        {
          if (!_dbus_group_info_fill (info, groupname, error))
            {
              _DBUS_ASSERT_ERROR_IS_SET (error);
              _dbus_group_info_free_allocated (info);
              return NULL;
            }
        }

      /* don't use these past here */
      gid = DBUS_GID_UNSET;
      groupname = NULL;
313

314
      if (!_dbus_hash_table_insert_uintptr (db->groups, info->gid, info))
315 316 317 318 319 320 321 322 323 324 325
        {
          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
          _dbus_group_info_free_allocated (info);
          return NULL;
        }


      if (!_dbus_hash_table_insert_string (db->groups_by_name,
                                           info->groupname,
                                           info))
        {
326
          _dbus_hash_table_remove_uintptr (db->groups, info->gid);
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
          return NULL;
        }
      
      return info;
    }
}


/**
 * Gets the user information for the given group name,
 * returned group info should not be freed. 
 *
 * @param db user database
 * @param groupname the group name
 * @param info return location for const ref to group info
 * @param error error location
 * @returns #FALSE if error is set
 */
dbus_bool_t
_dbus_user_database_get_groupname (DBusUserDatabase     *db,
                                   const DBusString     *groupname,
                                   const DBusGroupInfo **info,
                                   DBusError            *error)
{
  *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
  return *info != NULL;
}

/**
 * Gets the user information for the given GID,
 * returned group info should not be freed. 
 *
 * @param db user database
 * @param gid the group ID
 * @param info return location for const ref to group info
 * @param error error location
 * @returns #FALSE if error is set
 */
dbus_bool_t
_dbus_user_database_get_gid (DBusUserDatabase     *db,
                             dbus_gid_t            gid,
                             const DBusGroupInfo **info,
                             DBusError            *error)
{
  *info = _dbus_user_database_lookup_group (db, gid, NULL, error);
  return *info != NULL;
}


/**
Tim Dijkstra's avatar
Tim Dijkstra committed
378
 * Gets all groups  corresponding to the given UID. Returns #FALSE
379
 * if no memory, or user isn't known, but always initializes
Tim Dijkstra's avatar
Tim Dijkstra committed
380
 * group_ids to a NULL array. 
381
 *
Tim Dijkstra's avatar
Tim Dijkstra committed
382
 * @param uid the UID
383 384
 * @param group_ids return location for array of group IDs
 * @param n_group_ids return location for length of returned array
Tim Dijkstra's avatar
Tim Dijkstra committed
385
 * @returns #TRUE if the UID existed and we got some credentials
386 387
 */
dbus_bool_t
Tim Dijkstra's avatar
Tim Dijkstra committed
388 389 390
_dbus_groups_from_uid (dbus_uid_t         uid,
                       dbus_gid_t       **group_ids,
                       int               *n_group_ids)
391
{
Tim Dijkstra's avatar
Tim Dijkstra committed
392 393
  DBusUserDatabase *db;
  const DBusUserInfo *info;
394 395
  *group_ids = NULL;
  *n_group_ids = 0;
Tim Dijkstra's avatar
Tim Dijkstra committed
396

397 398 399
  /* FIXME: this can't distinguish ENOMEM from other errors */
  if (!_dbus_user_database_lock_system ())
    return FALSE;
Tim Dijkstra's avatar
Tim Dijkstra committed
400 401 402

  db = _dbus_user_database_get_system ();
  if (db == NULL)
403
    {
Tim Dijkstra's avatar
Tim Dijkstra committed
404
      _dbus_user_database_unlock_system ();
405 406 407
      return FALSE;
    }

Tim Dijkstra's avatar
Tim Dijkstra committed
408 409 410 411 412 413 414 415 416
  if (!_dbus_user_database_get_uid (db, uid,
                                    &info, NULL))
    {
      _dbus_user_database_unlock_system ();
      return FALSE;
    }

  _dbus_assert (info->uid == uid);
  
417 418 419 420 421
  if (info->n_group_ids > 0)
    {
      *group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
      if (*group_ids == NULL)
        {
Tim Dijkstra's avatar
Tim Dijkstra committed
422
	  _dbus_user_database_unlock_system ();
423 424 425 426 427 428 429 430
          return FALSE;
        }

      *n_group_ids = info->n_group_ids;

      memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
    }

Tim Dijkstra's avatar
Tim Dijkstra committed
431
  _dbus_user_database_unlock_system ();
432 433 434 435
  return TRUE;
}
/** @} */

436
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
437 438 439 440 441 442 443 444 445 446 447 448
#include <stdio.h>

/**
 * Unit test for dbus-userdb.c.
 * 
 * @returns #TRUE on success.
 */
dbus_bool_t
_dbus_userdb_test (const char *test_data_dir)
{
  const DBusString *username;
  const DBusString *homedir;
Tim Dijkstra's avatar
Tim Dijkstra committed
449 450 451
  dbus_uid_t uid;
  unsigned long *group_ids;
  int n_group_ids, i;
452
  DBusError error;
453

454
  if (!_dbus_username_from_current_process (&username))
455 456 457 458 459
    _dbus_assert_not_reached ("didn't get username");

  if (!_dbus_homedir_from_current_process (&homedir))
    _dbus_assert_not_reached ("didn't get homedir");  

Tim Dijkstra's avatar
Tim Dijkstra committed
460 461 462 463 464 465 466
  if (!_dbus_get_user_id (username, &uid))
    _dbus_assert_not_reached ("didn't get uid");

  if (!_dbus_groups_from_uid (uid, &group_ids, &n_group_ids))
    _dbus_assert_not_reached ("didn't get groups");

  printf ("    Current user: %s homedir: %s gids:",
467 468
          _dbus_string_get_const_data (username),
          _dbus_string_get_const_data (homedir));
Tim Dijkstra's avatar
Tim Dijkstra committed
469 470 471 472 473

  for (i=0; i<n_group_ids; i++)
      printf(" %ld", group_ids[i]);

  printf ("\n");
474 475 476 477 478 479 480 481 482 483 484

  dbus_error_init (&error);
  printf ("Is Console user: %i\n",
          _dbus_is_console_user (uid, &error));
  printf ("Invocation was OK: %s\n", error.message ? error.message : "yes");
  dbus_error_free (&error);
  printf ("Is Console user 4711: %i\n",
          _dbus_is_console_user (4711, &error));
  printf ("Invocation was OK: %s\n", error.message ? error.message : "yes");
  dbus_error_free (&error);

Tim Dijkstra's avatar
Tim Dijkstra committed
485 486
  dbus_free (group_ids);

487 488
  return TRUE;
}
489
#endif /* DBUS_ENABLE_EMBEDDED_TESTS */