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