2003-03-24 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / bus / policy.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* policy.c  Policies for what a connection can do
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-internals.h>
29
30 BusPolicyRule*
31 bus_policy_rule_new (BusPolicyRuleType type,
32                      dbus_bool_t       allow)
33 {
34   BusPolicyRule *rule;
35
36   rule = dbus_new0 (BusPolicyRule, 1);
37   if (rule == NULL)
38     return NULL;
39
40   rule->type = type;
41   rule->refcount = 1;
42   rule->allow = allow;
43
44   return rule;
45 }
46
47 void
48 bus_policy_rule_ref (BusPolicyRule *rule)
49 {
50   _dbus_assert (rule->refcount > 0);
51
52   rule->refcount += 1;
53 }
54
55 void
56 bus_policy_rule_unref (BusPolicyRule *rule)
57 {
58   _dbus_assert (rule->refcount > 0);
59
60   rule->refcount -= 1;
61
62   if (rule->refcount == 0)
63     {
64       switch (rule->type)
65         {
66         case BUS_POLICY_RULE_SEND:
67           dbus_free (rule->d.send.message_name);
68           dbus_free (rule->d.send.destination);
69           break;
70         case BUS_POLICY_RULE_RECEIVE:
71           dbus_free (rule->d.receive.message_name);
72           dbus_free (rule->d.receive.origin);
73           break;
74         case BUS_POLICY_RULE_OWN:
75           dbus_free (rule->d.own.service_name);
76           break;
77         case BUS_POLICY_RULE_USER:
78         case BUS_POLICY_RULE_GROUP:
79           _dbus_assert_not_reached ("invalid rule");
80           break;
81         }
82       
83       dbus_free (rule);
84     }
85 }
86
87 struct BusPolicy
88 {
89   int refcount;
90
91   DBusList *rules;
92 };
93
94 BusPolicy*
95 bus_policy_new (void)
96 {
97   BusPolicy *policy;
98
99   policy = dbus_new0 (BusPolicy, 1);
100   if (policy == NULL)
101     return NULL;
102
103   policy->refcount = 1;
104
105   return policy;
106 }
107
108 void
109 bus_policy_ref (BusPolicy *policy)
110 {
111   _dbus_assert (policy->refcount > 0);
112
113   policy->refcount += 1;
114 }
115
116 static void
117 rule_unref_foreach (void *data,
118                     void *user_data)
119 {
120   BusPolicyRule *rule = data;
121
122   bus_policy_rule_unref (rule);
123 }
124
125 void
126 bus_policy_unref (BusPolicy *policy)
127 {
128   _dbus_assert (policy->refcount > 0);
129
130   policy->refcount -= 1;
131
132   if (policy->refcount == 0)
133     {
134       _dbus_list_foreach (&policy->rules,
135                           rule_unref_foreach,
136                           NULL);
137
138       _dbus_list_clear (&policy->rules);
139       
140       dbus_free (policy);
141     }
142 }
143
144 static void
145 remove_rules_by_type_up_to (BusPolicy         *policy,
146                             BusPolicyRuleType  type,
147                             DBusList          *up_to)
148 {
149   DBusList *link;
150
151   link = _dbus_list_get_first (&policy->rules);
152   while (link != up_to)
153     {
154       BusPolicyRule *rule = link->data;
155       DBusList *next = _dbus_list_get_next_link (&policy->rules, link);
156
157       bus_policy_rule_unref (rule);
158       _dbus_list_remove_link (&policy->rules, link);
159
160       link = next;
161     }
162 }
163
164 void
165 bus_policy_optimize (BusPolicy *policy)
166 {
167   DBusList *link;
168
169   /* The idea here is that if we have:
170    * 
171    * <allow send="foo"/>
172    * <deny send="*"/>
173    *
174    * (for example) the deny will always override the allow.  So we
175    * delete the allow. Ditto for deny followed by allow, etc. This is
176    * a dumb thing to put in a config file, but the <include> feature
177    * of files allows for an "inheritance and override" pattern where
178    * it could make sense. If an included file wants to "start over"
179    * with a blanket deny, no point keeping the rules from the parent
180    * file.
181    */
182
183   _dbus_verbose ("Optimizing policy with %d rules\n",
184                  _dbus_list_get_length (&policy->rules));
185   
186   link = _dbus_list_get_first (&policy->rules);
187   while (link != NULL)
188     {
189       BusPolicyRule *rule = link->data;
190       DBusList *next = _dbus_list_get_next_link (&policy->rules, link);
191       dbus_bool_t remove_preceding;
192
193       remove_preceding = FALSE;
194       
195       switch (rule->type)
196         {
197         case BUS_POLICY_RULE_SEND:
198           remove_preceding =
199             rule->d.send.message_name == NULL &&
200             rule->d.send.destination == NULL;
201           break;
202         case BUS_POLICY_RULE_RECEIVE:
203           remove_preceding =
204             rule->d.receive.message_name == NULL &&
205             rule->d.receive.origin == NULL;
206           break;
207         case BUS_POLICY_RULE_OWN:
208           remove_preceding =
209             rule->d.own.service_name == NULL;
210           break;
211         case BUS_POLICY_RULE_USER:
212         case BUS_POLICY_RULE_GROUP:
213           _dbus_assert_not_reached ("invalid rule");
214           break;
215         }
216                 
217       if (remove_preceding)
218         remove_rules_by_type_up_to (policy, rule->type,
219                                     link);
220       
221       link = next;
222     }
223
224   _dbus_verbose ("After optimization, policy has %d rules\n",
225                  _dbus_list_get_length (&policy->rules));
226 }
227
228 dbus_bool_t
229 bus_policy_append_rule (BusPolicy     *policy,
230                         BusPolicyRule *rule)
231 {
232   if (!_dbus_list_append (&policy->rules, rule))
233     return FALSE;
234
235   bus_policy_rule_ref (rule);
236
237   return TRUE;
238 }
239
240 dbus_bool_t
241 bus_policy_check_can_send (BusPolicy      *policy,
242                            BusRegistry    *registry,
243                            DBusConnection *receiver,
244                            DBusMessage    *message)
245 {
246   DBusList *link;
247   dbus_bool_t allowed;
248   
249   /* policy->rules is in the order the rules appeared
250    * in the config file, i.e. last rule that applies wins
251    */
252
253   allowed = FALSE;
254   link = _dbus_list_get_first (&policy->rules);
255   while (link != NULL)
256     {
257       BusPolicyRule *rule = link->data;
258
259       link = _dbus_list_get_next_link (&policy->rules, link);
260       
261       /* Rule is skipped if it specifies a different
262        * message name from the message, or a different
263        * destination from the message
264        */
265       
266       if (rule->type != BUS_POLICY_RULE_SEND)
267         continue;
268
269       if (rule->d.send.message_name != NULL)
270         {
271           if (!dbus_message_name_is (message,
272                                      rule->d.send.message_name))
273             continue;
274         }
275
276       if (rule->d.send.destination != NULL)
277         {
278           /* receiver can be NULL for messages that are sent to the
279            * message bus itself, we check the strings in that case as
280            * built-in services don't have a DBusConnection but messages
281            * to them have a destination service name.
282            */
283           if (receiver == NULL)
284             {
285               if (!dbus_message_sender_is (message,
286                                            rule->d.send.destination))
287                 continue;
288             }
289           else
290             {
291               DBusString str;
292               BusService *service;
293               
294               _dbus_string_init_const (&str, rule->d.send.destination);
295               
296               service = bus_registry_lookup (registry, &str);
297               if (service == NULL)
298                 continue;
299
300               if (!bus_service_has_owner (service, receiver))
301                 continue;
302             }
303         }
304
305       /* Use this rule */
306       allowed = rule->allow;
307     }
308
309   return allowed;
310 }
311
312 dbus_bool_t
313 bus_policy_check_can_receive (BusPolicy      *policy,
314                               BusRegistry    *registry,
315                               DBusConnection *sender,
316                               DBusMessage    *message)
317 {
318   DBusList *link;
319   dbus_bool_t allowed;
320   
321   /* policy->rules is in the order the rules appeared
322    * in the config file, i.e. last rule that applies wins
323    */
324
325   allowed = FALSE;
326   link = _dbus_list_get_first (&policy->rules);
327   while (link != NULL)
328     {
329       BusPolicyRule *rule = link->data;
330
331       link = _dbus_list_get_next_link (&policy->rules, link);
332       
333       /* Rule is skipped if it specifies a different
334        * message name from the message, or a different
335        * origin from the message
336        */
337       
338       if (rule->type != BUS_POLICY_RULE_RECEIVE)
339         continue;
340
341       if (rule->d.receive.message_name != NULL)
342         {
343           if (!dbus_message_name_is (message,
344                                      rule->d.receive.message_name))
345             continue;
346         }
347
348       if (rule->d.receive.origin != NULL)
349         {          
350           /* sender can be NULL for messages that originate from the
351            * message bus itself, we check the strings in that case as
352            * built-in services don't have a DBusConnection but will
353            * still set the sender on their messages.
354            */
355           if (sender == NULL)
356             {
357               if (!dbus_message_sender_is (message,
358                                            rule->d.receive.origin))
359                 continue;
360             }
361           else
362             {
363               BusService *service;
364               DBusString str;
365
366               _dbus_string_init_const (&str, rule->d.receive.origin);
367               
368               service = bus_registry_lookup (registry, &str);
369               
370               if (service == NULL)
371                 continue;
372
373               if (!bus_service_has_owner (service, sender))
374                 continue;
375             }
376         }
377
378       /* Use this rule */
379       allowed = rule->allow;
380     }
381
382   return allowed;
383 }
384
385 dbus_bool_t
386 bus_policy_check_can_own (BusPolicy        *policy,
387                           DBusConnection   *connection,
388                           const DBusString *service_name)
389 {
390   DBusList *link;
391   dbus_bool_t allowed;
392   
393   /* policy->rules is in the order the rules appeared
394    * in the config file, i.e. last rule that applies wins
395    */
396
397   allowed = FALSE;
398   link = _dbus_list_get_first (&policy->rules);
399   while (link != NULL)
400     {
401       BusPolicyRule *rule = link->data;
402
403       link = _dbus_list_get_next_link (&policy->rules, link);
404       
405       /* Rule is skipped if it specifies a different service name from
406        * the desired one.
407        */
408       
409       if (rule->type != BUS_POLICY_RULE_OWN)
410         continue;
411
412       if (rule->d.own.service_name != NULL)
413         {
414           if (!_dbus_string_equal_c_str (service_name,
415                                          rule->d.own.service_name))
416             continue;
417         }
418
419       /* Use this rule */
420       allowed = rule->allow;
421     }
422
423   return allowed;
424 }
425
426 #ifdef DBUS_BUILD_TESTS
427
428 dbus_bool_t
429 bus_policy_test (const DBusString *test_data_dir)
430 {
431   /* This doesn't do anything for now because I decided to do it in
432    * dispatch.c instead by having some of the clients in dispatch.c
433    * have particular policies applied to them.
434    */
435   
436   return TRUE;
437 }
438
439 #endif /* DBUS_BUILD_TESTS */