cff2509c7b2b9a006da0fb5f0485e517e3628359
[platform/upstream/dbus.git] / bus / policy.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* policy.c  Bus security policy
3  *
4  * Copyright (C) 2003  Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "policy.h"
25 #include "services.h"
26 #include "test.h"
27 #include <dbus/dbus-list.h>
28 #include <dbus/dbus-hash.h>
29 #include <dbus/dbus-internals.h>
30
31 BusPolicyRule*
32 bus_policy_rule_new (BusPolicyRuleType type,
33                      dbus_bool_t       allow)
34 {
35   BusPolicyRule *rule;
36
37   rule = dbus_new0 (BusPolicyRule, 1);
38   if (rule == NULL)
39     return NULL;
40
41   rule->type = type;
42   rule->refcount = 1;
43   rule->allow = allow;
44
45   return rule;
46 }
47
48 void
49 bus_policy_rule_ref (BusPolicyRule *rule)
50 {
51   _dbus_assert (rule->refcount > 0);
52
53   rule->refcount += 1;
54 }
55
56 void
57 bus_policy_rule_unref (BusPolicyRule *rule)
58 {
59   _dbus_assert (rule->refcount > 0);
60
61   rule->refcount -= 1;
62
63   if (rule->refcount == 0)
64     {
65       switch (rule->type)
66         {
67         case BUS_POLICY_RULE_SEND:
68           dbus_free (rule->d.send.message_name);
69           dbus_free (rule->d.send.destination);
70           break;
71         case BUS_POLICY_RULE_RECEIVE:
72           dbus_free (rule->d.receive.message_name);
73           dbus_free (rule->d.receive.origin);
74           break;
75         case BUS_POLICY_RULE_OWN:
76           dbus_free (rule->d.own.service_name);
77           break;
78         case BUS_POLICY_RULE_USER:
79         case BUS_POLICY_RULE_GROUP:
80           _dbus_assert_not_reached ("invalid rule");
81           break;
82         }
83       
84       dbus_free (rule);
85     }
86 }
87
88 struct BusPolicy
89 {
90   int refcount;
91
92   DBusList *default_rules;       /**< Default policy rules */
93   DBusList *mandatory_rules;     /**< Mandatory policy rules */
94   DBusHashTable *rules_by_uid;   /**< per-UID policy rules */
95   DBusHashTable *rules_by_gid;   /**< per-GID policy rules */
96 };
97
98 static void
99 free_rule_func (void *data,
100                 void *user_data)
101 {
102   BusPolicyRule *rule = data;
103
104   bus_policy_rule_unref (rule);
105 }
106
107 static void
108 free_rule_list_func (void *data)
109 {
110   DBusList **list = data;
111
112   _dbus_list_foreach (list, free_rule_func, NULL);
113   
114   _dbus_list_clear (list);
115
116   dbus_free (list);
117 }
118
119 BusPolicy*
120 bus_policy_new (void)
121 {
122   BusPolicy *policy;
123
124   policy = dbus_new0 (BusPolicy, 1);
125   if (policy == NULL)
126     return NULL;
127
128   policy->refcount = 1;
129   
130   policy->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
131                                                NULL,
132                                                free_rule_list_func);
133   if (policy->rules_by_uid == NULL)
134     goto failed;
135
136   policy->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG,
137                                                NULL,
138                                                free_rule_list_func);
139   if (policy->rules_by_gid == NULL)
140     goto failed;
141   
142   return policy;
143   
144  failed:
145   bus_policy_unref (policy);
146   return NULL;
147 }
148
149 void
150 bus_policy_ref (BusPolicy *policy)
151 {
152   _dbus_assert (policy->refcount > 0);
153
154   policy->refcount += 1;
155 }
156
157 void
158 bus_policy_unref (BusPolicy *policy)
159 {
160   _dbus_assert (policy->refcount > 0);
161
162   policy->refcount -= 1;
163
164   if (policy->refcount == 0)
165     {
166       if (policy->rules_by_uid)
167         {
168           _dbus_hash_table_unref (policy->rules_by_uid);
169           policy->rules_by_uid = NULL;
170         }
171
172       if (policy->rules_by_gid)
173         {
174           _dbus_hash_table_unref (policy->rules_by_gid);
175           policy->rules_by_gid = NULL;
176         }
177       
178       dbus_free (policy);
179     }
180 }
181
182 static dbus_bool_t
183 add_list_to_client (DBusList        **list,
184                     BusClientPolicy  *client)
185 {
186   DBusList *link;
187
188   link = _dbus_list_get_first_link (list);
189   while (link != NULL)
190     {
191       BusPolicyRule *rule = link->data;
192       link = _dbus_list_get_next_link (list, link);
193
194       switch (rule->type)
195         {
196         case BUS_POLICY_RULE_USER:
197         case BUS_POLICY_RULE_GROUP:
198           /* These aren't per-connection policies */
199           break;
200
201         case BUS_POLICY_RULE_OWN:
202         case BUS_POLICY_RULE_SEND:
203         case BUS_POLICY_RULE_RECEIVE:
204           /* These are per-connection */
205           if (!bus_client_policy_append_rule (client, rule))
206             return FALSE;
207           break;
208         }
209     }
210   
211   return TRUE;
212 }
213
214 BusClientPolicy*
215 bus_policy_create_client_policy (BusPolicy      *policy,
216                                  DBusConnection *connection)
217 {
218   BusClientPolicy *client;
219   unsigned long uid;
220   DBusList **list;
221
222   _dbus_assert (dbus_connection_get_is_authenticated (connection));
223   
224   client = bus_client_policy_new ();
225   if (client == NULL)
226     return NULL;
227
228   if (!add_list_to_client (&policy->default_rules,
229                            client))
230     goto failed;
231
232   /* we avoid the overhead of looking up user's groups
233    * if we don't have any group rules anyway
234    */
235   if (_dbus_hash_table_get_n_entries (policy->rules_by_gid) > 0)
236     {
237       const unsigned long *groups;
238       int n_groups;
239       int i;
240       
241       if (!bus_connection_get_groups (connection, &groups, &n_groups))
242         goto failed;
243       
244       i = 0;
245       while (i < n_groups)
246         {
247           list = _dbus_hash_table_lookup_ulong (policy->rules_by_gid,
248                                                 groups[i]);
249           
250           if (list != NULL)
251             {
252               if (!add_list_to_client (list, client))
253                 goto failed;
254             }
255           
256           ++i;
257         }
258     }
259
260   if (!dbus_connection_get_unix_user (connection, &uid))
261     goto failed;
262
263   list = _dbus_hash_table_lookup_ulong (policy->rules_by_uid,
264                                         uid);
265
266   if (!add_list_to_client (list, client))
267     goto failed;
268   
269   if (!add_list_to_client (&policy->mandatory_rules,
270                            client))
271     goto failed;
272
273   bus_client_policy_optimize (client);
274   
275   return client;
276   
277  failed:
278   bus_client_policy_unref (client);
279   return NULL;
280 }
281
282 static dbus_bool_t
283 list_allows_user (dbus_bool_t           def,
284                   DBusList            **list,
285                   unsigned long         uid,
286                   const unsigned long  *group_ids,
287                   int                   n_group_ids)
288 {
289   DBusList *link;
290   dbus_bool_t allowed;
291   
292   allowed = def;
293
294   link = _dbus_list_get_first_link (list);
295   while (link != NULL)
296     {
297       BusPolicyRule *rule = link->data;
298       link = _dbus_list_get_next_link (list, link);
299       
300       if (rule->type == BUS_POLICY_RULE_USER)
301         {
302           if (rule->d.user.uid != uid)
303             continue;
304         }
305       else if (rule->type == BUS_POLICY_RULE_GROUP)
306         {
307           int i;
308
309           i = 0;
310           while (i < n_group_ids)
311             {
312               if (rule->d.group.gid == group_ids[i])
313                 break;
314               ++i;
315             }
316
317           if (i == n_group_ids)
318             continue;
319         }
320       else
321         continue;
322
323       allowed = rule->allow;
324     }
325   
326   return allowed;
327 }
328
329 dbus_bool_t
330 bus_policy_allow_user (BusPolicy    *policy,
331                        unsigned long uid)
332 {
333   dbus_bool_t allowed;
334   unsigned long *group_ids;
335   int n_group_ids;
336
337   /* On OOM or error we always reject the user */
338   if (!_dbus_get_groups (uid, &group_ids, &n_group_ids))
339     {
340       _dbus_verbose ("Did not get any groups for UID %lu\n",
341                      uid);
342       return FALSE;
343     }
344   
345   allowed = FALSE;
346
347   allowed = list_allows_user (allowed,
348                               &policy->default_rules,
349                               uid,
350                               group_ids, n_group_ids);
351
352   allowed = list_allows_user (allowed,
353                               &policy->mandatory_rules,
354                               uid,
355                               group_ids, n_group_ids);
356
357   dbus_free (group_ids);
358
359   return allowed;
360 }
361
362 struct BusClientPolicy
363 {
364   int refcount;
365
366   DBusList *rules;
367 };
368
369 BusClientPolicy*
370 bus_client_policy_new (void)
371 {
372   BusClientPolicy *policy;
373
374   policy = dbus_new0 (BusClientPolicy, 1);
375   if (policy == NULL)
376     return NULL;
377
378   policy->refcount = 1;
379
380   return policy;
381 }
382
383 void
384 bus_client_policy_ref (BusClientPolicy *policy)
385 {
386   _dbus_assert (policy->refcount > 0);
387
388   policy->refcount += 1;
389 }
390
391 static void
392 rule_unref_foreach (void *data,
393                     void *user_data)
394 {
395   BusPolicyRule *rule = data;
396
397   bus_policy_rule_unref (rule);
398 }
399
400 void
401 bus_client_policy_unref (BusClientPolicy *policy)
402 {
403   _dbus_assert (policy->refcount > 0);
404
405   policy->refcount -= 1;
406
407   if (policy->refcount == 0)
408     {
409       _dbus_list_foreach (&policy->rules,
410                           rule_unref_foreach,
411                           NULL);
412
413       _dbus_list_clear (&policy->rules);
414       
415       dbus_free (policy);
416     }
417 }
418
419 static void
420 remove_rules_by_type_up_to (BusClientPolicy   *policy,
421                             BusPolicyRuleType  type,
422                             DBusList          *up_to)
423 {
424   DBusList *link;
425
426   link = _dbus_list_get_first (&policy->rules);
427   while (link != up_to)
428     {
429       BusPolicyRule *rule = link->data;
430       DBusList *next = _dbus_list_get_next_link (&policy->rules, link);
431
432       bus_policy_rule_unref (rule);
433       _dbus_list_remove_link (&policy->rules, link);
434
435       link = next;
436     }
437 }
438
439 void
440 bus_client_policy_optimize (BusClientPolicy *policy)
441 {
442   DBusList *link;
443
444   /* The idea here is that if we have:
445    * 
446    * <allow send="foo"/>
447    * <deny send="*"/>
448    *
449    * (for example) the deny will always override the allow.  So we
450    * delete the allow. Ditto for deny followed by allow, etc. This is
451    * a dumb thing to put in a config file, but the <include> feature
452    * of files allows for an "inheritance and override" pattern where
453    * it could make sense. If an included file wants to "start over"
454    * with a blanket deny, no point keeping the rules from the parent
455    * file.
456    */
457
458   _dbus_verbose ("Optimizing policy with %d rules\n",
459                  _dbus_list_get_length (&policy->rules));
460   
461   link = _dbus_list_get_first (&policy->rules);
462   while (link != NULL)
463     {
464       BusPolicyRule *rule = link->data;
465       DBusList *next = _dbus_list_get_next_link (&policy->rules, link);
466       dbus_bool_t remove_preceding;
467
468       remove_preceding = FALSE;
469       
470       switch (rule->type)
471         {
472         case BUS_POLICY_RULE_SEND:
473           remove_preceding =
474             rule->d.send.message_name == NULL &&
475             rule->d.send.destination == NULL;
476           break;
477         case BUS_POLICY_RULE_RECEIVE:
478           remove_preceding =
479             rule->d.receive.message_name == NULL &&
480             rule->d.receive.origin == NULL;
481           break;
482         case BUS_POLICY_RULE_OWN:
483           remove_preceding =
484             rule->d.own.service_name == NULL;
485           break;
486         case BUS_POLICY_RULE_USER:
487         case BUS_POLICY_RULE_GROUP:
488           _dbus_assert_not_reached ("invalid rule");
489           break;
490         }
491                 
492       if (remove_preceding)
493         remove_rules_by_type_up_to (policy, rule->type,
494                                     link);
495       
496       link = next;
497     }
498
499   _dbus_verbose ("After optimization, policy has %d rules\n",
500                  _dbus_list_get_length (&policy->rules));
501 }
502
503 dbus_bool_t
504 bus_client_policy_append_rule (BusClientPolicy *policy,
505                                BusPolicyRule   *rule)
506 {
507   if (!_dbus_list_append (&policy->rules, rule))
508     return FALSE;
509
510   bus_policy_rule_ref (rule);
511
512   return TRUE;
513 }
514
515 dbus_bool_t
516 bus_client_policy_check_can_send (BusClientPolicy *policy,
517                                   BusRegistry     *registry,
518                                   DBusConnection  *receiver,
519                                   DBusMessage     *message)
520 {
521   DBusList *link;
522   dbus_bool_t allowed;
523   
524   /* policy->rules is in the order the rules appeared
525    * in the config file, i.e. last rule that applies wins
526    */
527
528   allowed = FALSE;
529   link = _dbus_list_get_first (&policy->rules);
530   while (link != NULL)
531     {
532       BusPolicyRule *rule = link->data;
533
534       link = _dbus_list_get_next_link (&policy->rules, link);
535       
536       /* Rule is skipped if it specifies a different
537        * message name from the message, or a different
538        * destination from the message
539        */
540       
541       if (rule->type != BUS_POLICY_RULE_SEND)
542         continue;
543
544       if (rule->d.send.message_name != NULL)
545         {
546           if (!dbus_message_name_is (message,
547                                      rule->d.send.message_name))
548             continue;
549         }
550
551       if (rule->d.send.destination != NULL)
552         {
553           /* receiver can be NULL for messages that are sent to the
554            * message bus itself, we check the strings in that case as
555            * built-in services don't have a DBusConnection but messages
556            * to them have a destination service name.
557            */
558           if (receiver == NULL)
559             {
560               if (!dbus_message_sender_is (message,
561                                            rule->d.send.destination))
562                 continue;
563             }
564           else
565             {
566               DBusString str;
567               BusService *service;
568               
569               _dbus_string_init_const (&str, rule->d.send.destination);
570               
571               service = bus_registry_lookup (registry, &str);
572               if (service == NULL)
573                 continue;
574
575               if (!bus_service_has_owner (service, receiver))
576                 continue;
577             }
578         }
579
580       /* Use this rule */
581       allowed = rule->allow;
582     }
583
584   return allowed;
585 }
586
587 dbus_bool_t
588 bus_client_policy_check_can_receive (BusClientPolicy *policy,
589                                      BusRegistry     *registry,
590                                      DBusConnection  *sender,
591                                      DBusMessage     *message)
592 {
593   DBusList *link;
594   dbus_bool_t allowed;
595   
596   /* policy->rules is in the order the rules appeared
597    * in the config file, i.e. last rule that applies wins
598    */
599
600   allowed = FALSE;
601   link = _dbus_list_get_first (&policy->rules);
602   while (link != NULL)
603     {
604       BusPolicyRule *rule = link->data;
605
606       link = _dbus_list_get_next_link (&policy->rules, link);
607       
608       /* Rule is skipped if it specifies a different
609        * message name from the message, or a different
610        * origin from the message
611        */
612       
613       if (rule->type != BUS_POLICY_RULE_RECEIVE)
614         continue;
615
616       if (rule->d.receive.message_name != NULL)
617         {
618           if (!dbus_message_name_is (message,
619                                      rule->d.receive.message_name))
620             continue;
621         }
622
623       if (rule->d.receive.origin != NULL)
624         {          
625           /* sender can be NULL for messages that originate from the
626            * message bus itself, we check the strings in that case as
627            * built-in services don't have a DBusConnection but will
628            * still set the sender on their messages.
629            */
630           if (sender == NULL)
631             {
632               if (!dbus_message_sender_is (message,
633                                            rule->d.receive.origin))
634                 continue;
635             }
636           else
637             {
638               BusService *service;
639               DBusString str;
640
641               _dbus_string_init_const (&str, rule->d.receive.origin);
642               
643               service = bus_registry_lookup (registry, &str);
644               
645               if (service == NULL)
646                 continue;
647
648               if (!bus_service_has_owner (service, sender))
649                 continue;
650             }
651         }
652
653       /* Use this rule */
654       allowed = rule->allow;
655     }
656
657   return allowed;
658 }
659
660 dbus_bool_t
661 bus_client_policy_check_can_own (BusClientPolicy  *policy,
662                                  DBusConnection   *connection,
663                                  const DBusString *service_name)
664 {
665   DBusList *link;
666   dbus_bool_t allowed;
667   
668   /* policy->rules is in the order the rules appeared
669    * in the config file, i.e. last rule that applies wins
670    */
671
672   allowed = FALSE;
673   link = _dbus_list_get_first (&policy->rules);
674   while (link != NULL)
675     {
676       BusPolicyRule *rule = link->data;
677
678       link = _dbus_list_get_next_link (&policy->rules, link);
679       
680       /* Rule is skipped if it specifies a different service name from
681        * the desired one.
682        */
683       
684       if (rule->type != BUS_POLICY_RULE_OWN)
685         continue;
686
687       if (rule->d.own.service_name != NULL)
688         {
689           if (!_dbus_string_equal_c_str (service_name,
690                                          rule->d.own.service_name))
691             continue;
692         }
693
694       /* Use this rule */
695       allowed = rule->allow;
696     }
697
698   return allowed;
699 }
700
701 #ifdef DBUS_BUILD_TESTS
702
703 dbus_bool_t
704 bus_policy_test (const DBusString *test_data_dir)
705 {
706   /* This doesn't do anything for now because I decided to do it in
707    * dispatch.c instead by having some of the clients in dispatch.c
708    * have particular policies applied to them.
709    */
710   
711   return TRUE;
712 }
713
714 #endif /* DBUS_BUILD_TESTS */