Main Page | Modules | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

dbus-userdb.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
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 #include "dbus-userdb.h"
00024 #include "dbus-hash.h"
00025 #include "dbus-test.h"
00026 #include "dbus-internals.h"
00027 #include "dbus-protocol.h"
00028 #include <string.h>
00029 
00033 struct DBusUserDatabase
00034 {
00035   int refcount; 
00037   DBusHashTable *users; 
00038   DBusHashTable *groups; 
00039   DBusHashTable *users_by_name; 
00040   DBusHashTable *groups_by_name; 
00042 };
00043 
00044 static void
00045 free_user_info (void *data)
00046 {
00047   DBusUserInfo *info = data;
00048 
00049   if (info == NULL) /* hash table will pass NULL */
00050     return;
00051 
00052   _dbus_user_info_free (info);
00053   dbus_free (info);
00054 }
00055 
00056 static void
00057 free_group_info (void *data)
00058 {
00059   DBusGroupInfo *info = data;
00060 
00061   if (info == NULL) /* hash table will pass NULL */
00062     return;
00063 
00064   _dbus_group_info_free (info);
00065   dbus_free (info);
00066 }
00067 
00068 static DBusUserInfo*
00069 _dbus_user_database_lookup (DBusUserDatabase *db,
00070                             dbus_uid_t        uid,
00071                             const DBusString *username,
00072                             DBusError        *error)
00073 {
00074   DBusUserInfo *info;
00075 
00076   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00077   _dbus_assert (uid != DBUS_UID_UNSET || username != NULL);
00078   
00079   if (uid != DBUS_UID_UNSET)
00080     info = _dbus_hash_table_lookup_ulong (db->users, uid);
00081   else
00082     info = _dbus_hash_table_lookup_string (db->users_by_name, _dbus_string_get_const_data (username));
00083   
00084   if (info)
00085     {
00086       _dbus_verbose ("Using cache for UID "DBUS_UID_FORMAT" information\n",
00087                      uid);
00088       return info;
00089     }
00090   else
00091     {
00092       _dbus_verbose ("No cache for UID "DBUS_UID_FORMAT"\n",
00093                      uid);
00094       
00095       info = dbus_new0 (DBusUserInfo, 1);
00096       if (info == NULL)
00097         {
00098           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00099           return NULL;
00100         }
00101 
00102       if (uid != DBUS_UID_UNSET)
00103         {
00104           if (!_dbus_user_info_fill_uid (info, uid, error))
00105             {
00106               _DBUS_ASSERT_ERROR_IS_SET (error);
00107               free_user_info (info);
00108               return NULL;
00109             }
00110         }
00111       else
00112         {
00113           if (!_dbus_user_info_fill (info, username, error))
00114             {
00115               _DBUS_ASSERT_ERROR_IS_SET (error);
00116               free_user_info (info);
00117               return NULL;
00118             }
00119         }
00120 
00121       /* be sure we don't use these after here */
00122       uid = DBUS_UID_UNSET;
00123       username = NULL;
00124 
00125       /* insert into hash */
00126       if (!_dbus_hash_table_insert_ulong (db->users, info->uid, info))
00127         {
00128           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00129           free_user_info (info);
00130           return NULL;
00131         }
00132 
00133       if (!_dbus_hash_table_insert_string (db->users_by_name,
00134                                            info->username,
00135                                            info))
00136         {
00137           _dbus_hash_table_remove_ulong (db->users, info->uid);
00138           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00139           return NULL;
00140         }
00141       
00142       return info;
00143     }
00144 }
00145 
00146 static DBusGroupInfo*
00147 _dbus_user_database_lookup_group (DBusUserDatabase *db,
00148                                   dbus_gid_t        gid,
00149                                   const DBusString *groupname,
00150                                   DBusError        *error)
00151 {
00152   DBusGroupInfo *info;
00153 
00154   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00155 
00156   if (gid != DBUS_GID_UNSET)
00157     info = _dbus_hash_table_lookup_ulong (db->groups, gid);
00158   else
00159     info = _dbus_hash_table_lookup_string (db->groups_by_name,
00160                                            _dbus_string_get_const_data (groupname));
00161   if (info)
00162     {
00163       _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
00164                      gid);
00165       return info;
00166     }
00167   else
00168     {
00169       _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
00170                      gid);
00171       
00172       info = dbus_new0 (DBusGroupInfo, 1);
00173       if (info == NULL)
00174         {
00175           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00176           return NULL;
00177         }
00178 
00179       if (!_dbus_group_info_fill_gid (info, gid, error))
00180         {
00181           _DBUS_ASSERT_ERROR_IS_SET (error);
00182           free_group_info (info);
00183           return NULL;
00184         }
00185 
00186       if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info))
00187         {
00188           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00189           free_group_info (info);
00190           return NULL;
00191         }
00192 
00193 
00194       if (!_dbus_hash_table_insert_string (db->groups_by_name,
00195                                            info->groupname,
00196                                            info))
00197         {
00198           _dbus_hash_table_remove_ulong (db->groups, info->gid);
00199           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00200           return NULL;
00201         }
00202       
00203       return info;
00204     }
00205 }
00206 
00207 _DBUS_DEFINE_GLOBAL_LOCK(system_users);
00208 static dbus_bool_t database_locked = FALSE;
00209 static DBusUserDatabase *system_db = NULL;
00210 static DBusString process_username;
00211 static DBusString process_homedir;
00212       
00213 static void
00214 shutdown_system_db (void *data)
00215 {
00216   _dbus_user_database_unref (system_db);
00217   system_db = NULL;
00218   _dbus_string_free (&process_username);
00219   _dbus_string_free (&process_homedir);
00220 }
00221 
00222 static dbus_bool_t
00223 init_system_db (void)
00224 {
00225   _dbus_assert (database_locked);
00226     
00227   if (system_db == NULL)
00228     {
00229       DBusError error;
00230       const DBusUserInfo *info;
00231       
00232       system_db = _dbus_user_database_new ();
00233       if (system_db == NULL)
00234         return FALSE;
00235 
00236       dbus_error_init (&error);
00237 
00238       if (!_dbus_user_database_get_uid (system_db,
00239                                         _dbus_getuid (),
00240                                         &info,
00241                                         &error))
00242         {
00243           _dbus_user_database_unref (system_db);
00244           system_db = NULL;
00245           
00246           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
00247             {
00248               dbus_error_free (&error);
00249               return FALSE;
00250             }
00251           else
00252             {
00253               /* This really should not happen. */
00254               _dbus_warn ("Could not get password database information for UID of current process: %s\n",
00255                           error.message);
00256               dbus_error_free (&error);
00257               return FALSE;
00258             }
00259         }
00260 
00261       if (!_dbus_string_init (&process_username))
00262         {
00263           _dbus_user_database_unref (system_db);
00264           system_db = NULL;
00265           return FALSE;
00266         }
00267 
00268       if (!_dbus_string_init (&process_homedir))
00269         {
00270           _dbus_string_free (&process_username);
00271           _dbus_user_database_unref (system_db);
00272           system_db = NULL;
00273           return FALSE;
00274         }
00275 
00276       if (!_dbus_string_append (&process_username,
00277                                 info->username) ||
00278           !_dbus_string_append (&process_homedir,
00279                                 info->homedir) ||
00280           !_dbus_register_shutdown_func (shutdown_system_db, NULL))
00281         {
00282           _dbus_string_free (&process_username);
00283           _dbus_string_free (&process_homedir);
00284           _dbus_user_database_unref (system_db);
00285           system_db = NULL;
00286           return FALSE;
00287         }
00288     }
00289 
00290   return TRUE;
00291 }
00292 
00301 void
00302 _dbus_user_database_lock_system (void)
00303 {
00304   _DBUS_LOCK (system_users);
00305   database_locked = TRUE;
00306 }
00307 
00311 void
00312 _dbus_user_database_unlock_system (void)
00313 {
00314   database_locked = FALSE;
00315   _DBUS_UNLOCK (system_users);
00316 }
00317 
00324 DBusUserDatabase*
00325 _dbus_user_database_get_system (void)
00326 {
00327   _dbus_assert (database_locked);
00328 
00329   init_system_db ();
00330   
00331   return system_db;
00332 }
00333 
00341 dbus_bool_t
00342 _dbus_username_from_current_process (const DBusString **username)
00343 {
00344   _dbus_user_database_lock_system ();
00345   if (!init_system_db ())
00346     {
00347       _dbus_user_database_unlock_system ();
00348       return FALSE;
00349     }
00350   *username = &process_username;
00351   _dbus_user_database_unlock_system ();  
00352 
00353   return TRUE;
00354 }
00355 
00363 dbus_bool_t
00364 _dbus_homedir_from_current_process (const DBusString  **homedir)
00365 {
00366   _dbus_user_database_lock_system ();
00367   if (!init_system_db ())
00368     {
00369       _dbus_user_database_unlock_system ();
00370       return FALSE;
00371     }
00372   *homedir = &process_homedir;
00373   _dbus_user_database_unlock_system ();
00374 
00375   return TRUE;
00376 }
00377 
00385 dbus_bool_t
00386 _dbus_get_user_id (const DBusString  *username,
00387                    dbus_uid_t        *uid)
00388 {
00389   DBusCredentials creds;
00390 
00391   if (!_dbus_credentials_from_username (username, &creds))
00392     return FALSE;
00393 
00394   if (creds.uid == DBUS_UID_UNSET)
00395     return FALSE;
00396 
00397   *uid = creds.uid;
00398 
00399   return TRUE;
00400 }
00401 
00409 dbus_bool_t
00410 _dbus_is_console_user (dbus_uid_t uid,
00411                        DBusError *error)
00412 {
00413 
00414   DBusUserDatabase *db;
00415   const DBusUserInfo *info;
00416   dbus_bool_t result = FALSE; 
00417 
00418   _dbus_user_database_lock_system ();
00419 
00420   db = _dbus_user_database_get_system ();
00421   if (db == NULL)
00422     {
00423       dbus_set_error (error, DBUS_ERROR_FAILED, "Could not get system database.");
00424       _dbus_user_database_unlock_system ();
00425       return FALSE;
00426     }
00427 
00428   info = _dbus_user_database_lookup (db, uid, NULL, error);
00429 
00430   if (info == NULL)
00431     {
00432       _dbus_user_database_unlock_system ();
00433        return FALSE;
00434     }
00435 
00436   result = _dbus_user_at_console (info->username, error);
00437 
00438   _dbus_user_database_unlock_system ();
00439 
00440   return result;
00441 }
00442 
00450 dbus_bool_t
00451 _dbus_get_group_id (const DBusString  *groupname,
00452                     dbus_gid_t        *gid)
00453 {
00454   DBusUserDatabase *db;
00455   const DBusGroupInfo *info;
00456   _dbus_user_database_lock_system ();
00457 
00458   db = _dbus_user_database_get_system ();
00459   if (db == NULL)
00460     {
00461       _dbus_user_database_unlock_system ();
00462       return FALSE;
00463     }
00464 
00465   if (!_dbus_user_database_get_groupname (db, groupname,
00466                                           &info, NULL))
00467     {
00468       _dbus_user_database_unlock_system ();
00469       return FALSE;
00470     }
00471 
00472   *gid = info->gid;
00473   
00474   _dbus_user_database_unlock_system ();
00475   return TRUE;
00476 }
00477 
00485 dbus_bool_t
00486 _dbus_homedir_from_username (const DBusString *username,
00487                              DBusString       *homedir)
00488 {
00489   DBusUserDatabase *db;
00490   const DBusUserInfo *info;
00491   _dbus_user_database_lock_system ();
00492 
00493   db = _dbus_user_database_get_system ();
00494   if (db == NULL)
00495     {
00496       _dbus_user_database_unlock_system ();
00497       return FALSE;
00498     }
00499 
00500   if (!_dbus_user_database_get_username (db, username,
00501                                          &info, NULL))
00502     {
00503       _dbus_user_database_unlock_system ();
00504       return FALSE;
00505     }
00506 
00507   if (!_dbus_string_append (homedir, info->homedir))
00508     {
00509       _dbus_user_database_unlock_system ();
00510       return FALSE;
00511     }
00512   
00513   _dbus_user_database_unlock_system ();
00514   return TRUE;
00515 }
00516 
00524 dbus_bool_t
00525 _dbus_uid_from_string (const DBusString      *uid_str,
00526                        dbus_uid_t            *uid)
00527 {
00528   int end;
00529   long val;
00530   
00531   if (_dbus_string_get_length (uid_str) == 0)
00532     {
00533       _dbus_verbose ("UID string was zero length\n");
00534       return FALSE;
00535     }
00536 
00537   val = -1;
00538   end = 0;
00539   if (!_dbus_string_parse_int (uid_str, 0, &val,
00540                                &end))
00541     {
00542       _dbus_verbose ("could not parse string as a UID\n");
00543       return FALSE;
00544     }
00545   
00546   if (end != _dbus_string_get_length (uid_str))
00547     {
00548       _dbus_verbose ("string contained trailing stuff after UID\n");
00549       return FALSE;
00550     }
00551 
00552   *uid = val;
00553 
00554   return TRUE;
00555 }
00556 
00564 dbus_bool_t
00565 _dbus_credentials_from_username (const DBusString *username,
00566                                  DBusCredentials  *credentials)
00567 {
00568   DBusUserDatabase *db;
00569   const DBusUserInfo *info;
00570   _dbus_user_database_lock_system ();
00571 
00572   db = _dbus_user_database_get_system ();
00573   if (db == NULL)
00574     {
00575       _dbus_user_database_unlock_system ();
00576       return FALSE;
00577     }
00578 
00579   if (!_dbus_user_database_get_username (db, username,
00580                                          &info, NULL))
00581     {
00582       _dbus_user_database_unlock_system ();
00583       return FALSE;
00584     }
00585 
00586   credentials->pid = DBUS_PID_UNSET;
00587   credentials->uid = info->uid;
00588   credentials->gid = info->primary_gid;
00589   
00590   _dbus_user_database_unlock_system ();
00591   return TRUE;
00592 }
00593 
00601 dbus_bool_t
00602 _dbus_credentials_from_uid (dbus_uid_t        uid,
00603                             DBusCredentials  *credentials)
00604 {
00605   DBusUserDatabase *db;
00606   const DBusUserInfo *info;
00607   _dbus_user_database_lock_system ();
00608 
00609   db = _dbus_user_database_get_system ();
00610   if (db == NULL)
00611     {
00612       _dbus_user_database_unlock_system ();
00613       return FALSE;
00614     }
00615 
00616   if (!_dbus_user_database_get_uid (db, uid,
00617                                     &info, NULL))
00618     {
00619       _dbus_user_database_unlock_system ();
00620       return FALSE;
00621     }
00622 
00623   _dbus_assert (info->uid == uid);
00624   
00625   credentials->pid = DBUS_PID_UNSET;
00626   credentials->uid = info->uid;
00627   credentials->gid = info->primary_gid;
00628   
00629   _dbus_user_database_unlock_system ();
00630   return TRUE;
00631 }
00632 
00638 DBusUserDatabase*
00639 _dbus_user_database_new (void)
00640 {
00641   DBusUserDatabase *db;
00642   
00643   db = dbus_new0 (DBusUserDatabase, 1);
00644   if (db == NULL)
00645     return NULL;
00646 
00647   db->refcount = 1;
00648 
00649   db->users = _dbus_hash_table_new (DBUS_HASH_ULONG,
00650                                     NULL, free_user_info);
00651   
00652   if (db->users == NULL)
00653     goto failed;
00654 
00655   db->groups = _dbus_hash_table_new (DBUS_HASH_ULONG,
00656                                      NULL, free_group_info);
00657   
00658   if (db->groups == NULL)
00659     goto failed;
00660 
00661   db->users_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00662                                             NULL, NULL);
00663   if (db->users_by_name == NULL)
00664     goto failed;
00665   
00666   db->groups_by_name = _dbus_hash_table_new (DBUS_HASH_STRING,
00667                                              NULL, NULL);
00668   if (db->groups_by_name == NULL)
00669     goto failed;
00670   
00671   return db;
00672   
00673  failed:
00674   _dbus_user_database_unref (db);
00675   return NULL;
00676 }
00677 
00683 DBusUserDatabase *
00684 _dbus_user_database_ref (DBusUserDatabase  *db)
00685 {
00686   _dbus_assert (db->refcount > 0);
00687 
00688   db->refcount += 1;
00689 
00690   return db;
00691 }
00692 
00697 void
00698 _dbus_user_database_unref (DBusUserDatabase  *db)
00699 {
00700   _dbus_assert (db->refcount > 0);
00701 
00702   db->refcount -= 1;
00703   if (db->refcount == 0)
00704     {
00705       if (db->users)
00706         _dbus_hash_table_unref (db->users);
00707 
00708       if (db->groups)
00709         _dbus_hash_table_unref (db->groups);
00710 
00711       if (db->users_by_name)
00712         _dbus_hash_table_unref (db->users_by_name);
00713 
00714       if (db->groups_by_name)
00715         _dbus_hash_table_unref (db->groups_by_name);
00716       
00717       dbus_free (db);
00718     }
00719 }
00720 
00734 dbus_bool_t
00735 _dbus_user_database_get_groups (DBusUserDatabase  *db,
00736                                 dbus_uid_t         uid,
00737                                 dbus_gid_t       **group_ids,
00738                                 int               *n_group_ids,
00739                                 DBusError         *error)
00740 {
00741   DBusUserInfo *info;
00742   
00743   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00744 
00745   *group_ids = NULL;
00746   *n_group_ids = 0;
00747   
00748   info = _dbus_user_database_lookup (db, uid, NULL, error);
00749   if (info == NULL)
00750     {
00751       _DBUS_ASSERT_ERROR_IS_SET (error);
00752       return FALSE;
00753     }
00754 
00755   if (info->n_group_ids > 0)
00756     {
00757       *group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
00758       if (*group_ids == NULL)
00759         {
00760           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00761           return FALSE;
00762         }
00763 
00764       *n_group_ids = info->n_group_ids;
00765 
00766       memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
00767     }
00768 
00769   return TRUE;
00770 }
00771 
00782 dbus_bool_t
00783 _dbus_user_database_get_uid (DBusUserDatabase    *db,
00784                              dbus_uid_t           uid,
00785                              const DBusUserInfo **info,
00786                              DBusError           *error)
00787 {
00788   *info = _dbus_user_database_lookup (db, uid, NULL, error);
00789   return *info != NULL;
00790 }
00791 
00802 dbus_bool_t
00803 _dbus_user_database_get_gid (DBusUserDatabase     *db,
00804                              dbus_gid_t            gid,
00805                              const DBusGroupInfo **info,
00806                              DBusError            *error)
00807 {
00808   *info = _dbus_user_database_lookup_group (db, gid, NULL, error);
00809   return *info != NULL;
00810 }
00811 
00821 dbus_bool_t
00822 _dbus_user_database_get_username  (DBusUserDatabase     *db,
00823                                    const DBusString     *username,
00824                                    const DBusUserInfo  **info,
00825                                    DBusError            *error)
00826 {
00827   *info = _dbus_user_database_lookup (db, DBUS_UID_UNSET, username, error);
00828   return *info != NULL;
00829 }
00830 
00841 dbus_bool_t
00842 _dbus_user_database_get_groupname (DBusUserDatabase     *db,
00843                                    const DBusString     *groupname,
00844                                    const DBusGroupInfo **info,
00845                                    DBusError            *error)
00846 {
00847   *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
00848   return *info != NULL;
00849 }
00850 
00853 #ifdef DBUS_BUILD_TESTS
00854 #include <stdio.h>
00855 
00861 dbus_bool_t
00862 _dbus_userdb_test (const char *test_data_dir)
00863 {
00864   const DBusString *username;
00865   const DBusString *homedir;
00866 
00867   if (!_dbus_username_from_current_process (&username))
00868     _dbus_assert_not_reached ("didn't get username");
00869 
00870   if (!_dbus_homedir_from_current_process (&homedir))
00871     _dbus_assert_not_reached ("didn't get homedir");  
00872 
00873   printf ("    Current user: %s homedir: %s\n",
00874           _dbus_string_get_const_data (username),
00875           _dbus_string_get_const_data (homedir));
00876   
00877   return TRUE;
00878 }
00879 #endif /* DBUS_BUILD_TESTS */

Generated on Tue Mar 22 23:25:51 2005 for D-BUS by  doxygen 1.4.1