libpolkit-context.c 27.3 KB
Newer Older
1
2
3
4
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
30
31
32
33
34
35
36
37
38
39
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/***************************************************************************
 *
 * libpolkit-context.c : context for PolicyKit
 *
 * 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

#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>

#include <glib.h>
40
#include "libpolkit-debug.h"
41
#include "libpolkit-context.h"
David Zeuthen's avatar
David Zeuthen committed
42
#include "libpolkit-policy-cache.h"
43
#include "libpolkit-module.h"
44

45
46
47
48
49
50
51
/**
 * SECTION:libpolkit
 * @short_description: Centralized policy management.
 *
 * libpolkit is a C library for centralized policy management.
 **/

David Zeuthen's avatar
David Zeuthen committed
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
 * SECTION:libpolkit-context
 * @short_description: Context.
 *
 * This class is used to represent the interface to PolicyKit.
 **/

/**
 * PolKitContext:
 *
 * Context object for users of PolicyKit.
 **/
struct PolKitContext
65
{
66
        int refcount;
67

68
69
        PolKitContextConfigChangedCB config_changed_cb;
        gpointer config_changed_user_data;
70

71
72
73
        PolKitContextFileMonitorAddWatch      file_monitor_add_watch_func;
        PolKitContextFileMonitorRemoveWatch   file_monitor_remove_watch_func;

David Zeuthen's avatar
David Zeuthen committed
74
        char *policy_dir;
75

David Zeuthen's avatar
David Zeuthen committed
76
        PolKitPolicyCache *priv_cache;
77
78

        GSList *modules;
79
80
};

David Zeuthen's avatar
David Zeuthen committed
81
82
83
/**
 * libpolkit_context_new:
 * 
84
 * Create a new context
David Zeuthen's avatar
David Zeuthen committed
85
 * 
David Zeuthen's avatar
David Zeuthen committed
86
 * Returns: the object
David Zeuthen's avatar
David Zeuthen committed
87
 **/
88
PolKitContext *
89
libpolkit_context_new (void)
90
{
91
92
93
        PolKitContext *pk_context;
        pk_context = g_new0 (PolKitContext, 1);
        pk_context->refcount = 1;
94
95
96
        return pk_context;
}

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
static gboolean
unload_modules (PolKitContext *pk_context)
{
        GSList *i;
        for (i = pk_context->modules; i != NULL; i = g_slist_next (i)) {
                PolKitModuleInterface *module_interface = i->data;
                libpolkit_module_interface_unref (module_interface);
        }
        g_slist_free (pk_context->modules);
        pk_context->modules = NULL;
        _pk_debug ("Unloaded modules");

        return TRUE;
}

static gboolean
load_modules (PolKitContext *pk_context, GError **error)
{
        const char *config_file;
        gboolean ret;
        char *buf;
        char *end;
        char line[256];
        char *p;
        char *q;
        gsize len;
        int line_number;
        int mod_number;

        ret = FALSE;
        buf = NULL;
        mod_number = 0;

        config_file = PACKAGE_SYSCONF_DIR "/PolicyKit/PolicyKit.conf";
        if (!g_file_get_contents (config_file,
                                  &buf,
                                  &len,
                                  error)) {
                _pk_debug ("Cannot load PolicyKit configuration file at '%s'", config_file);
                goto out;
        }

        end = buf + len;

        /* parse the config file; one line at a time (yes, this is super ugly code) */
        p = buf;
        line_number = -1;
        while (TRUE) {
                int argc;
                char **tokens;
                char *module_name;
                char *module_path;
                PolKitModuleControl module_control;
                PolKitModuleInterface *module_interface;

                line_number++;

                q = p;
                while (*q != '\n' && q != '\0' && q < end)
                        q++;
                if (*q == '\0' || q >= end) {
                        /* skip last line if it's not terminated by whitespace */
                        break;
                }
                if ((unsigned int) (q - p) > sizeof(line) - 1) {
                        _pk_debug ("Line is too long; skipping it");
                        continue;
                }
                strncpy (line, p, q - p);
                line[q - p] = '\0';
                p = q + 1;

                /* remove leading and trailing white space */
                g_strstrip (line);

                /* comments, blank lines are fine; just skip them */
                if (line[0] == '#' || strlen (line) == 0) {
                        continue;
                }

                /*_pk_debug ("Looking at line: '%s'", line);*/

                if (!g_shell_parse_argv (line, &argc, &tokens, NULL)) {
                        _pk_debug ("Cannot parse line %d - skipping", line_number);
                        continue;
                }
                if (argc < 2) {
                        _pk_debug ("Line %d is malformed - skipping line", line_number);
                        g_strfreev (tokens);
                        continue;
                }
                if (!libpolkit_module_control_from_string_representation (tokens[0], &module_control)) {
                        _pk_debug ("Unknown module_control '%s' at line %d - skipping line", tokens[0], line_number);
                        g_strfreev (tokens);
                        continue;
                }
                module_name = tokens[1];

                module_path = g_strdup_printf (PACKAGE_LIB_DIR "/PolicyKit/modules/%s", module_name);
                _pk_debug ("MODULE: number=%d control=%d name=%s argc=%d", 
                           mod_number, module_control, module_name, argc - 1);
                module_interface = libpolkit_module_interface_load_module (module_path, 
                                                                           module_control, 
                                                                           argc - 1, 
                                                                           tokens + 1);
                g_free (module_path);

                if (module_interface != NULL) {
                        pk_context->modules = g_slist_append (pk_context->modules, module_interface);
                        mod_number++;
                }
                g_strfreev (tokens);

        }

        ret = TRUE;

out:
        if (buf != NULL)
                g_free (buf);

        _pk_debug ("Loaded %d modules in total", mod_number);
        return ret;
}

static void
_config_file_events (PolKitContext                 *pk_context,
                     PolKitContextFileMonitorEvent  event_mask,
                     const char                    *path,
                     gpointer                       user_data)
{
        _pk_debug ("Config file changed");
        unload_modules (pk_context);
        load_modules (pk_context, NULL);

        /* signal that our configuration (may have) changed */
        if (pk_context->config_changed_cb) {
                pk_context->config_changed_cb (pk_context, pk_context->config_changed_user_data);
        }
}

238
static void
David Zeuthen's avatar
David Zeuthen committed
239
_policy_dir_events (PolKitContext                 *pk_context,
240
241
242
243
                       PolKitContextFileMonitorEvent  event_mask,
                       const char                    *path,
                       gpointer                       user_data)
{
David Zeuthen's avatar
David Zeuthen committed
244
        /* mark cache of policy files as stale.. (will be populated on-demand, see _get_cache()) */
245
        if (pk_context->priv_cache != NULL) {
David Zeuthen's avatar
David Zeuthen committed
246
247
                _pk_debug ("Something happened in %s - invalidating cache", pk_context->policy_dir);
                libpolkit_policy_cache_unref (pk_context->priv_cache);
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
                pk_context->priv_cache = NULL;
        }

        /* signal that our configuration (may have) changed */
        if (pk_context->config_changed_cb) {
                pk_context->config_changed_cb (pk_context, pk_context->config_changed_user_data);
        }
}

/**
 * libpolkit_context_init:
 * @pk_context: the context object
 * @error: return location for error
 * 
 * Initializes a new context; loads PolicyKit files from
David Zeuthen's avatar
David Zeuthen committed
263
264
 * /etc/PolicyKit/policy unless the environment variable
 * $POLKIT_POLICY_DIR points to a location.
265
266
267
268
269
270
271
272
 *
 * Returns: #FALSE if @error was set, otherwise #TRUE
 **/
gboolean
libpolkit_context_init (PolKitContext *pk_context, GError **error)
{
        const char *dirname;

David Zeuthen's avatar
David Zeuthen committed
273
        dirname = getenv ("POLKIT_POLICY_DIR");
274
        if (dirname != NULL) {
David Zeuthen's avatar
David Zeuthen committed
275
                pk_context->policy_dir = g_strdup (dirname);
276
        } else {
David Zeuthen's avatar
David Zeuthen committed
277
                pk_context->policy_dir = g_strdup (PACKAGE_SYSCONF_DIR "/PolicyKit/policy");
278
        }
David Zeuthen's avatar
David Zeuthen committed
279
        _pk_debug ("Using policy files from directory %s", pk_context->policy_dir);
280

281
282
283
284
        /* Load modules */
        if (!load_modules (pk_context, error))
                goto error;

285
        /* don't populate the cache until it's needed.. */
286

287
        if (pk_context->file_monitor_add_watch_func == NULL) {
288
                _pk_debug ("No file monitor; cannot monitor '%s' for .policy file changes", pk_context->policy_dir);
289
        } else {
David Zeuthen's avatar
David Zeuthen committed
290
                /* Watch when policy definitions file change */
291
                pk_context->file_monitor_add_watch_func (pk_context, 
David Zeuthen's avatar
David Zeuthen committed
292
                                                         pk_context->policy_dir,
293
294
295
                                                         POLKIT_CONTEXT_FILE_MONITOR_EVENT_CREATE|
                                                         POLKIT_CONTEXT_FILE_MONITOR_EVENT_DELETE|
                                                         POLKIT_CONTEXT_FILE_MONITOR_EVENT_CHANGE,
David Zeuthen's avatar
David Zeuthen committed
296
                                                         _policy_dir_events,
297
                                                         NULL);
298
299
300
301
302
303
304
305
306

                /* Config file changes */
                pk_context->file_monitor_add_watch_func (pk_context, 
                                                         PACKAGE_SYSCONF_DIR "/PolicyKit",
                                                         POLKIT_CONTEXT_FILE_MONITOR_EVENT_CREATE|
                                                         POLKIT_CONTEXT_FILE_MONITOR_EVENT_DELETE|
                                                         POLKIT_CONTEXT_FILE_MONITOR_EVENT_CHANGE,
                                                         _config_file_events,
                                                         NULL);
307
308
        }

309
310
311
312
        return TRUE;
error:
        if (pk_context != NULL)
                libpolkit_context_unref (pk_context);
313

314
        return FALSE;
315
316
}

David Zeuthen's avatar
David Zeuthen committed
317
318
319
320
321
322
323
324
/**
 * libpolkit_context_ref:
 * @pk_context: the context object
 * 
 * Increase reference count.
 * 
 * Returns: the object
 **/
325
326
327
PolKitContext *
libpolkit_context_ref (PolKitContext *pk_context)
{
328
329
        g_return_val_if_fail (pk_context != NULL, pk_context);
        pk_context->refcount++;
330
331
332
        return pk_context;
}

333
334
335
336
337
338
339
340
341
342
343
/**
 * libpolkit_context_unref:
 * @pk_context: the context 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
libpolkit_context_unref (PolKitContext *pk_context)
{
344

345
346
347
348
        g_return_if_fail (pk_context != NULL);
        pk_context->refcount--;
        if (pk_context->refcount > 0) 
                return;
349
350
351

        unload_modules (pk_context);

352
353
354
        g_free (pk_context);
}

David Zeuthen's avatar
David Zeuthen committed
355
356
357
358
359
360
361
362
363
/**
 * libpolkit_context_set_config_changed:
 * @pk_context: the context object
 * @cb: the callback to invoke
 * @user_data: user data to pass to the callback
 * 
 * Register the callback function for when configuration changes.
 * Mechanisms should use this callback to e.g. reconfigure all
 * permissions / acl's they have set in response to policy decisions
364
365
366
367
368
369
370
371
372
 * made from information provided by PolicyKit. 
 *
 * Note that this function may be called many times within a short
 * interval due to how file monitoring works if e.g. the user is
 * editing a configuration file (editors typically create back-up
 * files). Mechanisms should use a "cool-off" timer (of, say, one
 * second) to avoid doing many expensive operations (such as
 * reconfiguring all ACL's for all devices) within a very short
 * timeframe.
David Zeuthen's avatar
David Zeuthen committed
373
 **/
374
375
376
377
378
void
libpolkit_context_set_config_changed (PolKitContext                *pk_context, 
                                      PolKitContextConfigChangedCB cb, 
                                      gpointer                     user_data)
{
379
380
381
        g_return_if_fail (pk_context != NULL);
        pk_context->config_changed_cb = cb;
        pk_context->config_changed_user_data = user_data;
382
}
383

384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
/**
 * libpolkit_context_set_file_monitor:
 * @pk_context: the context object
 * @add_watch_func: the function that the PolicyKit library can invoke to start watching a file
 * @remove_watch_func: the function that the PolicyKit library can invoke to stop watching a file
 * 
 * Register a functions that PolicyKit can use for watching files.
 **/
void
libpolkit_context_set_file_monitor (PolKitContext                        *pk_context, 
                                    PolKitContextFileMonitorAddWatch      add_watch_func,
                                    PolKitContextFileMonitorRemoveWatch   remove_watch_func)
{
        g_return_if_fail (pk_context != NULL);
        pk_context->file_monitor_add_watch_func = add_watch_func;
        pk_context->file_monitor_remove_watch_func = remove_watch_func;
}


403
/**
David Zeuthen's avatar
David Zeuthen committed
404
 * libpolkit_context_get_policy_cache:
405
406
 * @pk_context: the context
 * 
David Zeuthen's avatar
David Zeuthen committed
407
 * Get the #PolKitPolicyCache object that holds all the defined policies as well as their defaults.
408
 * 
David Zeuthen's avatar
David Zeuthen committed
409
 * Returns: the #PolKitPolicyCache object. Caller shall not unref it.
410
 **/
David Zeuthen's avatar
David Zeuthen committed
411
412
PolKitPolicyCache *
libpolkit_context_get_policy_cache (PolKitContext *pk_context)
413
414
{
        g_return_val_if_fail (pk_context != NULL, NULL);
415
416
417
418

        if (pk_context->priv_cache == NULL) {
                GError *error;

David Zeuthen's avatar
David Zeuthen committed
419
                _pk_debug ("Populating cache from directory %s", pk_context->policy_dir);
420
421

                error = NULL;
David Zeuthen's avatar
David Zeuthen committed
422
                pk_context->priv_cache = libpolkit_policy_cache_new (pk_context->policy_dir, &error);
423
                if (pk_context->priv_cache == NULL) {
David Zeuthen's avatar
David Zeuthen committed
424
425
                        g_warning ("Error loading policy files from %s: %s", 
                                   pk_context->policy_dir, error->message);
426
427
                        g_error_free (error);
                } else {
428
                        libpolkit_policy_cache_debug (pk_context->priv_cache);
429
430
431
                }
        }

432
433
        return pk_context->priv_cache;
}
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491


/**
 * libpolkit_context_get_seat_resource_association:
 * @pk_context: the PolicyKit context
 * @visitor: visitor function
 * @user_data: user data
 *
 * Retrieve information about what resources are associated to what
 * seats. Note that a resource may be associated to more than one
 * seat. This information stems from user configuration and consumers
 * of this information that know better (e.g. HAL) may choose to
 * override it. 
 *
 * Typically, this information is used to e.g. bootstrap the system
 * insofar that it can be used to start login greeters on the given
 * video hardware (e.g. resources) on the given user-configured seats.
 *
 * If a resource is not associated with any seat, it is assumed to be
 * available to any local seat.
 *
 * Returns: A #PolKitResult - can only be one of
 * #LIBPOLKIT_RESULT_NOT_AUTHORIZED_TO_KNOW or
 * #LIBPOLKIT_RESULT_YES (if the callback was invoked)
 */
PolKitResult
libpolkit_context_get_seat_resource_association (PolKitContext       *pk_context,
                                                 PolKitSeatVisitorCB  visitor,
                                                 gpointer            *user_data)
{
        return LIBPOLKIT_RESULT_YES;
}

/**
 * libpolkit_context_is_resource_associated_with_seat:
 * @pk_context: the PolicyKit context
 * @resource: the resource in question
 * @seat: the seat
 *
 * Determine if a given resource is associated with a given seat. The
 * same comments noted in libpolkit_get_seat_resource_association() about the
 * source purely being user configuration applies here as well.
 *
 * Returns: A #PolKitResult - can only be one of
 * #LIBPOLKIT_RESULT_NOT_AUTHORIZED_TO_KNOW,
 * #LIBPOLKIT_RESULT_YES, #LIBPOLKIT_RESULT_NO.
 */
PolKitResult
libpolkit_context_is_resource_associated_with_seat (PolKitContext   *pk_context,
                                                    PolKitResource  *resource,
                                                    PolKitSeat      *seat)
{
        return LIBPOLKIT_RESULT_NO;
}

/**
 * libpolkit_context_can_session_access_resource:
 * @pk_context: the PolicyKit context
David Zeuthen's avatar
David Zeuthen committed
492
 * @action: the type of access to check for
493
494
495
496
497
498
499
500
501
502
503
 * @resource: the resource in question
 * @session: the session in question
 *
 * Determine if a given session can access a given resource in a given way.
 *
 * Returns: A #PolKitResult - can only be one of
 * #LIBPOLKIT_RESULT_NOT_AUTHORIZED_TO_KNOW,
 * #LIBPOLKIT_RESULT_YES, #LIBPOLKIT_RESULT_NO.
 */
PolKitResult
libpolkit_context_can_session_access_resource (PolKitContext   *pk_context,
David Zeuthen's avatar
David Zeuthen committed
504
                                               PolKitAction *action,
505
506
507
                                               PolKitResource  *resource,
                                               PolKitSession   *session)
{
David Zeuthen's avatar
David Zeuthen committed
508
509
        PolKitPolicyCache *cache;
        PolKitPolicyFileEntry *pfe;
510
511
512
513
514
515
        PolKitResult current_result;
        PolKitModuleControl current_control;
        GSList *i;

        current_result = LIBPOLKIT_RESULT_NO;

David Zeuthen's avatar
David Zeuthen committed
516
        cache = libpolkit_context_get_policy_cache (pk_context);
517
518
519
520
        if (cache == NULL)
                goto out;

        _pk_debug ("entering libpolkit_can_session_access_resource()");
David Zeuthen's avatar
David Zeuthen committed
521
        libpolkit_action_debug (action);
522
523
524
        libpolkit_resource_debug (resource);
        libpolkit_session_debug (session);

David Zeuthen's avatar
David Zeuthen committed
525
        pfe = libpolkit_policy_cache_get_entry (cache, action);
526
        if (pfe == NULL) {
David Zeuthen's avatar
David Zeuthen committed
527
528
529
                char *action_name;
                if (!libpolkit_action_get_action_id (action, &action_name)) {
                        g_warning ("given action has no name");
530
                } else {
David Zeuthen's avatar
David Zeuthen committed
531
                        g_warning ("no action with name '%s'", action_name);
532
                }
David Zeuthen's avatar
David Zeuthen committed
533
                current_result = LIBPOLKIT_RESULT_UNKNOWN_ACTION;
534
535
536
                goto out;
        }

David Zeuthen's avatar
David Zeuthen committed
537
        libpolkit_policy_file_entry_debug (pfe);
538

David Zeuthen's avatar
David Zeuthen committed
539
        current_result = LIBPOLKIT_RESULT_UNKNOWN_ACTION;
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
        current_control = LIBPOLKIT_MODULE_CONTROL_ADVISE; /* start with advise */

        /* visit modules */
        for (i = pk_context->modules; i != NULL; i = g_slist_next (i)) {
                PolKitModuleInterface *module_interface = i->data;
                PolKitModuleCanSessionAccessResource func;

                func = libpolkit_module_get_func_can_session_access_resource (module_interface);
                if (func != NULL) {
                        PolKitModuleControl module_control;
                        PolKitResult module_result;

                        _pk_debug ("Asking module '%s'", libpolkit_module_get_name (module_interface));

                        module_control = libpolkit_module_interface_get_control (module_interface);
555
556
557
558

                        if (libpolkit_module_interface_check_builtin_confinement_for_session (
                                    module_interface,
                                    pk_context,
David Zeuthen's avatar
David Zeuthen committed
559
                                    action,
560
561
562
                                    resource,
                                    session)) {
                                /* module is confined by built-in options */
David Zeuthen's avatar
David Zeuthen committed
563
                                module_result = LIBPOLKIT_RESULT_UNKNOWN_ACTION;
564
565
566
567
568
                                _pk_debug ("Module '%s' confined by built-in's", 
                                           libpolkit_module_get_name (module_interface));
                        } else {
                                module_result = func (module_interface,
                                                      pk_context,
David Zeuthen's avatar
David Zeuthen committed
569
                                                      action, 
570
571
572
                                                      resource, 
                                                      session);
                        }
573

David Zeuthen's avatar
David Zeuthen committed
574
                        /* if a module returns _UNKNOWN_ACTION, it means that it doesn't
575
                         * have an opinion about the query; e.g. polkit-module-allow-all(8)
David Zeuthen's avatar
David Zeuthen committed
576
                         * will return this if it's confined to only consider certain actions
577
578
                         * or certain users.
                         */
David Zeuthen's avatar
David Zeuthen committed
579
                        if (module_result != LIBPOLKIT_RESULT_UNKNOWN_ACTION) {
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601

                                if (current_control == LIBPOLKIT_MODULE_CONTROL_ADVISE &&
                                    module_control == LIBPOLKIT_MODULE_CONTROL_ADVISE) {

                                        /* take the less strict result */
                                        if (current_result < module_result) {
                                                current_result = module_result;
                                        }

                                } else if (current_control == LIBPOLKIT_MODULE_CONTROL_ADVISE &&
                                           module_control == LIBPOLKIT_MODULE_CONTROL_MANDATORY) {
                                        
                                        /* here we just override */
                                        current_result = module_result;

                                        /* we are now in mandatory mode */
                                        current_control = LIBPOLKIT_MODULE_CONTROL_MANDATORY;
                                }
                        }
                }
        }

David Zeuthen's avatar
David Zeuthen committed
602
603
        /* Never return UNKNOWN_ACTION to user */
        if (current_result == LIBPOLKIT_RESULT_UNKNOWN_ACTION)
604
605
606
607
608
609
610
611
612
613
                current_result = LIBPOLKIT_RESULT_NO;

out:
        _pk_debug ("... result was %s", libpolkit_result_to_string_representation (current_result));
        return current_result;
}

/**
 * libpolkit_context_can_caller_access_resource:
 * @pk_context: the PolicyKit context
David Zeuthen's avatar
David Zeuthen committed
614
 * @action: the type of access to check for
615
616
617
618
619
620
621
622
623
624
 * @resource: the resource in question
 * @caller: the resource in question
 *
 * Determine if a given caller can access a given resource in a given way.
 *
 * Returns: A #PolKitResult specifying if, and how, the caller can
 * access the resource in the given way
 */
PolKitResult
libpolkit_context_can_caller_access_resource (PolKitContext   *pk_context,
David Zeuthen's avatar
David Zeuthen committed
625
                                              PolKitAction *action,
626
627
628
                                              PolKitResource  *resource,
                                              PolKitCaller    *caller)
{
David Zeuthen's avatar
David Zeuthen committed
629
630
        PolKitPolicyCache *cache;
        PolKitPolicyFileEntry *pfe;
631
632
633
634
635
636
        PolKitResult current_result;
        PolKitModuleControl current_control;
        GSList *i;

        current_result = LIBPOLKIT_RESULT_NO;

David Zeuthen's avatar
David Zeuthen committed
637
        cache = libpolkit_context_get_policy_cache (pk_context);
638
639
640
641
        if (cache == NULL)
                goto out;

        _pk_debug ("entering libpolkit_can_caller_access_resource()");
David Zeuthen's avatar
David Zeuthen committed
642
        libpolkit_action_debug (action);
643
644
645
        libpolkit_resource_debug (resource);
        libpolkit_caller_debug (caller);

David Zeuthen's avatar
David Zeuthen committed
646
        pfe = libpolkit_policy_cache_get_entry (cache, action);
647
        if (pfe == NULL) {
David Zeuthen's avatar
David Zeuthen committed
648
649
650
                char *action_name;
                if (!libpolkit_action_get_action_id (action, &action_name)) {
                        g_warning ("given action has no name");
651
                } else {
David Zeuthen's avatar
David Zeuthen committed
652
                        g_warning ("no action with name '%s'", action_name);
653
                }
David Zeuthen's avatar
David Zeuthen committed
654
                current_result = LIBPOLKIT_RESULT_UNKNOWN_ACTION;
655
656
657
                goto out;
        }

David Zeuthen's avatar
David Zeuthen committed
658
        libpolkit_policy_file_entry_debug (pfe);
659

David Zeuthen's avatar
David Zeuthen committed
660
        current_result = LIBPOLKIT_RESULT_UNKNOWN_ACTION;
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
        current_control = LIBPOLKIT_MODULE_CONTROL_ADVISE; /* start with advise */

        /* visit modules */
        for (i = pk_context->modules; i != NULL; i = g_slist_next (i)) {
                PolKitModuleInterface *module_interface = i->data;
                PolKitModuleCanCallerAccessResource func;

                func = libpolkit_module_get_func_can_caller_access_resource (module_interface);
                if (func != NULL) {
                        PolKitModuleControl module_control;
                        PolKitResult module_result;

                        _pk_debug ("Asking module '%s'", libpolkit_module_get_name (module_interface));

                        module_control = libpolkit_module_interface_get_control (module_interface);
676
677
678
679

                        if (libpolkit_module_interface_check_builtin_confinement_for_caller (
                                    module_interface,
                                    pk_context,
David Zeuthen's avatar
David Zeuthen committed
680
                                    action,
681
682
683
                                    resource,
                                    caller)) {
                                /* module is confined by built-in options */
David Zeuthen's avatar
David Zeuthen committed
684
                                module_result = LIBPOLKIT_RESULT_UNKNOWN_ACTION;
685
686
687
688
689
                                _pk_debug ("Module '%s' confined by built-in's", 
                                           libpolkit_module_get_name (module_interface));
                        } else {
                                module_result = func (module_interface,
                                                      pk_context,
David Zeuthen's avatar
David Zeuthen committed
690
                                                      action, 
691
692
693
                                                      resource, 
                                                      caller);
                        }
694

David Zeuthen's avatar
David Zeuthen committed
695
                        /* if a module returns _UNKNOWN_ACTION, it means that it doesn't
696
                         * have an opinion about the query; e.g. polkit-module-allow-all(8)
David Zeuthen's avatar
David Zeuthen committed
697
                         * will return this if it's confined to only consider certain actions
698
699
                         * or certain users.
                         */
David Zeuthen's avatar
David Zeuthen committed
700
                        if (module_result != LIBPOLKIT_RESULT_UNKNOWN_ACTION) {
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722

                                if (current_control == LIBPOLKIT_MODULE_CONTROL_ADVISE &&
                                    module_control == LIBPOLKIT_MODULE_CONTROL_ADVISE) {

                                        /* take the less strict result */
                                        if (current_result < module_result) {
                                                current_result = module_result;
                                        }

                                } else if (current_control == LIBPOLKIT_MODULE_CONTROL_ADVISE &&
                                           module_control == LIBPOLKIT_MODULE_CONTROL_MANDATORY) {
                                        
                                        /* here we just override */
                                        current_result = module_result;

                                        /* we are now in mandatory mode */
                                        current_control = LIBPOLKIT_MODULE_CONTROL_MANDATORY;
                                }
                        }
                }
        }

David Zeuthen's avatar
David Zeuthen committed
723
724
        /* Never return UNKNOWN_ACTION to user */
        if (current_result == LIBPOLKIT_RESULT_UNKNOWN_ACTION)
725
726
727
728
729
                current_result = LIBPOLKIT_RESULT_NO;
out:
        _pk_debug ("... result was %s", libpolkit_result_to_string_representation (current_result));
        return current_result;
}