"Fossies" - the Fresh Open Source Software Archive

Member "dbus-1.12.20/dbus/dbus-userdb.c" (2 Jul 2020, 18372 Bytes) of package /linux/misc/dbus-1.12.20.tar.gz:


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.12.18_vs_1.12.20.

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