Consistently include <config.h> in all C source files and never in header files.
[platform/upstream/dbus.git] / dbus / dbus-userdb-util.c
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_ulong (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_ulong (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_ulong (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 */