Home | History | Annotate | Download | only in dbus
      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