polkit-policy-cache.c 17 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
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 *
 * Copyright (C) 2007 David Zeuthen, <david@fubar.dk>
 *
 * Licensed under the Academic Free License version 2.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307	 USA
 *
 **************************************************************************/

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

30
31
#define _GNU_SOURCE

David Zeuthen's avatar
David Zeuthen committed
32
33
34
35
36
37
38
39
#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>
40
#include <syslog.h>
41
42
#include <fcntl.h>
#include <dirent.h>
David Zeuthen's avatar
David Zeuthen committed
43

David Zeuthen's avatar
David Zeuthen committed
44
45
46
#include "polkit-debug.h"
#include "polkit-policy-file.h"
#include "polkit-policy-cache.h"
47
#include "polkit-private.h"
48
#include "polkit-test.h"
David Zeuthen's avatar
David Zeuthen committed
49
50

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

69
        KitList *priv_entries;
David Zeuthen's avatar
David Zeuthen committed
70
71
72
};


73
74
static polkit_bool_t
_prepend_entry (PolKitPolicyFile       *policy_file,
75
76
               PolKitPolicyFileEntry  *policy_file_entry,
               void                   *user_data)
David Zeuthen's avatar
David Zeuthen committed
77
{
78
        KitList *l;
79
        PolKitPolicyCache *policy_cache = user_data;
David Zeuthen's avatar
David Zeuthen committed
80

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

PolKitPolicyCache *
94
_polkit_policy_cache_new (const char *dirname, polkit_bool_t load_descriptions, PolKitError **error)
David Zeuthen's avatar
David Zeuthen committed
95
{
96
97
        DIR *dir;
        struct dirent64 *d;
David Zeuthen's avatar
David Zeuthen committed
98
99
        PolKitPolicyCache *pc;

100
101
        dir = NULL;

102
        pc = kit_new0 (PolKitPolicyCache, 1);
103
104
105
106
107
        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
108
109
        pc->refcount = 1;

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

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

126
                if (d->d_type != DT_REG)
David Zeuthen's avatar
David Zeuthen committed
127
128
                        continue;

129
130
131
                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
132
133
                        continue;

134
135
136
137
138
                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
139
140

                _pk_debug ("Loading %s", path);
141
142
                pk_error = NULL;
                pf = polkit_policy_file_new (path, load_descriptions, &pk_error);
143
                kit_free (path);
David Zeuthen's avatar
David Zeuthen committed
144
145

                if (pf == NULL) {
146
147
148
149
150
                        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;
                        }

151
152
153
154
155
156
                        _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
157
158
                }

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

        return pc;
out:
172
173
174
        if (dir != NULL)
                closedir(dir);

David Zeuthen's avatar
David Zeuthen committed
175
        if (pc != NULL)
176
                polkit_policy_cache_unref (pc);
David Zeuthen's avatar
David Zeuthen committed
177
178
179
180
        return NULL;
}

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

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

209
        kit_return_if_fail (policy_cache != NULL);
David Zeuthen's avatar
David Zeuthen committed
210
211
212
213
        policy_cache->refcount--;
        if (policy_cache->refcount > 0) 
                return;

214
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
David Zeuthen's avatar
David Zeuthen committed
215
                PolKitPolicyFileEntry *pfe = i->data;
David Zeuthen's avatar
David Zeuthen committed
216
                polkit_policy_file_entry_unref (pfe);
David Zeuthen's avatar
David Zeuthen committed
217
218
        }
        if (policy_cache->priv_entries != NULL)
219
                kit_list_free (policy_cache->priv_entries);
David Zeuthen's avatar
David Zeuthen committed
220

221
        kit_free (policy_cache);
David Zeuthen's avatar
David Zeuthen committed
222
223
224
}

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

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

240
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
David Zeuthen's avatar
David Zeuthen committed
241
                PolKitPolicyFileEntry *pfe = i->data;
David Zeuthen's avatar
David Zeuthen committed
242
                polkit_policy_file_entry_debug (pfe);
David Zeuthen's avatar
David Zeuthen committed
243
244
245
        }
}

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

265
266
        kit_return_val_if_fail (policy_cache != NULL, NULL);
        kit_return_val_if_fail (action_id != NULL, NULL);
267
268
269

        pfe = NULL;

270
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
271
272
273
274
275
                pfe = i->data;
                if (strcmp (polkit_policy_file_entry_get_id (pfe), action_id) == 0) {
                        goto out;
                }
        }
276
        pfe = NULL;
277

278
279
280
281
        if (pfe == NULL) {
                /* the authdb backend may want to synthesize pfe's */
                pfe = _polkit_authorization_db_pfe_get_by_id (policy_cache, action_id);
        }
282
283
284
285
286

out:
        return pfe;        
}

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

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

309
310
        kit_return_val_if_fail (policy_cache != NULL, NULL);
        kit_return_val_if_fail (action != NULL, NULL);
David Zeuthen's avatar
David Zeuthen committed
311
312
313

        pfe = NULL;

314
315
316
317
        if (!polkit_action_get_action_id (action, &action_id))
                goto out;

        pfe = polkit_policy_cache_get_entry_by_id (policy_cache, action_id);
318

David Zeuthen's avatar
David Zeuthen committed
319
320
321
out:
        return pfe;
}
322
323
324
325
326
327
328
329

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

341
342
        kit_return_val_if_fail (policy_cache != NULL, FALSE);
        kit_return_val_if_fail (callback != NULL, FALSE);
343

344
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
345
                pfe = i->data;
346
347
                if (callback (policy_cache, pfe, user_data))
                        return TRUE;
348
        }
349
350

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

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

380
381
382
        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);
383

384
        for (i = policy_cache->priv_entries; i != NULL; i = i->next) {
385
386
387
388
389
390
391
392
393
394
395
396
397
398
                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;
}
399
400
401
402
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

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

462
463
        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);
464
465

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

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

                polkit_action_unref (a);
        }

        counter = 0;
        polkit_policy_cache_foreach (pc, _test_count, &counter);
493
        kit_assert (counter == 6);
494
495
496

        counter = 0;
        polkit_policy_cache_foreach (pc, _test_short_circuit, &counter);
497
        kit_assert (counter == 1);
498
499
500
501
502
503
504
505
506

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

507
KitTest _test_policy_cache = {
508
509
510
511
512
513
514
        "polkit_policy_cache",
        NULL,
        NULL,
        _run_test
};

#endif /* POLKIT_BUILD_TESTS */