dbus-userdb.c

00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 /* dbus-userdb.c User database abstraction
00003  * 
00004  * Copyright (C) 2003, 2004  Red Hat, Inc.
00005  *
00006  * Licensed under the Academic Free License version 2.1
00007  * 
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  * 
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 #define DBUS_USERDB_INCLUDES_PRIVATE 1
00024 #include "dbus-userdb.h"
00025 #include "dbus-hash.h"
00026 #include "dbus-test.h"
00027 #include "dbus-internals.h"
00028 #include "dbus-protocol.h"
00029 #include "dbus-credentials.h"
00030 #include <string.h>
00031 
00043 void
00044 _dbus_user_info_free_allocated (DBusUserInfo *info)
00045 {
00046   if (info == NULL) /* hash table will pass NULL */
00047     return;
00048 
00049   _dbus_user_info_free (info);
00050   dbus_free (info);
00051 }
00052 
00059 void
00060 _dbus_group_info_free_allocated (DBusGroupInfo *info)
00061 {
00062   if (info == NULL) /* hash table will pass NULL */
00063     return;
00064 
00065   _dbus_group_info_free (info);
00066   dbus_free (info);
00067 }
00068 
00074 void
00075 _dbus_user_info_free (DBusUserInfo *info)
00076 {
00077   dbus_free (info->group_ids);
00078   dbus_free (info->username);
00079   dbus_free (info->homedir);
00080 }
00081 
00087 void
00088 _dbus_group_info_free (DBusGroupInfo    *info)
00089 {
00090   dbus_free (info->groupname);
00091 }
00092 
00101 dbus_bool_t
00102 _dbus_is_a_number (const DBusString *str,
00103                    unsigned long    *num)
00104 {
00105   int end;
00106 
00107   if (_dbus_string_parse_uint (str, 0, num, &end) &&
00108       end == _dbus_string_get_length (str))
00109     return TRUE;
00110   else
00111     return FALSE;
00112 }
00113 
00126 DBusUserInfo*
00127 _dbus_user_database_lookup (DBusUserDatabase *db,
00128                             dbus_uid_t        uid,
00129                             const DBusString *username,
00130                             DBusError        *error)
00131 {
00132   DBusUserInfo *info;
00133 
00134   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00135   _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
00136 
00137   /* See if the username is really a number */
00138   if (uid == DBUS_UID_UNSET)
00139     {
00140       unsigned long n;
00141 
00142       if (_dbus_is_a_number (username, &n))
00143         uid = n;
00144     }
00145 
00146 #ifdef DBUS_ENABLE_USERDB_CACHE  
00147   if (uid != DBUS_UID_UNSET)
00148     info = _dbus_hash_table_lookup_ulong (db->users, uid);
00149   else
00150     info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
00151 
00152   if (info)
00153     {
00154       _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
00155                      info->uid);
00156       return info;
00157     }
00158   else
00159 #else 
00160   if (1)
00161 #endif
00162     {
00163       if (uid != DBUS_UID_UNSET)
00164         _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
00165                        uid);
00166       else
00167         _dbus_verbose ("No cache for user \"%s\"\n",
00168                        _dbus_string_get_const_data (username));
00169       
00170       info = dbus_new0 (DBusUserInfo, 1);
00171       if (info == NULL)
00172         {
00173           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00174           return NULL;
00175         }
00176 
00177       if (uid != DBUS_UID_UNSET)
00178         {
00179           if (!_dbus_user_info_fill_uid (info, uid, error))
00180             {
00181               _DBUS_ASSERT_ERROR_IS_SET (error);
00182               _dbus_user_info_free_allocated (info);
00183               return NULL;
00184             }
00185         }
00186       else
00187         {
00188           if (!_dbus_user_info_fill (info, username, error))
00189             {
00190               _DBUS_ASSERT_ERROR_IS_SET (error);
00191               _dbus_user_info_free_allocated (info);
00192               return NULL;
00193             }
00194         }
00195 
00196       /* be sure we don't use these after here */
00197       uid = DBUS_UID_UNSET;
00198       username = NULL;
00199 
00200       /* insert into hash */
00201       if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
00202         {
00203           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00204           _dbus_user_info_free_allocated (info);
00205           return NULL;
00206         }
00207 
00208       if (!_dbus_hash_table_insert_string (db->users_by_name,
00209                                            info->username,
00210                                            info))
00211         {
00212           _dbus_hash_table_remove_ulong (db->users, info->uid);
00213           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00214           return NULL;
00215         }
00216       
00217       return info;
00218     }
00219 }
00220 
00221 static dbus_bool_t database_locked = FALSE;
00222 static DBusUserDatabase *system_db = NULL;
00223 static DBusString process_username;
00224 static DBusString process_homedir;
00225       
00226 static void
00227 shutdown_system_db (void *data)
00228 {
00229   _dbus_user_database_unref (system_db);
00230   system_db = NULL;
00231   _dbus_string_free (&process_username);
00232   _dbus_string_free (&process_homedir);
00233 }
00234 
00235 static dbus_bool_t
00236 init_system_db (void)
00237 {
00238   _dbus_assert (database_locked);
00239     
00240   if (system_db == NULL)
00241     {
00242       DBusError error = DBUS_ERROR_INIT;
00243       const DBusUserInfo *info;
00244       
00245       system_db = _dbus_user_database_new ();
00246       if (system_db == NULL)
00247         return FALSE;
00248 
00249       if (!_dbus_user_database_get_uid (system_db,
00250                                         _dbus_getuid (),
00251                                         &info,
00252                                         &error))
00253         {
00254           _dbus_user_database_unref (system_db);
00255           system_db = NULL;
00256           
00257           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
00258             {
00259               dbus_error_free (&error);
00260               return FALSE;
00261             }
00262           else
00263             {
00264               /* This really should not happen. */
00265               _dbus_warn ("Could not get password database information for UID of current process: %s\n",
00266                           error.message);
00267               dbus_error_free (&error);
00268               return FALSE;
00269             }
00270         }
00271 
00272       if (!_dbus_string_init (&process_username))
00273         {
00274           _dbus_user_database_unref (system_db);
00275           system_db = NULL;
00276           return FALSE;
00277         }
00278 
00279       if (!_dbus_string_init (&process_homedir))
00280         {
00281           _dbus_string_free (&process_username);
00282           _dbus_user_database_unref (system_db);
00283           system_db = NULL;
00284           return FALSE;
00285         }
00286 
00287       if (!_dbus_string_append (&process_username,
00288                                 info->username) ||
00289           !_dbus_string_append (&process_homedir,
00290                                 info->homedir) ||
00291           !_dbus_register_shutdown_func (shutdown_system_db, NULL))
00292         {
00293           _dbus_string_free (&process_username);
00294           _dbus_string_free (&process_homedir);
00295           _dbus_user_database_unref (system_db);
00296           system_db = NULL;
00297           return FALSE;
00298         }
00299     }
00300 
00301   return TRUE;
00302 }
00303 
00307 void
00308 _dbus_user_database_lock_system (void)
00309 {
00310   _DBUS_LOCK (system_users);
00311   database_locked = TRUE;
00312 }
00313 
00317 void
00318 _dbus_user_database_unlock_system (void)
00319 {
00320   database_locked = FALSE;
00321   _DBUS_UNLOCK (system_users);
00322 }
00323 
00330 DBusUserDatabase*
00331 _dbus_user_database_get_system (void)
00332 {
00333   _dbus_assert (database_locked);
00334 
00335   init_system_db ();
00336   
00337   return system_db;
00338 }
00339 
00343 void
00344 _dbus_user_database_flush_system (void)
00345 {
00346   _dbus_user_database_lock_system ();
00347    
00348   _dbus_user_database_flush (system_db);
00349 
00350   _dbus_user_database_unlock_system ();
00351 }
00352 
00360 dbus_bool_t
00361 _dbus_username_from_current_process (const DBusString **username)
00362 {
00363   _dbus_user_database_lock_system ();
00364   if (!init_system_db ())
00365     {
00366       _dbus_user_database_unlock_system ();
00367       return FALSE;
00368     }
00369   *username = &process_username;
00370   _dbus_user_database_unlock_system ();  
00371 
00372   return TRUE;
00373 }
00374 
00382 dbus_bool_t
00383 _dbus_homedir_from_current_process (const DBusString  **homedir)
00384 {
00385   _dbus_user_database_lock_system ();
00386   if (!init_system_db ())
00387     {
00388       _dbus_user_database_unlock_system ();
00389       return FALSE;
00390     }
00391   *homedir = &process_homedir;
00392   _dbus_user_database_unlock_system ();
00393 
00394   return TRUE;
00395 }
00396 
00404 dbus_bool_t
00405 _dbus_homedir_from_username (const DBusString *username,
00406                              DBusString       *homedir)
00407 {
00408   DBusUserDatabase *db;
00409   const DBusUserInfo *info;
00410   _dbus_user_database_lock_system ();
00411 
00412   db = _dbus_user_database_get_system ();
00413   if (db == NULL)
00414     {
00415       _dbus_user_database_unlock_system ();
00416       return FALSE;
00417     }
00418 
00419   if (!_dbus_user_database_get_username (db, username,
00420                                          &info, NULL))
00421     {
00422       _dbus_user_database_unlock_system ();
00423       return FALSE;
00424     }
00425 
00426   if (!_dbus_string_append (homedir, info->homedir))
00427     {
00428       _dbus_user_database_unlock_system ();
00429       return FALSE;
00430     }
00431   
00432   _dbus_user_database_unlock_system ();
00433   return TRUE;
00434 }
00435 
00443 dbus_bool_t
00444 _dbus_homedir_from_uid (dbus_uid_t         uid,
00445                         DBusString        *homedir)
00446 {
00447   DBusUserDatabase *db;
00448   const DBusUserInfo *info;
00449   _dbus_user_database_lock_system ();
00450 
00451   db = _dbus_user_database_get_system ();
00452   if (db == NULL)
00453     {
00454       _dbus_user_database_unlock_system ();
00455       return FALSE;
00456     }
00457 
00458   if (!_dbus_user_database_get_uid (db, uid,
00459                                     &info, NULL))
00460     {
00461       _dbus_user_database_unlock_system ();
00462       return FALSE;
00463     }
00464 
00465   if (!_dbus_string_append (homedir, info->homedir))
00466     {
00467       _dbus_user_database_unlock_system ();
00468       return FALSE;
00469     }
00470   
00471   _dbus_user_database_unlock_system ();
00472   return TRUE;
00473 }
00474 
00489 dbus_bool_t
00490 _dbus_credentials_add_from_user (DBusCredentials  *credentials,
00491                                  const DBusString *username)
00492 {
00493   DBusUserDatabase *db;
00494   const DBusUserInfo *info;
00495 
00496   _dbus_user_database_lock_system ();
00497 
00498   db = _dbus_user_database_get_system ();
00499   if (db == NULL)
00500     {
00501       _dbus_user_database_unlock_system ();
00502       return FALSE;
00503     }
00504 
00505   if (!_dbus_user_database_get_username (db, username,
00506                                          &info, NULL))
00507     {
00508       _dbus_user_database_unlock_system ();
00509       return FALSE;
00510     }
00511 
00512   if (!_dbus_credentials_add_unix_uid(credentials, info->uid))
00513     {
00514       _dbus_user_database_unlock_system ();
00515       return FALSE;
00516     }
00517   
00518   _dbus_user_database_unlock_system ();
00519   return TRUE;
00520 }
00521 
00527 DBusUserDatabase*
00528 _dbus_user_database_new (void)
00529 {
00530   DBusUserDatabase *db;
00531   
00532   db = dbus_new0 (DBusUserDatabase, 1);
00533   if (db == NULL)
00534     return NULL;
00535 
00536   db->refcount = 1;
00537 
00538   db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
00539                                     NULL, (DBusFreeFunction) _dbus_user_info_free_allocated);
00540   
00541   if (db->users == NULL)
00542     goto failed;
00543 
00544   db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
00545                                      NULL, (DBusFreeFunction) _dbus_group_info_free_allocated);
00546   
00547   if (db->groups == NULL)
00548     goto failed;
00549 
00550   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00551                                             NULL, NULL);
00552   if (db->users_by_name == NULL)
00553     goto failed;
00554   
00555   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00556                                              NULL, NULL);
00557   if (db->groups_by_name == NULL)
00558     goto failed;
00559   
00560   return db;
00561   
00562  failed:
00563   _dbus_user_database_unref (db);
00564   return NULL;
00565 }
00566 
00570 void
00571 _dbus_user_database_flush (DBusUserDatabase *db) 
00572 {
00573   _dbus_hash_table_remove_all(db->users_by_name);
00574   _dbus_hash_table_remove_all(db->groups_by_name);
00575   _dbus_hash_table_remove_all(db->users);
00576   _dbus_hash_table_remove_all(db->groups);
00577 }
00578 
00579 #ifdef DBUS_BUILD_TESTS
00580 
00585 DBusUserDatabase *
00586 _dbus_user_database_ref (DBusUserDatabase  *db)
00587 {
00588   _dbus_assert (db->refcount > 0);
00589 
00590   db->refcount += 1;
00591 
00592   return db;
00593 }
00594 #endif /* DBUS_BUILD_TESTS */
00595 
00600 void
00601 _dbus_user_database_unref (DBusUserDatabase  *db)
00602 {
00603   _dbus_assert (db->refcount > 0);
00604 
00605   db->refcount -= 1;
00606   if (db->refcount == 0)
00607     {
00608       if (db->users)
00609         _dbus_hash_table_unref (db->users);
00610 
00611       if (db->groups)
00612         _dbus_hash_table_unref (db->groups);
00613 
00614       if (db->users_by_name)
00615         _dbus_hash_table_unref (db->users_by_name);
00616 
00617       if (db->groups_by_name)
00618         _dbus_hash_table_unref (db->groups_by_name);
00619       
00620       dbus_free (db);
00621     }
00622 }
00623 
00634 dbus_bool_t
00635 _dbus_user_database_get_uid (DBusUserDatabase    *db,
00636                              dbus_uid_t           uid,
00637                              const DBusUserInfo **info,
00638                              DBusError           *error)
00639 {
00640   *info = _dbus_user_database_lookup (db, uid, NULL, error);
00641   return *info != NULL;
00642 }
00643 
00653 dbus_bool_t
00654 _dbus_user_database_get_username  (DBusUserDatabase     *db,
00655                                    const DBusString     *username,
00656                                    const DBusUserInfo  **info,
00657                                    DBusError            *error)
00658 {
00659   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
00660   return *info != NULL;
00661 }
00662 
00665 /* Tests in dbus-userdb-util.c */

Generated on Thu Jan 22 16:37:06 2009 for D-Bus by  doxygen 1.5.1