2006-10-21 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-userdb-util.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
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   info = _dbus_user_database_lookup (db, uid, NULL, error);
90
91   if (info == NULL)
92     {
93       _dbus_user_database_unlock_system ();
94        return FALSE;
95     }
96
97   result = _dbus_user_at_console (info->username, error);
98
99   _dbus_user_database_unlock_system ();
100
101   return result;
102 }
103
104
105 /**
106  * Gets the credentials corresponding to the given UID.
107  *
108  * @param uid the UID
109  * @param credentials credentials to fill in
110  * @returns #TRUE if the UID existed and we got some credentials
111  */
112 dbus_bool_t
113 _dbus_credentials_from_uid (dbus_uid_t        uid,
114                             DBusCredentials  *credentials)
115 {
116   DBusUserDatabase *db;
117   const DBusUserInfo *info;
118   _dbus_user_database_lock_system ();
119
120   db = _dbus_user_database_get_system ();
121   if (db == NULL)
122     {
123       _dbus_user_database_unlock_system ();
124       return FALSE;
125     }
126
127   if (!_dbus_user_database_get_uid (db, uid,
128                                     &info, NULL))
129     {
130       _dbus_user_database_unlock_system ();
131       return FALSE;
132     }
133
134   _dbus_assert (info->uid == uid);
135   
136   credentials->pid = DBUS_PID_UNSET;
137   credentials->uid = info->uid;
138   credentials->gid = info->primary_gid;
139   
140   _dbus_user_database_unlock_system ();
141   return TRUE;
142 }
143
144
145 /**
146  * Gets user ID given username
147  *
148  * @param username the username
149  * @param uid return location for UID
150  * @returns #TRUE if username existed and we got the UID
151  */
152 dbus_bool_t
153 _dbus_get_user_id (const DBusString  *username,
154                    dbus_uid_t        *uid)
155 {
156   DBusCredentials creds;
157
158   if (!_dbus_credentials_from_username (username, &creds))
159     return FALSE;
160
161   if (creds.uid == DBUS_UID_UNSET)
162     return FALSE;
163
164   *uid = creds.uid;
165
166   return TRUE;
167 }
168
169 /**
170  * Gets group ID given groupname
171  *
172  * @param groupname the groupname
173  * @param gid return location for GID
174  * @returns #TRUE if group name existed and we got the GID
175  */
176 dbus_bool_t
177 _dbus_get_group_id (const DBusString  *groupname,
178                     dbus_gid_t        *gid)
179 {
180   DBusUserDatabase *db;
181   const DBusGroupInfo *info;
182   _dbus_user_database_lock_system ();
183
184   db = _dbus_user_database_get_system ();
185   if (db == NULL)
186     {
187       _dbus_user_database_unlock_system ();
188       return FALSE;
189     }
190
191   if (!_dbus_user_database_get_groupname (db, groupname,
192                                           &info, NULL))
193     {
194       _dbus_user_database_unlock_system ();
195       return FALSE;
196     }
197
198   *gid = info->gid;
199   
200   _dbus_user_database_unlock_system ();
201   return TRUE;
202 }
203
204 /**
205  * Looks up a gid or group name in the user database.  Only one of
206  * name or GID can be provided. There are wrapper functions for this
207  * that are better to use, this one does no locking or anything on the
208  * database and otherwise sort of sucks.
209  *
210  * @param db the database
211  * @param gid the group ID or #DBUS_GID_UNSET
212  * @param groupname group name or #NULL 
213  * @param error error to fill in
214  * @returns the entry in the database
215  */
216 DBusGroupInfo*
217 _dbus_user_database_lookup_group (DBusUserDatabase *db,
218                                   dbus_gid_t        gid,
219                                   const DBusString *groupname,
220                                   DBusError        *error)
221 {
222   DBusGroupInfo *info;
223
224   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
225
226    /* See if the group is really a number */
227    if (gid == DBUS_UID_UNSET)
228     {
229       unsigned long n;
230
231       if (_dbus_is_a_number (groupname, &n))
232         gid = n;
233     }
234
235
236   if (gid != DBUS_GID_UNSET)
237     info = _dbus_hash_table_lookup_ulong (db->groups, gid);
238   else
239     info = _dbus_hash_table_lookup_string (db->groups_by_name,
240                                            _dbus_string_get_const_data (groupname));
241   if (info)
242     {
243       _dbus_verbose ("Using cache for GID "DBUS_GID_FORMAT" information\n",
244                      info->gid);
245       return info;
246     }
247   else
248     {
249       if (gid != DBUS_GID_UNSET)
250         _dbus_verbose ("No cache for GID "DBUS_GID_FORMAT"\n",
251                        gid);
252       else
253         _dbus_verbose ("No cache for groupname \"%s\"\n",
254                        _dbus_string_get_const_data (groupname));
255       
256       info = dbus_new0 (DBusGroupInfo, 1);
257       if (info == NULL)
258         {
259           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
260           return NULL;
261         }
262
263       if (gid != DBUS_GID_UNSET)
264         {
265           if (!_dbus_group_info_fill_gid (info, gid, error))
266             {
267               _DBUS_ASSERT_ERROR_IS_SET (error);
268               _dbus_group_info_free_allocated (info);
269               return NULL;
270             }
271         }
272       else
273         {
274           if (!_dbus_group_info_fill (info, groupname, error))
275             {
276               _DBUS_ASSERT_ERROR_IS_SET (error);
277               _dbus_group_info_free_allocated (info);
278               return NULL;
279             }
280         }
281
282       /* don't use these past here */
283       gid = DBUS_GID_UNSET;
284       groupname = NULL;
285
286       if (!_dbus_hash_table_insert_ulong (db->groups, info->gid, info))
287         {
288           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
289           _dbus_group_info_free_allocated (info);
290           return NULL;
291         }
292
293
294       if (!_dbus_hash_table_insert_string (db->groups_by_name,
295                                            info->groupname,
296                                            info))
297         {
298           _dbus_hash_table_remove_ulong (db->groups, info->gid);
299           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
300           return NULL;
301         }
302       
303       return info;
304     }
305 }
306
307
308 /**
309  * Gets the user information for the given group name,
310  * returned group info should not be freed. 
311  *
312  * @param db user database
313  * @param groupname the group name
314  * @param info return location for const ref to group info
315  * @param error error location
316  * @returns #FALSE if error is set
317  */
318 dbus_bool_t
319 _dbus_user_database_get_groupname (DBusUserDatabase     *db,
320                                    const DBusString     *groupname,
321                                    const DBusGroupInfo **info,
322                                    DBusError            *error)
323 {
324   *info = _dbus_user_database_lookup_group (db, DBUS_GID_UNSET, groupname, error);
325   return *info != NULL;
326 }
327
328 /**
329  * Gets the user information for the given GID,
330  * returned group info should not be freed. 
331  *
332  * @param db user database
333  * @param gid the group ID
334  * @param info return location for const ref to group info
335  * @param error error location
336  * @returns #FALSE if error is set
337  */
338 dbus_bool_t
339 _dbus_user_database_get_gid (DBusUserDatabase     *db,
340                              dbus_gid_t            gid,
341                              const DBusGroupInfo **info,
342                              DBusError            *error)
343 {
344   *info = _dbus_user_database_lookup_group (db, gid, NULL, error);
345   return *info != NULL;
346 }
347
348
349 /**
350  * Gets all groups for a particular user. Returns #FALSE
351  * if no memory, or user isn't known, but always initializes
352  * group_ids to a NULL array. Sets error to the reason
353  * for returning #FALSE.
354  *
355  * @param db the user database object
356  * @param uid the user ID
357  * @param group_ids return location for array of group IDs
358  * @param n_group_ids return location for length of returned array
359  * @param error return location for error
360  * @returns #TRUE on success
361  */
362 dbus_bool_t
363 _dbus_user_database_get_groups (DBusUserDatabase  *db,
364                                 dbus_uid_t         uid,
365                                 dbus_gid_t       **group_ids,
366                                 int               *n_group_ids,
367                                 DBusError         *error)
368 {
369   DBusUserInfo *info;
370   
371   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
372
373   *group_ids = NULL;
374   *n_group_ids = 0;
375   
376   info = _dbus_user_database_lookup (db, uid, NULL, error);
377   if (info == NULL)
378     {
379       _DBUS_ASSERT_ERROR_IS_SET (error);
380       return FALSE;
381     }
382
383   if (info->n_group_ids > 0)
384     {
385       *group_ids = dbus_new (dbus_gid_t, info->n_group_ids);
386       if (*group_ids == NULL)
387         {
388           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
389           return FALSE;
390         }
391
392       *n_group_ids = info->n_group_ids;
393
394       memcpy (*group_ids, info->group_ids, info->n_group_ids * sizeof (dbus_gid_t));
395     }
396
397   return TRUE;
398 }
399
400 /** @} */
401
402 #ifdef DBUS_BUILD_TESTS
403 #include <stdio.h>
404
405 /**
406  * Unit test for dbus-userdb.c.
407  * 
408  * @returns #TRUE on success.
409  */
410 dbus_bool_t
411 _dbus_userdb_test (const char *test_data_dir)
412 {
413   const DBusString *username;
414   const DBusString *homedir;
415
416   if (!_dbus_username_from_current_process (&username))
417     _dbus_assert_not_reached ("didn't get username");
418
419   if (!_dbus_homedir_from_current_process (&homedir))
420     _dbus_assert_not_reached ("didn't get homedir");  
421
422   printf ("    Current user: %s homedir: %s\n",
423           _dbus_string_get_const_data (username),
424           _dbus_string_get_const_data (homedir));
425   
426   return TRUE;
427 }
428 #endif /* DBUS_BUILD_TESTS */