Merge branch 'master' of drop.maemo.org:/git/monky
[monky] / src / 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #define DBUS_USERDB_INCLUDES_PRIVATE 1
24 #include "dbus-userdb.h"
25 #include "dbus-test.h"
26 #include "dbus-internals.h"
27 #include "dbus-protocol.h"
28 #include <string.h>
29
30 /**
31  * @addtogroup DBusInternalsUtils
32  * @{
33  */
34
35 /**
36  * Checks to see if the UID sent in is the console user
37  *
38  * @param uid UID of person to check 
39  * @param error return location for errors
40  * @returns #TRUE if the UID is the same as the console user and there are no errors
41  */
42 dbus_bool_t
43 _dbus_is_console_user (dbus_uid_t uid,
44                        DBusError *error)
45 {
46
47   DBusUserDatabase *db;
48   const DBusUserInfo *info;
49   dbus_bool_t result = FALSE; 
50
51 #ifdef HAVE_CONSOLE_OWNER_FILE
52
53   DBusString f;
54   DBusStat st;
55
56   if (!_dbus_string_init (&f))
57     {
58       _DBUS_SET_OOM (error);
59       return FALSE;
60     }
61
62   if (!_dbus_string_append(&f, DBUS_CONSOLE_OWNER_FILE))
63     {
64       _dbus_string_free(&f);
65       _DBUS_SET_OOM (error);
66       return FALSE;
67     }
68
69   if (_dbus_stat(&f, &st, NULL) && (st.uid == uid))
70     {
71       _dbus_string_free(&f);
72       return TRUE;
73     }
74
75   _dbus_string_free(&f);
76
77 #endif /* HAVE_CONSOLE_OWNER_FILE */
78
79   _dbus_user_database_lock_system ();
80
81   db = _dbus_user_database_get_system ();
82   if (db == NULL)
83     {
84       dbus_set_error (error, DBUS_ERROR_FAILED, "Could not get system database.");
85       _dbus_user_database_unlock_system ();
86       return FALSE;
87     }
88
89   /* TPTD: this should be cache-safe, we've locked the DB and
90     _dbus_user_at_console doesn't pass it on. */
91   info = _dbus_user_database_lookup (db, uid, NULL, error);
92
93   if (info == NULL)
94     {
95       _dbus_user_database_unlock_system ();
96        return FALSE;
97     }
98
99   result = _dbus_user_at_console (info->username, error);
100
101   _dbus_user_database_unlock_system ();
102
103   return result;
104 }
105
106 /**
107  * Gets user ID given username
108  *
109  * @param username the username
110  * @param uid return location for UID
111  * @returns #TRUE if username existed and we got the UID
112  */
113 dbus_bool_t
114 _dbus_get_user_id (const DBusString  *username,
115                    dbus_uid_t        *uid)
116 {
117   return _dbus_get_user_id_and_primary_group (username, uid, NULL);
118 }
119
120 /**
121  * Gets group ID given groupname
122  *
123  * @param groupname the groupname
124  * @param gid return location for GID
125  * @returns #TRUE if group name existed and we got the GID
126  */
127 dbus_bool_t
128 _dbus_get_group_id (const DBusString  *groupname,
129                     dbus_gid_t        *gid)
130 {
131   DBusUserDatabase *db;
132   const DBusGroupInfo *info;
133   _dbus_user_database_lock_system ();
134
135   db = _dbus_user_database_get_system ();
136   if (db == NULL)
137     {
138       _dbus_user_database_unlock_system ();
139       return FALSE;
140     }
141
142   if (!_dbus_user_database_get_groupname (db, groupname,
143                                           &info, NULL))
144     {
145       _dbus_user_database_unlock_system ();
146       return FALSE;
147     }
148
149   *gid = info->gid;
150   
151   _dbus_user_database_unlock_system ();
152   return TRUE;
153 }
154
155 /**
156  * Gets user ID and primary group given username
157  *
158  * @param username the username
159  * @param uid_p return location for UID
160  * @param gid_p return location for GID
161  * @returns #TRUE if username existed and we got the UID and GID
162  */
163 dbus_bool_t
164 _dbus_get_user_id_and_primary_group (const DBusString  *username,
165                                      dbus_uid_t        *uid_p,
166                                      dbus_gid_t        *gid_p)
167 {
168   DBusUserDatabase *db;
169   const DBusUserInfo *info;
170   _dbus_user_database_lock_system ();
171
172   db = _dbus_user_database_get_system ();
173   if (db == NULL)
174     {
175       _dbus_user_database_unlock_system ();
176       return FALSE;
177     }
178
179   if (!_dbus_user_database_get_username (db, username,
180                                          &info, NULL))
181     {
182       _dbus_user_database_unlock_system ();
183       return FALSE;
184     }
185
186   if (uid_p)
187     *uid_p = info->uid;
188   if (gid_p)
189     *gid_p = info->primary_gid;
190   
191   _dbus_user_database_unlock_system ();
192   return TRUE;
193 }
194
195 /**
196  * Looks up a gid or group name in the user database.  Only one of
197  * name or GID can be provided. There are wrapper functions for this
198  * that are better to use, this one does no locking or anything on the
199  * database and otherwise sort of sucks.
200  *
201  * @param db the database
202  * @param gid the group ID or #DBUS_GID_UNSET
203  * @param groupname group name or #NULL 
204  * @param error error to fill in
205  * @returns the entry in the database
206  */
207 DBusGroupInfo*
208 _dbus_user_database_lookup_group (DBusUserDatabase *db,
209                                   dbus_gid_t        gid,
210                                   const DBusString *groupname,
211                                   DBusError        *error)
212 {
213   DBusGroupInfo *info;
214
215   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
216
217    /* See if the group is really a number */
218    if (gid == DBUS_UID_UNSET)
219     {
220       unsigned long n;
221
222       if (_dbus_is_a_number (groupname, &n))
223         gid = n;
224     }
225
226 #ifdef DBUS_ENABLE_USERDB_CACHE
227   if (gid != DBUS_GID_UNSET)
228     info = _dbus_hash_table_lookup_ulong (db->groups, gid);
229   else
230     info = _dbus_hash_table_lookup_string (db->groups_by_name,
231                                            _dbus_string_get_const_data (groupname));
232   if (info)
233     {
234       _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
235                      info->gid);
236       return info;
237     }
238   else
239 #else
240   if (1)
241 #endif
242     {
243       if (gid != DBUS_GID_UNSET)
244         _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
245                        gid);
246       else
247         _dbus_verbose ("No cache for groupname \"%s\"\n",
248                        _dbus_string_get_const_data (groupname));
249       
250       info = dbus_new0 (DBusGroupInfo, 1);
251       if (info == NULL)
252         {
253           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
254           return NULL;
255         }
256
257       if (gid != DBUS_GID_UNSET)
258         {
259           if (!_dbus_group_info_fill_gid (info, gid, error))
260             {
261               _DBUS_ASSERT_ERROR_IS_SET (error);
262               _dbus_group_info_free_allocated (info);
263               return NULL;
264             }
265         }
266       else
267         {
268           if (!_dbus_group_info_fill (info, groupname, error))
269             {
270               _DBUS_ASSERT_ERROR_IS_SET (error);
271               _dbus_group_info_free_allocated (info);
272               return NULL;
273             }
274         }
275
276       /* don't use these past here */
277       gid = DBUS_GID_UNSET;
278       groupname = NULL;
279
280       if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info))
281         {
282           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
283           _dbus_group_info_free_allocated (info);
284           return NULL;
285         }
286
287
288       if (!_dbus_hash_table_insert_string (db->groups_by_name,
289                                            info->groupname,
290                                            info))
291         {
292           _dbus_hash_table_remove_ulong (db->groups, info->gid);
293           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
294           return NULL;
295         }
296       
297       return info;
298     }
299 }
300
301
302 /**
303  * Gets the user information for the given group name,
304  * returned group info should not be freed. 
305  *
306  * @param db user database
307  * @param groupname the group name
308  * @param info return location for const ref to group info
309  * @param error error location
310  * @returns #FALSE if error is set
311  */
312 dbus_bool_t
313 _dbus_user_database_get_groupname (DBusUserDatabase     *db,
314                                    const DBusString     *groupname,
315                                    const DBusGroupInfo **info,
316                                    DBusError            *error)
317 {
318   *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
319   return *info != NULL;
320 }
321
322 /**
323  * Gets the user information for the given GID,
324  * returned group info should not be freed. 
325  *
326  * @param db user database
327  * @param gid the group ID
328  * @param info return location for const ref to group info
329  * @param error error location
330  * @returns #FALSE if error is set
331  */
332 dbus_bool_t
333 _dbus_user_database_get_gid (DBusUserDatabase     *db,
334                              dbus_gid_t            gid,
335                              const DBusGroupInfo **info,
336                              DBusError            *error)
337 {
338   *info = _dbus_user_database_lookup_group (db, gid, NULL, error);
339   return *info != NULL;
340 }
341
342
343 /**
344  * Gets all groups  corresponding to the given UID. Returns #FALSE
345  * if no memory, or user isn't known, but always initializes
346  * group_ids to a NULL array. 
347  *
348  * @param uid the UID
349  * @param group_ids return location for array of group IDs
350  * @param n_group_ids return location for length of returned array
351  * @returns #TRUE if the UID existed and we got some credentials
352  */
353 dbus_bool_t
354 _dbus_groups_from_uid (dbus_uid_t         uid,
355                        dbus_gid_t       **group_ids,
356                        int               *n_group_ids)
357 {
358   DBusUserDatabase *db;
359   const DBusUserInfo *info;
360   *group_ids = NULL;
361   *n_group_ids = 0;
362
363   _dbus_user_database_lock_system ();
364
365   db = _dbus_user_database_get_system ();
366   if (db == NULL)
367     {
368       _dbus_user_database_unlock_system ();
369       return FALSE;
370     }
371
372   if (!_dbus_user_database_get_uid (db, uid,
373                                     &info, NULL))
374     {
375       _dbus_user_database_unlock_system ();
376       return FALSE;
377     }
378
379   _dbus_assert (info->uid == uid);
380   
381   if (info->n_group_ids > 0)
382     {
383       *group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
384       if (*group_ids == NULL)
385         {
386           _dbus_user_database_unlock_system ();
387           return FALSE;
388         }
389
390       *n_group_ids = info->n_group_ids;
391
392       memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
393     }
394
395   _dbus_user_database_unlock_system ();
396   return TRUE;
397 }
398 /** @} */
399
400 #ifdef DBUS_BUILD_TESTS
401 #include <stdio.h>
402
403 /**
404  * Unit test for dbus-userdb.c.
405  * 
406  * @returns #TRUE on success.
407  */
408 dbus_bool_t
409 _dbus_userdb_test (const char *test_data_dir)
410 {
411   const DBusString *username;
412   const DBusString *homedir;
413   dbus_uid_t uid;
414   unsigned long *group_ids;
415   int n_group_ids, i;
416
417   if (!_dbus_username_from_current_process (&username))
418     _dbus_assert_not_reached ("didn't get username");
419
420   if (!_dbus_homedir_from_current_process (&homedir))
421     _dbus_assert_not_reached ("didn't get homedir");  
422
423   if (!_dbus_get_user_id (username, &uid))
424     _dbus_assert_not_reached ("didn't get uid");
425
426   if (!_dbus_groups_from_uid (uid, &group_ids, &n_group_ids))
427     _dbus_assert_not_reached ("didn't get groups");
428
429   printf ("    Current user: %s homedir: %s gids:",
430           _dbus_string_get_const_data (username),
431           _dbus_string_get_const_data (homedir));
432
433   for (i=0; i<n_group_ids; i++)
434       printf(" %ld", group_ids[i]);
435
436   printf ("\n");
437  
438   dbus_free (group_ids);
439
440   return TRUE;
441 }
442 #endif /* DBUS_BUILD_TESTS */