polkit-policy-cache.c 17.3 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
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <errno.h>
44
#include <syslog.h>
45
46
#include <fcntl.h>
#include <dirent.h>
David Zeuthen's avatar
David Zeuthen committed
47

David Zeuthen's avatar
David Zeuthen committed
48
49
50
#include "polkit-debug.h"
#include "polkit-policy-file.h"
#include "polkit-policy-cache.h"
51
#include "polkit-private.h"
52
#include "polkit-test.h"
David Zeuthen's avatar
David Zeuthen committed
53
54

/**
David Zeuthen's avatar
David Zeuthen committed
55
 * SECTION:polkit-policy-cache
David Zeuthen's avatar
David Zeuthen committed
56
57
 * @title: Policy Cache
 * @short_description: Holds the actions defined on the system.
David Zeuthen's avatar
David Zeuthen committed
58
59
60
61
62
63
64
65
66
67
68
 *
 * 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
69
struct _PolKitPolicyCache
David Zeuthen's avatar
David Zeuthen committed
70
71
72
{
        int refcount;

73
        KitList *priv_entries;
David Zeuthen's avatar
David Zeuthen committed
74
75
76
};


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

David Zeuthen's avatar
David Zeuthen committed
85
        polkit_policy_file_entry_ref (policy_file_entry);
86
        l = kit_list_prepend (policy_cache->priv_entries, policy_file_entry);
87
88
89
90
91
92
93
94
        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
95
96
97
}

PolKitPolicyCache *
98
_polkit_policy_cache_new (const char *dirname, polkit_bool_t load_descriptions, PolKitError **error)
David Zeuthen's avatar
David Zeuthen committed
99
{
100
101
        DIR *dir;
        struct dirent64 *d;
David Zeuthen's avatar
David Zeuthen committed
102
103
        PolKitPolicyCache *pc;

104
105
        dir = NULL;

106
        pc = kit_new0 (PolKitPolicyCache, 1);
107
108
109
110
111
        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
112
113
        pc->refcount = 1;

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

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

130
                if (d->d_type != DT_REG)
David Zeuthen's avatar
David Zeuthen committed
131
132
                        continue;

133
134
135
                filename = d->d_name;
                name_len = strlen (filename);
                if (name_len < sizeof (suffix) || strcmp ((filename + name_len - sizeof (suffix) + 1), suffix) != 0)
David Zeuthen's avatar
David Zeuthen committed
136
137
                        continue;

138
139
140
141
142
                path = kit_strdup_printf ("%s/%s", dirname, filename);
                if (path == NULL) {
                        polkit_error_set_error (error, POLKIT_ERROR_OUT_OF_MEMORY, "Out of memory");
                        goto out;
                }
David Zeuthen's avatar
David Zeuthen committed
143
144

                _pk_debug ("Loading %s", path);
145
146
                pk_error = NULL;
                pf = polkit_policy_file_new (path, load_descriptions, &pk_error);
147
                kit_free (path);
David Zeuthen's avatar
David Zeuthen committed
148
149

                if (pf == NULL) {
150
151
152
153
154
                        if (polkit_error_get_error_code (pk_error) == POLKIT_ERROR_OUT_OF_MEMORY) {
                                polkit_error_set_error (error, POLKIT_ERROR_OUT_OF_MEMORY, "Out of memory");
                                goto out;
                        }

155
156
157
158
159
160
                        _pk_debug ("libpolkit: ignoring malformed policy file: %s", 
                                   polkit_error_get_error_message (pk_error));
                        syslog (LOG_ALERT, "libpolkit: ignoring malformed policy file: %s", 
                                polkit_error_get_error_message (pk_error));
                        polkit_error_free (pk_error);
                        continue;
David Zeuthen's avatar
David Zeuthen committed
161
162
                }

163
                /* steal entries */
164
165
166
167
168
169
                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
170
                polkit_policy_file_unref (pf);
David Zeuthen's avatar
David Zeuthen committed
171
        }
172
        closedir (dir);
David Zeuthen's avatar
David Zeuthen committed
173
174
175

        return pc;
out:
176
177
178
        if (dir != NULL)
                closedir(dir);

David Zeuthen's avatar
David Zeuthen committed
179
        if (pc != NULL)
180
                polkit_policy_cache_unref (pc);
David Zeuthen's avatar
David Zeuthen committed
181
182
183
184
        return NULL;
}

/**
David Zeuthen's avatar
David Zeuthen committed
185
 * polkit_policy_cache_ref:
David Zeuthen's avatar
David Zeuthen committed
186
187
188
189
190
191
192
 * @policy_cache: the policy cache object
 * 
 * Increase reference count.
 * 
 * Returns: the object
 **/
PolKitPolicyCache *
David Zeuthen's avatar
David Zeuthen committed
193
polkit_policy_cache_ref (PolKitPolicyCache *policy_cache)
David Zeuthen's avatar
David Zeuthen committed
194
{
195
        kit_return_val_if_fail (policy_cache != NULL, policy_cache);
David Zeuthen's avatar
David Zeuthen committed
196
197
198
199
200
        policy_cache->refcount++;
        return policy_cache;
}

/**
David Zeuthen's avatar
David Zeuthen committed
201
 * polkit_policy_cache_unref:
David Zeuthen's avatar
David Zeuthen committed
202
203
204
205
206
207
208
 * @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
209
polkit_policy_cache_unref (PolKitPolicyCache *policy_cache)
David Zeuthen's avatar
David Zeuthen committed
210
{
211
        KitList *i;
David Zeuthen's avatar
David Zeuthen committed
212

213
        kit_return_if_fail (policy_cache != NULL);
David Zeuthen's avatar
David Zeuthen committed
214
215
216
217
        policy_cache->refcount--;
        if (policy_cache->refcount > 0) 
                return;

218
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
David Zeuthen's avatar
David Zeuthen committed
219
                PolKitPolicyFileEntry *pfe = i->data;
David Zeuthen's avatar
David Zeuthen committed
220
                polkit_policy_file_entry_unref (pfe);
David Zeuthen's avatar
David Zeuthen committed
221
222
        }
        if (policy_cache->priv_entries != NULL)
223
                kit_list_free (policy_cache->priv_entries);
David Zeuthen's avatar
David Zeuthen committed
224

225
        kit_free (policy_cache);
David Zeuthen's avatar
David Zeuthen committed
226
227
228
}

/**
David Zeuthen's avatar
David Zeuthen committed
229
 * polkit_policy_cache_debug:
David Zeuthen's avatar
David Zeuthen committed
230
231
232
233
234
 * @policy_cache: the cache
 * 
 * Print debug information about object
 **/
void
David Zeuthen's avatar
David Zeuthen committed
235
polkit_policy_cache_debug (PolKitPolicyCache *policy_cache)
David Zeuthen's avatar
David Zeuthen committed
236
{
237
238
        KitList *i;
        kit_return_if_fail (policy_cache != NULL);
David Zeuthen's avatar
David Zeuthen committed
239
240
241

        _pk_debug ("PolKitPolicyCache: refcount=%d num_entries=%d ...", 
                   policy_cache->refcount,
242
                   policy_cache->priv_entries == NULL ? 0 : kit_list_length (policy_cache->priv_entries));
David Zeuthen's avatar
David Zeuthen committed
243

244
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
David Zeuthen's avatar
David Zeuthen committed
245
                PolKitPolicyFileEntry *pfe = i->data;
David Zeuthen's avatar
David Zeuthen committed
246
                polkit_policy_file_entry_debug (pfe);
David Zeuthen's avatar
David Zeuthen committed
247
248
249
        }
}

250
251
252
/**
 * polkit_policy_cache_get_entry_by_id:
 * @policy_cache: the cache
David Zeuthen's avatar
David Zeuthen committed
253
 * @action_id: the action identifier
254
255
256
257
258
259
260
261
262
263
264
265
 * 
 * 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)
{
266
        KitList *i;
267
268
        PolKitPolicyFileEntry *pfe;

269
270
        kit_return_val_if_fail (policy_cache != NULL, NULL);
        kit_return_val_if_fail (action_id != NULL, NULL);
271
272
273

        pfe = NULL;

274
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
275
276
277
278
279
                pfe = i->data;
                if (strcmp (polkit_policy_file_entry_get_id (pfe), action_id) == 0) {
                        goto out;
                }
        }
280
        pfe = NULL;
281

282
283
284
285
        if (pfe == NULL) {
                /* the authdb backend may want to synthesize pfe's */
                pfe = _polkit_authorization_db_pfe_get_by_id (policy_cache, action_id);
        }
286
287
288
289
290

out:
        return pfe;        
}

David Zeuthen's avatar
David Zeuthen committed
291
/**
David Zeuthen's avatar
David Zeuthen committed
292
 * polkit_policy_cache_get_entry:
David Zeuthen's avatar
David Zeuthen committed
293
294
295
296
297
 * @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
298
 * /usr/share/PolicyKit/policy.
David Zeuthen's avatar
David Zeuthen committed
299
300
301
302
303
304
 * 
 * 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
305
polkit_policy_cache_get_entry (PolKitPolicyCache *policy_cache,
David Zeuthen's avatar
David Zeuthen committed
306
307
                                  PolKitAction      *action)
{
308
        char *action_id;
David Zeuthen's avatar
David Zeuthen committed
309
310
311
312
        PolKitPolicyFileEntry *pfe;

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

313
314
        kit_return_val_if_fail (policy_cache != NULL, NULL);
        kit_return_val_if_fail (action != NULL, NULL);
David Zeuthen's avatar
David Zeuthen committed
315
316
317

        pfe = NULL;

318
319
320
321
        if (!polkit_action_get_action_id (action, &action_id))
                goto out;

        pfe = polkit_policy_cache_get_entry_by_id (policy_cache, action_id);
322

David Zeuthen's avatar
David Zeuthen committed
323
324
325
out:
        return pfe;
}
326
327
328
329
330
331
332
333

/**
 * 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.
334
335
 *
 * Returns: #TRUE only if iteration was short-circuited
336
 **/
337
polkit_bool_t
338
339
340
341
polkit_policy_cache_foreach (PolKitPolicyCache *policy_cache, 
                             PolKitPolicyCacheForeachFunc callback,
                             void *user_data)
{
342
        KitList *i;
343
344
        PolKitPolicyFileEntry *pfe;

345
346
        kit_return_val_if_fail (policy_cache != NULL, FALSE);
        kit_return_val_if_fail (callback != NULL, FALSE);
347

348
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
349
                pfe = i->data;
350
351
                if (callback (policy_cache, pfe, user_data))
                        return TRUE;
352
        }
353
354

        /* the authdb backend may also want to return synthesized pfe's */
355
356
357
        return _polkit_authorization_db_pfe_foreach (policy_cache,
                                                     callback,
                                                     user_data);
358
}
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373

/**
 * 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.
374
375
 *
 * Since: 0.7
376
377
378
379
380
381
 */
PolKitPolicyFileEntry* 
polkit_policy_cache_get_entry_by_annotation (PolKitPolicyCache *policy_cache, 
                                             const char *annotation_key,
                                             const char *annotation_value)
{
382
        KitList *i;
383

384
385
386
        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);
387

388
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
389
390
391
392
393
394
395
396
397
398
399
400
401
402
                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;
}
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
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

#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;
443
444
        kit_assert (_polkit_policy_cache_new (TEST_DATA_DIR "/non-existant", TRUE, &error) == NULL);
        kit_assert (polkit_error_is_set (error) && 
445
446
447
448
449
450
                  (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) {
451
                kit_assert (polkit_error_is_set (error) && 
452
453
454
455
456
457
458
459
                          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) {
460
                kit_assert (polkit_error_is_set (error) && 
461
462
463
464
465
                          polkit_error_get_error_code (error) == POLKIT_ERROR_OUT_OF_MEMORY);
                polkit_error_free (error);
                goto out;
        }

466
467
        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);
468
469

        pfe = polkit_policy_cache_get_entry_by_annotation (pc, "the.key1", "Some Value 1");
470
        kit_assert (pfe != NULL && strcmp (polkit_policy_file_entry_get_id (pfe), "org.example.valid2") == 0);
471
        pfe = polkit_policy_cache_get_entry_by_annotation (pc, "the.key2", "Some Value 2");
472
        kit_assert (pfe != NULL && strcmp (polkit_policy_file_entry_get_id (pfe), "org.example.valid2") == 0);
473
        pfe = polkit_policy_cache_get_entry_by_annotation (pc, "the.key1", "Some Value 1b");
474
        kit_assert (pfe != NULL && strcmp (polkit_policy_file_entry_get_id (pfe), "org.example.valid2b") == 0);
475
        pfe = polkit_policy_cache_get_entry_by_annotation (pc, "the.key1", "NON-EXISTANT VALUE");
476
        kit_assert (pfe == NULL);
477
        pfe = polkit_policy_cache_get_entry_by_annotation (pc, "NON_EXISTANT KEY", "NON-EXISTANT VALUE");
478
        kit_assert (pfe == NULL);
479
480
481

        if ((a = polkit_action_new ()) != NULL) {
                pfe = polkit_policy_cache_get_entry (pc, a);
482
                kit_assert (pfe == NULL);
483
484
                if (polkit_action_set_action_id (a, "org.example.valid1")) {
                        pfe = polkit_policy_cache_get_entry (pc, a);
485
                        kit_assert (pfe != NULL && strcmp (polkit_policy_file_entry_get_id (pfe), "org.example.valid1") == 0);
486
487
488
                }
                if (polkit_action_set_action_id (a, "org.example.non-existant")) {
                        pfe = polkit_policy_cache_get_entry (pc, a);
489
                        kit_assert (pfe == NULL);
490
491
492
493
494
495
496
                }

                polkit_action_unref (a);
        }

        counter = 0;
        polkit_policy_cache_foreach (pc, _test_count, &counter);
497
        kit_assert (counter == 6);
498
499
500

        counter = 0;
        polkit_policy_cache_foreach (pc, _test_short_circuit, &counter);
501
        kit_assert (counter == 1);
502
503
504
505
506
507
508
509
510

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

511
KitTest _test_policy_cache = {
512
513
514
515
516
517
518
        "polkit_policy_cache",
        NULL,
        NULL,
        _run_test
};

#endif /* POLKIT_BUILD_TESTS */