"Fossies" - the Fresh Open Source Software Archive

Member "dbus-1.13.18/dbus/dbus-userdb.c" (1 Jul 2020, 18671 Bytes) of package /linux/misc/dbus-1.13.18.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "dbus-userdb.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.13.16_vs_1.13.18.

    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 
   33 /* It isn't obvious from its name, but this file is part of the Unix
   34  * system-dependent part of libdbus. Windows has a parallel
   35  * implementation of some of it in dbus-sysdeps-win.c. */
   36 #if defined(DBUS_WIN) || !defined(DBUS_UNIX)
   37 #error "This file only makes sense on Unix OSs"
   38 #endif
   39 
   40 /**
   41  * @addtogroup DBusInternalsUtils
   42  * @{
   43  */
   44 
   45 static DBusUserInfo *
   46 _dbus_user_info_ref (DBusUserInfo *info)
   47 {
   48   _dbus_assert (info->refcount > 0);
   49   _dbus_assert (info->refcount < SIZE_MAX);
   50   info->refcount++;
   51   return info;
   52 }
   53 
   54 /**
   55  * Decrements the reference count. If it reaches 0,
   56  * frees the given #DBusUserInfo's members with _dbus_user_info_free()
   57  * and also calls dbus_free() on the block itself
   58  *
   59  * @param info the info
   60  */
   61 void
   62 _dbus_user_info_unref (DBusUserInfo *info)
   63 {
   64   if (info == NULL) /* hash table will pass NULL */
   65     return;
   66 
   67   _dbus_assert (info->refcount > 0);
   68   _dbus_assert (info->refcount < SIZE_MAX);
   69 
   70   if (--info->refcount > 0)
   71     return;
   72 
   73   _dbus_user_info_free (info);
   74   dbus_free (info);
   75 }
   76 
   77 /**
   78  * Decrements the reference count. If it reaches 0,
   79  * frees the given #DBusGroupInfo's members with _dbus_group_info_free()
   80  * and also calls dbus_free() on the block itself
   81  *
   82  * @param info the info
   83  */
   84 void
   85 _dbus_group_info_unref (DBusGroupInfo *info)
   86 {
   87   if (info == NULL) /* hash table will pass NULL */
   88     return;
   89 
   90   _dbus_assert (info->refcount > 0);
   91   _dbus_assert (info->refcount < SIZE_MAX);
   92 
   93   if (--info->refcount > 0)
   94     return;
   95 
   96   _dbus_group_info_free (info);
   97   dbus_free (info);
   98 }
   99 
  100 /**
  101  * Frees the members of info
  102  * (but not info itself)
  103  * @param info the user info struct
  104  */
  105 void
  106 _dbus_user_info_free (DBusUserInfo *info)
  107 {
  108   dbus_free (info->group_ids);
  109   dbus_free (info->username);
  110   dbus_free (info->homedir);
  111 }
  112 
  113 /**
  114  * Frees the members of info (but not info itself).
  115  *
  116  * @param info the group info
  117  */
  118 void
  119 _dbus_group_info_free (DBusGroupInfo    *info)
  120 {
  121   dbus_free (info->groupname);
  122 }
  123 
  124 /**
  125  * Checks if a given string is actually a number 
  126  * and converts it if it is 
  127  *
  128  * @param str the string to check
  129  * @param num the memory location of the unsigned long to fill in
  130  * @returns TRUE if str is a number and num is filled in 
  131  */
  132 dbus_bool_t
  133 _dbus_is_a_number (const DBusString *str,
  134                    unsigned long    *num)
  135 {
  136   int end;
  137 
  138   if (_dbus_string_parse_uint (str, 0, num, &end) &&
  139       end == _dbus_string_get_length (str))
  140     return TRUE;
  141   else
  142     return FALSE;
  143 }
  144 
  145 /**
  146  * Looks up a uid or username in the user database.  Only one of name
  147  * or UID can be provided. There are wrapper functions for this that
  148  * are better to use, this one does no locking or anything on the
  149  * database and otherwise sort of sucks.
  150  *
  151  * @param db the database
  152  * @param uid the user ID or #DBUS_UID_UNSET
  153  * @param username username or #NULL 
  154  * @param error error to fill in
  155  * @returns the entry in the database (borrowed, do not free)
  156  */
  157 const DBusUserInfo *
  158 _dbus_user_database_lookup (DBusUserDatabase *db,
  159                             dbus_uid_t        uid,
  160                             const DBusString *username,
  161                             DBusError        *error)
  162 {
  163   DBusUserInfo *info;
  164 
  165   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
  166   _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
  167 
  168   /* See if the username is really a number */
  169   if (uid == DBUS_UID_UNSET)
  170     {
  171       unsigned long n;
  172 
  173       if (_dbus_is_a_number (username, &n))
  174         uid = n;
  175     }
  176 
  177   if (uid != DBUS_UID_UNSET)
  178     info = _dbus_hash_table_lookup_uintptr (db->users, uid);
  179   else
  180     info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
  181 
  182   if (info)
  183     {
  184       _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
  185                      info->uid);
  186       return info;
  187     }
  188   else
  189     {
  190       if (uid != DBUS_UID_UNSET)
  191     _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
  192                uid);
  193       else
  194     _dbus_verbose ("No cache for user \"%s\"\n",
  195                _dbus_string_get_const_data (username));
  196       
  197       info = dbus_new0 (DBusUserInfo, 1);
  198       if (info == NULL)
  199         {
  200           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
  201           return NULL;
  202         }
  203       info->refcount = 1;
  204 
  205       if (uid != DBUS_UID_UNSET)
  206         {
  207           if (!_dbus_user_info_fill_uid (info, uid, error))
  208             {
  209               _DBUS_ASSERT_ERROR_IS_SET (error);
  210               _dbus_user_info_unref (info);
  211               return NULL;
  212             }
  213         }
  214       else
  215         {
  216           if (!_dbus_user_info_fill (info, username, error))
  217             {
  218               _DBUS_ASSERT_ERROR_IS_SET (error);
  219               _dbus_user_info_unref (info);
  220               return NULL;
  221             }
  222         }
  223 
  224       /* be sure we don't use these after here */
  225       uid = DBUS_UID_UNSET;
  226       username = NULL;
  227 
  228       /* insert into hash */
  229       if (_dbus_hash_table_insert_uintptr (db->users, info->uid, info))
  230         {
  231           _dbus_user_info_ref (info);
  232         }
  233       else
  234         {
  235           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
  236           _dbus_user_info_unref (info);
  237           return NULL;
  238         }
  239 
  240       if (_dbus_hash_table_insert_string (db->users_by_name,
  241                                           info->username,
  242                                           info))
  243         {
  244           _dbus_user_info_ref (info);
  245         }
  246       else
  247         {
  248           _dbus_hash_table_remove_uintptr (db->users, info->uid);
  249           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
  250           _dbus_user_info_unref (info);
  251           return NULL;
  252         }
  253 
  254       _dbus_user_info_unref (info);
  255 
  256       /* Return a borrowed pointer to the DBusUserInfo owned by the
  257        * hash tables */
  258       return info;
  259     }
  260 }
  261 
  262 /* Protected by _DBUS_LOCK_system_users */
  263 static dbus_bool_t database_locked = FALSE;
  264 static DBusUserDatabase *system_db = NULL;
  265 static DBusString process_username;
  266 static DBusString process_homedir;
  267       
  268 static void
  269 shutdown_system_db (void *data)
  270 {
  271   if (system_db != NULL)
  272     _dbus_user_database_unref (system_db);
  273   system_db = NULL;
  274   _dbus_string_free (&process_username);
  275   _dbus_string_free (&process_homedir);
  276 }
  277 
  278 static dbus_bool_t
  279 init_system_db (void)
  280 {
  281   _dbus_assert (database_locked);
  282     
  283   if (system_db == NULL)
  284     {
  285       DBusError error = DBUS_ERROR_INIT;
  286       const DBusUserInfo *info;
  287       
  288       system_db = _dbus_user_database_new ();
  289       if (system_db == NULL)
  290         return FALSE;
  291 
  292       if (!_dbus_user_database_get_uid (system_db,
  293                                         _dbus_getuid (),
  294                                         &info,
  295                                         &error))
  296         {
  297           _dbus_user_database_unref (system_db);
  298           system_db = NULL;
  299           
  300           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
  301             {
  302               dbus_error_free (&error);
  303               return FALSE;
  304             }
  305           else
  306             {
  307               /* This really should not happen. */
  308               _dbus_warn ("Could not get password database information for UID of current process: %s",
  309                           error.message);
  310               dbus_error_free (&error);
  311               return FALSE;
  312             }
  313         }
  314 
  315       if (!_dbus_string_init (&process_username))
  316         {
  317           _dbus_user_database_unref (system_db);
  318           system_db = NULL;
  319           return FALSE;
  320         }
  321 
  322       if (!_dbus_string_init (&process_homedir))
  323         {
  324           _dbus_string_free (&process_username);
  325           _dbus_user_database_unref (system_db);
  326           system_db = NULL;
  327           return FALSE;
  328         }
  329 
  330       if (!_dbus_string_append (&process_username,
  331                                 info->username) ||
  332           !_dbus_string_append (&process_homedir,
  333                                 info->homedir) ||
  334           !_dbus_register_shutdown_func (shutdown_system_db, NULL))
  335         {
  336           _dbus_string_free (&process_username);
  337           _dbus_string_free (&process_homedir);
  338           _dbus_user_database_unref (system_db);
  339           system_db = NULL;
  340           return FALSE;
  341         }
  342     }
  343 
  344   return TRUE;
  345 }
  346 
  347 /**
  348  * Locks global system user database.
  349  */
  350 dbus_bool_t
  351 _dbus_user_database_lock_system (void)
  352 {
  353   if (_DBUS_LOCK (system_users))
  354     {
  355       database_locked = TRUE;
  356       return TRUE;
  357     }
  358   else
  359     {
  360       return FALSE;
  361     }
  362 }
  363 
  364 /**
  365  * Unlocks global system user database.
  366  */
  367 void
  368 _dbus_user_database_unlock_system (void)
  369 {
  370   database_locked = FALSE;
  371   _DBUS_UNLOCK (system_users);
  372 }
  373 
  374 /**
  375  * Gets the system global user database;
  376  * must be called with lock held (_dbus_user_database_lock_system()).
  377  *
  378  * @returns the database or #NULL if no memory
  379  */
  380 DBusUserDatabase*
  381 _dbus_user_database_get_system (void)
  382 {
  383   _dbus_assert (database_locked);
  384 
  385   init_system_db ();
  386   
  387   return system_db;
  388 }
  389 
  390 /**
  391  * Flushes the system global user database;
  392  */
  393 void
  394 _dbus_user_database_flush_system (void)
  395 {
  396   if (!_dbus_user_database_lock_system ())
  397     {
  398       /* nothing to flush */
  399       return;
  400     }
  401 
  402    if (system_db != NULL)
  403     _dbus_user_database_flush (system_db);
  404 
  405   _dbus_user_database_unlock_system ();
  406 }
  407 
  408 /**
  409  * Gets username of user owning current process.  The returned string
  410  * is valid until dbus_shutdown() is called.
  411  *
  412  * @param username place to store pointer to username
  413  * @returns #FALSE if no memory
  414  */
  415 dbus_bool_t
  416 _dbus_username_from_current_process (const DBusString **username)
  417 {
  418   if (!_dbus_user_database_lock_system ())
  419     return FALSE;
  420 
  421   if (!init_system_db ())
  422     {
  423       _dbus_user_database_unlock_system ();
  424       return FALSE;
  425     }
  426   *username = &process_username;
  427   _dbus_user_database_unlock_system ();  
  428 
  429   return TRUE;
  430 }
  431 
  432 /**
  433  * Gets homedir of user owning current process.  The returned string
  434  * is valid until dbus_shutdown() is called.
  435  *
  436  * @param homedir place to store pointer to homedir
  437  * @returns #FALSE if no memory
  438  */
  439 dbus_bool_t
  440 _dbus_homedir_from_current_process (const DBusString  **homedir)
  441 {
  442   if (!_dbus_user_database_lock_system ())
  443     return FALSE;
  444 
  445   if (!init_system_db ())
  446     {
  447       _dbus_user_database_unlock_system ();
  448       return FALSE;
  449     }
  450   *homedir = &process_homedir;
  451   _dbus_user_database_unlock_system ();
  452 
  453   return TRUE;
  454 }
  455 
  456 /**
  457  * Gets the home directory for the given user.
  458  *
  459  * @param uid the uid
  460  * @param homedir string to append home directory to
  461  * @returns #TRUE if user existed and we appended their homedir
  462  */
  463 dbus_bool_t
  464 _dbus_homedir_from_uid (dbus_uid_t         uid,
  465                         DBusString        *homedir)
  466 {
  467   DBusUserDatabase *db;
  468   const DBusUserInfo *info;
  469 
  470   if (uid == _dbus_getuid () && uid == _dbus_geteuid ())
  471     {
  472       const char *from_environment;
  473 
  474       from_environment = _dbus_getenv ("HOME");
  475 
  476       if (from_environment != NULL)
  477         return _dbus_string_append (homedir, from_environment);
  478     }
  479 
  480   /* FIXME: this can't distinguish ENOMEM from other errors */
  481   if (!_dbus_user_database_lock_system ())
  482     return FALSE;
  483 
  484   db = _dbus_user_database_get_system ();
  485   if (db == NULL)
  486     {
  487       _dbus_user_database_unlock_system ();
  488       return FALSE;
  489     }
  490 
  491   if (!_dbus_user_database_get_uid (db, uid,
  492                                     &info, NULL))
  493     {
  494       _dbus_user_database_unlock_system ();
  495       return FALSE;
  496     }
  497 
  498   if (!_dbus_string_append (homedir, info->homedir))
  499     {
  500       _dbus_user_database_unlock_system ();
  501       return FALSE;
  502     }
  503   
  504   _dbus_user_database_unlock_system ();
  505   return TRUE;
  506 }
  507 
  508 /**
  509  * Adds the credentials corresponding to the given username.
  510  *
  511  * Used among other purposes to parses a desired identity provided
  512  * from a client in the auth protocol. On UNIX this means parsing a
  513  * UID, on Windows probably parsing an SID string.
  514  * 
  515  * @todo this is broken because it treats OOM and parse error
  516  * the same way. Needs a #DBusError.
  517  * 
  518  * @param credentials credentials to fill in 
  519  * @param username the username
  520  * @returns #TRUE if the username existed and we got some credentials
  521  */
  522 dbus_bool_t
  523 _dbus_credentials_add_from_user (DBusCredentials         *credentials,
  524                                  const DBusString        *username,
  525                                  DBusCredentialsAddFlags  flags,
  526                                  DBusError               *error)
  527 {
  528   DBusUserDatabase *db;
  529   const DBusUserInfo *info;
  530   unsigned long uid = DBUS_UID_UNSET;
  531 
  532   /* Fast-path for the common case: if the "username" is all-numeric,
  533    * then it's a Unix uid. This is true regardless of whether that uid
  534    * exists in NSS or /etc/passwd or equivalent. */
  535   if (_dbus_is_a_number (username, &uid))
  536     {
  537       _DBUS_STATIC_ASSERT (sizeof (uid) == sizeof (dbus_uid_t));
  538 
  539       if (_dbus_credentials_add_unix_uid (credentials, uid))
  540         {
  541           return TRUE;
  542         }
  543       else
  544         {
  545           _DBUS_SET_OOM (error);
  546           return FALSE;
  547         }
  548     }
  549 
  550   /* If we aren't allowed to look in NSS or /etc/passwd, fail now. */
  551   if (!(flags & DBUS_CREDENTIALS_ADD_FLAGS_USER_DATABASE))
  552     {
  553       dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
  554                       "Expected a numeric Unix uid");
  555       return FALSE;
  556     }
  557 
  558   if (!_dbus_user_database_lock_system ())
  559     {
  560       _DBUS_SET_OOM (error);
  561       return FALSE;
  562     }
  563 
  564   db = _dbus_user_database_get_system ();
  565   if (db == NULL)
  566     {
  567       _dbus_user_database_unlock_system ();
  568       _DBUS_SET_OOM (error);
  569       return FALSE;
  570     }
  571 
  572   if (!_dbus_user_database_get_username (db, username,
  573                                          &info, error))
  574     {
  575       _dbus_user_database_unlock_system ();
  576       return FALSE;
  577     }
  578 
  579   if (!_dbus_credentials_add_unix_uid(credentials, info->uid))
  580     {
  581       _dbus_user_database_unlock_system ();
  582       _DBUS_SET_OOM (error);
  583       return FALSE;
  584     }
  585   
  586   _dbus_user_database_unlock_system ();
  587   return TRUE;
  588 }
  589 
  590 /**
  591  * Creates a new user database object used to look up and
  592  * cache user information.
  593  * @returns new database, or #NULL on out of memory
  594  */
  595 DBusUserDatabase*
  596 _dbus_user_database_new (void)
  597 {
  598   DBusUserDatabase *db;
  599   
  600   db = dbus_new0 (DBusUserDatabase, 1);
  601   if (db == NULL)
  602     return NULL;
  603 
  604   db->refcount = 1;
  605 
  606   db->users = _dbus_hash_table_new (DBUS_HASH_UINTPTR,
  607                                     NULL, (DBusFreeFunction) _dbus_user_info_unref);
  608   
  609   if (db->users == NULL)
  610     goto failed;
  611 
  612   db->groups = _dbus_hash_table_new (DBUS_HASH_UINTPTR,
  613                                      NULL, (DBusFreeFunction) _dbus_group_info_unref);
  614   
  615   if (db->groups == NULL)
  616     goto failed;
  617 
  618   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
  619                                             NULL, (DBusFreeFunction) _dbus_user_info_unref);
  620   if (db->users_by_name == NULL)
  621     goto failed;
  622   
  623   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
  624                                              NULL, (DBusFreeFunction) _dbus_group_info_unref);
  625   if (db->groups_by_name == NULL)
  626     goto failed;
  627   
  628   return db;
  629   
  630  failed:
  631   _dbus_user_database_unref (db);
  632   return NULL;
  633 }
  634 
  635 /**
  636  * Flush all information out of the user database. 
  637  */
  638 void
  639 _dbus_user_database_flush (DBusUserDatabase *db) 
  640 {
  641   _dbus_hash_table_remove_all(db->users_by_name);
  642   _dbus_hash_table_remove_all(db->groups_by_name);
  643   _dbus_hash_table_remove_all(db->users);
  644   _dbus_hash_table_remove_all(db->groups);
  645 }
  646 
  647 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
  648 /**
  649  * Increments refcount of user database.
  650  * @param db the database
  651  * @returns the database
  652  */
  653 DBusUserDatabase *
  654 _dbus_user_database_ref (DBusUserDatabase  *db)
  655 {
  656   _dbus_assert (db->refcount > 0);
  657 
  658   db->refcount += 1;
  659 
  660   return db;
  661 }
  662 #endif /* DBUS_ENABLE_EMBEDDED_TESTS */
  663 
  664 /**
  665  * Decrements refcount of user database.
  666  * @param db the database
  667  */
  668 void
  669 _dbus_user_database_unref (DBusUserDatabase  *db)
  670 {
  671   _dbus_assert (db->refcount > 0);
  672 
  673   db->refcount -= 1;
  674   if (db->refcount == 0)
  675     {
  676       if (db->users)
  677         _dbus_hash_table_unref (db->users);
  678 
  679       if (db->groups)
  680         _dbus_hash_table_unref (db->groups);
  681 
  682       if (db->users_by_name)
  683         _dbus_hash_table_unref (db->users_by_name);
  684 
  685       if (db->groups_by_name)
  686         _dbus_hash_table_unref (db->groups_by_name);
  687       
  688       dbus_free (db);
  689     }
  690 }
  691 
  692 /**
  693  * Gets the user information for the given UID,
  694  * returned user info should not be freed. 
  695  *
  696  * @param db user database
  697  * @param uid the user ID
  698  * @param info return location for const ref to user info
  699  * @param error error location
  700  * @returns #FALSE if error is set
  701  */
  702 dbus_bool_t
  703 _dbus_user_database_get_uid (DBusUserDatabase    *db,
  704                              dbus_uid_t           uid,
  705                              const DBusUserInfo **info,
  706                              DBusError           *error)
  707 {
  708   *info = _dbus_user_database_lookup (db, uid, NULL, error);
  709   return *info != NULL;
  710 }
  711 
  712 /**
  713  * Gets the user information for the given username.
  714  *
  715  * @param db user database
  716  * @param username the user name
  717  * @param info return location for const ref to user info
  718  * @param error error location
  719  * @returns #FALSE if error is set
  720  */
  721 dbus_bool_t
  722 _dbus_user_database_get_username  (DBusUserDatabase     *db,
  723                                    const DBusString     *username,
  724                                    const DBusUserInfo  **info,
  725                                    DBusError            *error)
  726 {
  727   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
  728   return *info != NULL;
  729 }
  730 
  731 /** @} */
  732 
  733 /* Tests in dbus-userdb-util.c */