polkit-policy-cache.c 17.7 KB
Newer Older
David Zeuthen's avatar
David Zeuthen committed
1
2
3
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/***************************************************************************
 *
David Zeuthen's avatar
David Zeuthen committed
4
 * polkit-policy-cache.c : policy cache
David Zeuthen's avatar
David Zeuthen committed
5
6
7
 *
 * Copyright (C) 2007 David Zeuthen, <david@fubar.dk>
 *
8
9
10
11
12
13
14
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
David Zeuthen's avatar
David Zeuthen committed
15
 *
16
17
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
David Zeuthen's avatar
David Zeuthen committed
18
 *
19
20
21
22
23
24
25
26
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
David Zeuthen's avatar
David Zeuthen committed
27
28
29
30
31
32
33
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

34
35
#define _GNU_SOURCE

David Zeuthen's avatar
David Zeuthen committed
36
37
38
39
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
Jim Li's avatar
Jim Li committed
40
41
42
#if HAVE_SOLARIS
#include <sys/stat.h>
#endif
David Zeuthen's avatar
David Zeuthen committed
43
44
45
46
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <errno.h>
47
#include <syslog.h>
48
49
#include <fcntl.h>
#include <dirent.h>
David Zeuthen's avatar
David Zeuthen committed
50

David Zeuthen's avatar
David Zeuthen committed
51
52
53
#include "polkit-debug.h"
#include "polkit-policy-file.h"
#include "polkit-policy-cache.h"
54
#include "polkit-private.h"
55
#include "polkit-test.h"
David Zeuthen's avatar
David Zeuthen committed
56
57

/**
David Zeuthen's avatar
David Zeuthen committed
58
 * SECTION:polkit-policy-cache
David Zeuthen's avatar
David Zeuthen committed
59
60
 * @title: Policy Cache
 * @short_description: Holds the actions defined on the system.
David Zeuthen's avatar
David Zeuthen committed
61
62
63
64
65
66
67
68
69
70
71
 *
 * This class is used to hold all policy objects (stemming from policy
 * files) and provide look-up functions.
 **/

/**
 * PolKitPolicyCache:
 *
 * Instances of this class are used to hold all policy objects
 * (stemming from policy files) and provide look-up functions.
 **/
David Zeuthen's avatar
David Zeuthen committed
72
struct _PolKitPolicyCache
David Zeuthen's avatar
David Zeuthen committed
73
74
75
{
        int refcount;

76
        KitList *priv_entries;
David Zeuthen's avatar
David Zeuthen committed
77
78
79
};


80
81
static polkit_bool_t
_prepend_entry (PolKitPolicyFile       *policy_file,
82
83
               PolKitPolicyFileEntry  *policy_file_entry,
               void                   *user_data)
David Zeuthen's avatar
David Zeuthen committed
84
{
85
        KitList *l;
86
        PolKitPolicyCache *policy_cache = user_data;
David Zeuthen's avatar
David Zeuthen committed
87

David Zeuthen's avatar
David Zeuthen committed
88
        polkit_policy_file_entry_ref (policy_file_entry);
89
        l = kit_list_prepend (policy_cache->priv_entries, policy_file_entry);
90
91
92
93
94
95
96
97
        if (l == NULL) {
                polkit_policy_file_entry_unref (policy_file_entry);
                goto oom;
        }
        policy_cache->priv_entries = l;
        return FALSE;
oom:
        return TRUE;
David Zeuthen's avatar
David Zeuthen committed
98
99
100
}

PolKitPolicyCache *
101
_polkit_policy_cache_new (const char *dirname, polkit_bool_t load_descriptions, PolKitError **error)
David Zeuthen's avatar
David Zeuthen committed
102
{
103
104
        DIR *dir;
        struct dirent64 *d;
David Zeuthen's avatar
David Zeuthen committed
105
        PolKitPolicyCache *pc;
106
        struct stat statbuf;
David Zeuthen's avatar
David Zeuthen committed
107

108
109
        dir = NULL;

110
        pc = kit_new0 (PolKitPolicyCache, 1);
111
112
113
114
115
        if (pc == NULL) {
                polkit_error_set_error (error, POLKIT_ERROR_OUT_OF_MEMORY, "Out of memory");
                goto out;
        }

David Zeuthen's avatar
David Zeuthen committed
116
117
        pc->refcount = 1;

118
        dir = opendir (dirname);
119
        if (dir == NULL) {
120
                polkit_error_set_error (error, POLKIT_ERROR_POLICY_FILE_INVALID,
121
122
                                        "Cannot load policy files from directory %s: %m",
                                        dirname);
David Zeuthen's avatar
David Zeuthen committed
123
124
                goto out;
        }
125

126
        while ((d = readdir64 (dir)) != NULL) {
David Zeuthen's avatar
David Zeuthen committed
127
128
                char *path;
                PolKitPolicyFile *pf;
129
                PolKitError *pk_error;
130
131
132
                size_t name_len;
                char *filename;
                static const char suffix[] = ".policy";
David Zeuthen's avatar
David Zeuthen committed
133

134
135
136
137
138
139
140
141
142
143
144
145
146
147
                path = kit_strdup_printf ("%s/%s", dirname, d->d_name);
                if (path == NULL) {
                        polkit_error_set_error (error, POLKIT_ERROR_OUT_OF_MEMORY, "Out of memory");
                        goto out;
                }

                if (stat (path, &statbuf) != 0)  {
                        polkit_error_set_error (error, POLKIT_ERROR_GENERAL_ERROR, "stat()");
                        kit_free (path);
                        goto out;
                }
                
                if (!S_ISREG (statbuf.st_mode)) {
                        kit_free (path);
David Zeuthen's avatar
David Zeuthen committed
148
                        continue;
149
                }
David Zeuthen's avatar
David Zeuthen committed
150

151
152
                filename = d->d_name;
                name_len = strlen (filename);
153
154
                if (name_len < sizeof (suffix) || strcmp ((filename + name_len - sizeof (suffix) + 1), suffix) != 0) {
                        kit_free (path);
David Zeuthen's avatar
David Zeuthen committed
155
                        continue;
156
                }
David Zeuthen's avatar
David Zeuthen committed
157

158
                polkit_debug ("Loading %s", path);
159
160
                pk_error = NULL;
                pf = polkit_policy_file_new (path, load_descriptions, &pk_error);
161
                kit_free (path);
David Zeuthen's avatar
David Zeuthen committed
162
163

                if (pf == NULL) {
164
                        if (polkit_error_get_error_code (pk_error) == POLKIT_ERROR_OUT_OF_MEMORY) {
165
166
167
168
                                if (error != NULL)
                                        *error = pk_error;
                                else
                                        polkit_error_free (pk_error);
169
170
171
                                goto out;
                        }

172
173
                        kit_warning ("libpolkit: ignoring malformed policy file: %s", 
                                     polkit_error_get_error_message (pk_error));
174
175
                        polkit_error_free (pk_error);
                        continue;
David Zeuthen's avatar
David Zeuthen committed
176
177
                }

178
                /* steal entries */
179
180
181
182
183
184
                if (polkit_policy_file_entry_foreach (pf, _prepend_entry, pc)) {
                        /* OOM failure */
                        polkit_policy_file_unref (pf);
                        polkit_error_set_error (error, POLKIT_ERROR_OUT_OF_MEMORY, "Out of memory");
                        goto out;
                }
David Zeuthen's avatar
David Zeuthen committed
185
                polkit_policy_file_unref (pf);
David Zeuthen's avatar
David Zeuthen committed
186
        }
187
        closedir (dir);
David Zeuthen's avatar
David Zeuthen committed
188
189
190

        return pc;
out:
191
192
193
        if (dir != NULL)
                closedir(dir);

David Zeuthen's avatar
David Zeuthen committed
194
        if (pc != NULL)
195
                polkit_policy_cache_unref (pc);
David Zeuthen's avatar
David Zeuthen committed
196
197
198
199
        return NULL;
}

/**
David Zeuthen's avatar
David Zeuthen committed
200
 * polkit_policy_cache_ref:
David Zeuthen's avatar
David Zeuthen committed
201
202
203
204
205
206
207
 * @policy_cache: the policy cache object
 * 
 * Increase reference count.
 * 
 * Returns: the object
 **/
PolKitPolicyCache *
David Zeuthen's avatar
David Zeuthen committed
208
polkit_policy_cache_ref (PolKitPolicyCache *policy_cache)
David Zeuthen's avatar
David Zeuthen committed
209
{
210
        kit_return_val_if_fail (policy_cache != NULL, policy_cache);
David Zeuthen's avatar
David Zeuthen committed
211
212
213
214
215
        policy_cache->refcount++;
        return policy_cache;
}

/**
David Zeuthen's avatar
David Zeuthen committed
216
 * polkit_policy_cache_unref:
David Zeuthen's avatar
David Zeuthen committed
217
218
219
220
221
222
223
 * @policy_cache: the policy cache object
 * 
 * Decreases the reference count of the object. If it becomes zero,
 * the object is freed. Before freeing, reference counts on embedded
 * objects are decresed by one.
 **/
void
David Zeuthen's avatar
David Zeuthen committed
224
polkit_policy_cache_unref (PolKitPolicyCache *policy_cache)
David Zeuthen's avatar
David Zeuthen committed
225
{
226
        KitList *i;
David Zeuthen's avatar
David Zeuthen committed
227

228
        kit_return_if_fail (policy_cache != NULL);
David Zeuthen's avatar
David Zeuthen committed
229
230
231
232
        policy_cache->refcount--;
        if (policy_cache->refcount > 0) 
                return;

233
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
David Zeuthen's avatar
David Zeuthen committed
234
                PolKitPolicyFileEntry *pfe = i->data;
David Zeuthen's avatar
David Zeuthen committed
235
                polkit_policy_file_entry_unref (pfe);
David Zeuthen's avatar
David Zeuthen committed
236
237
        }
        if (policy_cache->priv_entries != NULL)
238
                kit_list_free (policy_cache->priv_entries);
David Zeuthen's avatar
David Zeuthen committed
239

240
        kit_free (policy_cache);
David Zeuthen's avatar
David Zeuthen committed
241
242
243
}

/**
David Zeuthen's avatar
David Zeuthen committed
244
 * polkit_policy_cache_debug:
David Zeuthen's avatar
David Zeuthen committed
245
246
247
248
249
 * @policy_cache: the cache
 * 
 * Print debug information about object
 **/
void
David Zeuthen's avatar
David Zeuthen committed
250
polkit_policy_cache_debug (PolKitPolicyCache *policy_cache)
David Zeuthen's avatar
David Zeuthen committed
251
{
252
253
        KitList *i;
        kit_return_if_fail (policy_cache != NULL);
David Zeuthen's avatar
David Zeuthen committed
254

255
256
257
        polkit_debug ("PolKitPolicyCache: refcount=%d num_entries=%d ...", 
                      policy_cache->refcount,
                      policy_cache->priv_entries == NULL ? 0 : kit_list_length (policy_cache->priv_entries));
David Zeuthen's avatar
David Zeuthen committed
258

259
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
David Zeuthen's avatar
David Zeuthen committed
260
                PolKitPolicyFileEntry *pfe = i->data;
David Zeuthen's avatar
David Zeuthen committed
261
                polkit_policy_file_entry_debug (pfe);
David Zeuthen's avatar
David Zeuthen committed
262
263
264
        }
}

265
266
267
/**
 * polkit_policy_cache_get_entry_by_id:
 * @policy_cache: the cache
David Zeuthen's avatar
David Zeuthen committed
268
 * @action_id: the action identifier
269
270
271
272
273
274
275
276
277
278
279
280
 * 
 * Given a action identifier, find the object describing the
 * definition of the policy; e.g. data stemming from files in
 * /usr/share/PolicyKit/policy.
 * 
 * Returns: A #PolKitPolicyFileEntry entry on sucess; otherwise
 * #NULL if the action wasn't identified. Caller shall not unref
 * this object.
 **/
PolKitPolicyFileEntry* 
polkit_policy_cache_get_entry_by_id (PolKitPolicyCache *policy_cache, const char *action_id)
{
281
        KitList *i;
282
283
        PolKitPolicyFileEntry *pfe;

284
285
        kit_return_val_if_fail (policy_cache != NULL, NULL);
        kit_return_val_if_fail (action_id != NULL, NULL);
286
287
288

        pfe = NULL;

289
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
290
291
292
293
294
                pfe = i->data;
                if (strcmp (polkit_policy_file_entry_get_id (pfe), action_id) == 0) {
                        goto out;
                }
        }
295
        pfe = NULL;
296

297
298
299
300
        if (pfe == NULL) {
                /* the authdb backend may want to synthesize pfe's */
                pfe = _polkit_authorization_db_pfe_get_by_id (policy_cache, action_id);
        }
301
302
303
304
305

out:
        return pfe;        
}

David Zeuthen's avatar
David Zeuthen committed
306
/**
David Zeuthen's avatar
David Zeuthen committed
307
 * polkit_policy_cache_get_entry:
David Zeuthen's avatar
David Zeuthen committed
308
309
310
311
312
 * @policy_cache: the cache
 * @action: the action
 * 
 * Given a action, find the object describing the definition of the
 * policy; e.g. data stemming from files in
313
 * /usr/share/PolicyKit/policy.
David Zeuthen's avatar
David Zeuthen committed
314
315
316
317
318
319
 * 
 * Returns: A #PolKitPolicyFileEntry entry on sucess; otherwise
 * #NULL if the action wasn't identified. Caller shall not unref
 * this object.
 **/
PolKitPolicyFileEntry* 
David Zeuthen's avatar
David Zeuthen committed
320
polkit_policy_cache_get_entry (PolKitPolicyCache *policy_cache,
David Zeuthen's avatar
David Zeuthen committed
321
322
                                  PolKitAction      *action)
{
323
        char *action_id;
David Zeuthen's avatar
David Zeuthen committed
324
325
326
327
        PolKitPolicyFileEntry *pfe;

        /* I'm sure it would be easy to make this O(1)... */

328
329
        kit_return_val_if_fail (policy_cache != NULL, NULL);
        kit_return_val_if_fail (action != NULL, NULL);
David Zeuthen's avatar
David Zeuthen committed
330
331
332

        pfe = NULL;

333
334
335
336
        if (!polkit_action_get_action_id (action, &action_id))
                goto out;

        pfe = polkit_policy_cache_get_entry_by_id (policy_cache, action_id);
337

David Zeuthen's avatar
David Zeuthen committed
338
339
340
out:
        return pfe;
}
341
342
343
344
345
346
347
348

/**
 * polkit_policy_cache_foreach:
 * @policy_cache: the policy cache
 * @callback: callback function
 * @user_data: user data to pass to callback function
 * 
 * Visit all entries in the policy cache.
349
350
 *
 * Returns: #TRUE only if iteration was short-circuited
351
 **/
352
polkit_bool_t
353
354
355
356
polkit_policy_cache_foreach (PolKitPolicyCache *policy_cache, 
                             PolKitPolicyCacheForeachFunc callback,
                             void *user_data)
{
357
        KitList *i;
358
359
        PolKitPolicyFileEntry *pfe;

360
361
        kit_return_val_if_fail (policy_cache != NULL, FALSE);
        kit_return_val_if_fail (callback != NULL, FALSE);
362

363
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
364
                pfe = i->data;
365
366
                if (callback (policy_cache, pfe, user_data))
                        return TRUE;
367
        }
368
369

        /* the authdb backend may also want to return synthesized pfe's */
370
371
372
        return _polkit_authorization_db_pfe_foreach (policy_cache,
                                                     callback,
                                                     user_data);
373
}
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388

/**
 * polkit_policy_cache_get_entry_by_annotation:
 * @policy_cache: the policy cache
 * @annotation_key: the key to check for
 * @annotation_value: the value to check for
 *
 * Find the first policy file entry where a given annotation matches a
 * given value. Note that there is nothing preventing the existence of
 * multiple policy file entries matching this criteria; it would
 * however be a packaging bug if this situation occured.
 *
 * Returns: The first #PolKitPolicyFileEntry matching the search
 * criteria. The caller shall not unref this object. Returns #NULL if
 * there are no policy file entries matching the search criteria.
389
390
 *
 * Since: 0.7
391
392
393
394
395
396
 */
PolKitPolicyFileEntry* 
polkit_policy_cache_get_entry_by_annotation (PolKitPolicyCache *policy_cache, 
                                             const char *annotation_key,
                                             const char *annotation_value)
{
397
        KitList *i;
398

399
400
401
        kit_return_val_if_fail (policy_cache != NULL, NULL);
        kit_return_val_if_fail (annotation_key != NULL, NULL);
        kit_return_val_if_fail (annotation_value != NULL, NULL);
402

403
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
404
405
406
407
408
409
410
411
412
413
414
415
416
417
                const char *value;
                PolKitPolicyFileEntry *pfe = i->data;

                value = polkit_policy_file_entry_get_annotation (pfe, annotation_key);
                if (value == NULL)
                        continue;

                if (strcmp (annotation_value, value) == 0) {
                        return pfe;
                }
        }

        return NULL;
}
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457

#ifdef POLKIT_BUILD_TESTS

static polkit_bool_t
_test_count (PolKitPolicyCache *pc, PolKitPolicyFileEntry *pfe, void *user_data)
{
        int *counter = (int *) user_data;
        const char *action_id;

        action_id = polkit_policy_file_entry_get_id (pfe);
        if (action_id != NULL && (strcmp (action_id, "org.example.valid1") == 0 ||
                                  strcmp (action_id, "org.example.valid2") == 0 ||
                                  strcmp (action_id, "org.example.valid2b") == 0 ||
                                  strcmp (action_id, "org.example.valid3") == 0 ||
                                  strcmp (action_id, "org.example.valid3b") == 0 ||
                                  strcmp (action_id, "org.example.valid4") == 0)) {
                *counter += 1;
        }
                    
        return FALSE;
}

static polkit_bool_t
_test_short_circuit (PolKitPolicyCache *pc, PolKitPolicyFileEntry *pfe, void *user_data)
{
        int *counter = (int *) user_data;
        *counter += 1;
        return TRUE;
}

static polkit_bool_t
_run_test (void)
{
        PolKitError *error;
        PolKitPolicyCache *pc;
        PolKitPolicyFileEntry *pfe;
        PolKitAction *a;
        int counter;

        error = NULL;
458
459
        kit_assert (_polkit_policy_cache_new (TEST_DATA_DIR "/non-existant", TRUE, &error) == NULL);
        kit_assert (polkit_error_is_set (error) && 
460
461
462
463
464
465
                  (polkit_error_get_error_code (error) == POLKIT_ERROR_POLICY_FILE_INVALID ||
                   polkit_error_get_error_code (error) == POLKIT_ERROR_OUT_OF_MEMORY));
        polkit_error_free (error);

        error = NULL;
        if ((pc = _polkit_policy_cache_new (TEST_DATA_DIR "/invalid", TRUE, &error)) == NULL) {
466
                kit_assert (polkit_error_is_set (error) && 
467
468
469
470
471
472
473
474
                          polkit_error_get_error_code (error) == POLKIT_ERROR_OUT_OF_MEMORY);
                polkit_error_free (error);
        } else {
                polkit_policy_cache_unref (pc);
        }

        error = NULL;
        if ((pc = _polkit_policy_cache_new (TEST_DATA_DIR "/valid", TRUE, &error)) == NULL) {
475
                kit_assert (polkit_error_is_set (error) && 
476
477
478
479
480
                          polkit_error_get_error_code (error) == POLKIT_ERROR_OUT_OF_MEMORY);
                polkit_error_free (error);
                goto out;
        }

481
482
        kit_assert (polkit_policy_cache_get_entry_by_id (pc, "org.example.valid1") != NULL);
        kit_assert (polkit_policy_cache_get_entry_by_id (pc, "org.example.non-existant") == NULL);
483
484

        pfe = polkit_policy_cache_get_entry_by_annotation (pc, "the.key1", "Some Value 1");
485
        kit_assert (pfe != NULL && strcmp (polkit_policy_file_entry_get_id (pfe), "org.example.valid2") == 0);
486
        pfe = polkit_policy_cache_get_entry_by_annotation (pc, "the.key2", "Some Value 2");
487
        kit_assert (pfe != NULL && strcmp (polkit_policy_file_entry_get_id (pfe), "org.example.valid2") == 0);
488
        pfe = polkit_policy_cache_get_entry_by_annotation (pc, "the.key1", "Some Value 1b");
489
        kit_assert (pfe != NULL && strcmp (polkit_policy_file_entry_get_id (pfe), "org.example.valid2b") == 0);
490
        pfe = polkit_policy_cache_get_entry_by_annotation (pc, "the.key1", "NON-EXISTANT VALUE");
491
        kit_assert (pfe == NULL);
492
        pfe = polkit_policy_cache_get_entry_by_annotation (pc, "NON_EXISTANT KEY", "NON-EXISTANT VALUE");
493
        kit_assert (pfe == NULL);
494
495
496

        if ((a = polkit_action_new ()) != NULL) {
                pfe = polkit_policy_cache_get_entry (pc, a);
497
                kit_assert (pfe == NULL);
498
499
                if (polkit_action_set_action_id (a, "org.example.valid1")) {
                        pfe = polkit_policy_cache_get_entry (pc, a);
500
                        kit_assert (pfe != NULL && strcmp (polkit_policy_file_entry_get_id (pfe), "org.example.valid1") == 0);
501
502
503
                }
                if (polkit_action_set_action_id (a, "org.example.non-existant")) {
                        pfe = polkit_policy_cache_get_entry (pc, a);
504
                        kit_assert (pfe == NULL);
505
506
507
508
509
510
511
                }

                polkit_action_unref (a);
        }

        counter = 0;
        polkit_policy_cache_foreach (pc, _test_count, &counter);
512
        kit_assert (counter == 6);
513
514
515

        counter = 0;
        polkit_policy_cache_foreach (pc, _test_short_circuit, &counter);
516
        kit_assert (counter == 1);
517
518
519
520
521
522
523
524
525

        polkit_policy_cache_debug (pc);
        polkit_policy_cache_ref (pc);
        polkit_policy_cache_unref (pc);
        polkit_policy_cache_unref (pc);
out:
        return TRUE;
}

526
KitTest _test_policy_cache = {
527
528
529
530
531
532
533
        "polkit_policy_cache",
        NULL,
        NULL,
        _run_test
};

#endif /* POLKIT_BUILD_TESTS */