2003-03-20 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 static 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   link = _dbus_list_get_first (&policy->rules);
179   while (link != NULL)
180     {
181       BusPolicyRule *rule = link->data;
182       DBusList *next = _dbus_list_get_next_link (&policy->rules, link);
183       dbus_bool_t remove_preceding;
184
185       remove_preceding = FALSE;
186       
187       switch (rule->type)
188         {
189         case BUS_POLICY_RULE_SEND:
190           remove_preceding =
191             rule->d.send.message_name == NULL &&
192             rule->d.send.destination == NULL;
193           break;
194         case BUS_POLICY_RULE_RECEIVE:
195           remove_preceding =
196             rule->d.receive.message_name == NULL &&
197             rule->d.receive.origin == NULL;
198           break;
199         case BUS_POLICY_RULE_OWN:
200           remove_preceding =
201             rule->d.own.service_name == NULL;
202           break;
203         }
204                 
205       if (remove_preceding)
206         remove_rules_by_type_up_to (policy, rule->type,
207                                     link);
208       
209       link = next;
210     }
211 }
212
213 dbus_bool_t
214 bus_policy_check_can_send (BusPolicy      *policy,
215                            BusRegistry    *registry,
216                            DBusConnection *receiver,
217                            DBusMessage    *message)
218 {
219   DBusList *link;
220   dbus_bool_t allowed;
221   
222   /* policy->rules is in the order the rules appeared
223    * in the config file, i.e. last rule that applies wins
224    */
225
226   allowed = FALSE;
227   link = _dbus_list_get_first (&policy->rules);
228   while (link != NULL)
229     {
230       BusPolicyRule *rule = link->data;
231
232       link = _dbus_list_get_next_link (&policy->rules, link);
233       
234       /* Rule is skipped if it specifies a different
235        * message name from the message, or a different
236        * destination from the message
237        */
238       
239       if (rule->type != BUS_POLICY_RULE_SEND)
240         continue;
241
242       if (rule->d.send.message_name != NULL)
243         {
244           if (!dbus_message_name_is (message,
245                                      rule->d.send.message_name))
246             continue;
247         }
248
249       if (rule->d.send.destination != NULL)
250         {
251           /* receiver can be NULL for messages that are sent to the
252            * message bus itself, we check the strings in that case as
253            * built-in services don't have a DBusConnection but messages
254            * to them have a destination service name.
255            */
256           if (receiver == NULL)
257             {
258               if (!dbus_message_sender_is (message,
259                                            rule->d.send.destination))
260                 continue;
261             }
262           else
263             {
264               DBusString str;
265               BusService *service;
266               
267               _dbus_string_init_const (&str, rule->d.send.destination);
268               
269               service = bus_registry_lookup (registry, &str);
270               if (service == NULL)
271                 continue;
272
273               if (!bus_service_has_owner (service, receiver))
274                 continue;
275             }
276         }
277
278       /* Use this rule */
279       allowed = rule->allow;
280     }
281
282   return allowed;
283 }
284
285 dbus_bool_t
286 bus_policy_check_can_receive (BusPolicy      *policy,
287                               BusRegistry    *registry,
288                               DBusConnection *sender,
289                               DBusMessage    *message)
290 {
291   DBusList *link;
292   dbus_bool_t allowed;
293   
294   /* policy->rules is in the order the rules appeared
295    * in the config file, i.e. last rule that applies wins
296    */
297
298   allowed = FALSE;
299   link = _dbus_list_get_first (&policy->rules);
300   while (link != NULL)
301     {
302       BusPolicyRule *rule = link->data;
303
304       link = _dbus_list_get_next_link (&policy->rules, link);
305       
306       /* Rule is skipped if it specifies a different
307        * message name from the message, or a different
308        * origin from the message
309        */
310       
311       if (rule->type != BUS_POLICY_RULE_RECEIVE)
312         continue;
313
314       if (rule->d.receive.message_name != NULL)
315         {
316           if (!dbus_message_name_is (message,
317                                      rule->d.receive.message_name))
318             continue;
319         }
320
321       if (rule->d.receive.origin != NULL)
322         {          
323           /* sender can be NULL for messages that originate from the
324            * message bus itself, we check the strings in that case as
325            * built-in services don't have a DBusConnection but will
326            * still set the sender on their messages.
327            */
328           if (sender == NULL)
329             {
330               if (!dbus_message_sender_is (message,
331                                            rule->d.receive.origin))
332                 continue;
333             }
334           else
335             {
336               BusService *service;
337               DBusString str;
338
339               _dbus_string_init_const (&str, rule->d.receive.origin);
340               
341               service = bus_registry_lookup (registry, &str);
342               
343               if (service == NULL)
344                 continue;
345
346               if (!bus_service_has_owner (service, sender))
347                 continue;
348             }
349         }
350
351       /* Use this rule */
352       allowed = rule->allow;
353     }
354
355   return allowed;
356 }
357
358 dbus_bool_t
359 bus_policy_check_can_own (BusPolicy        *policy,
360                           DBusConnection   *connection,
361                           const DBusString *service_name)
362 {
363   DBusList *link;
364   dbus_bool_t allowed;
365   
366   /* policy->rules is in the order the rules appeared
367    * in the config file, i.e. last rule that applies wins
368    */
369
370   allowed = FALSE;
371   link = _dbus_list_get_first (&policy->rules);
372   while (link != NULL)
373     {
374       BusPolicyRule *rule = link->data;
375
376       link = _dbus_list_get_next_link (&policy->rules, link);
377       
378       /* Rule is skipped if it specifies a different service name from
379        * the desired one.
380        */
381       
382       if (rule->type != BUS_POLICY_RULE_OWN)
383         continue;
384
385       if (rule->d.own.service_name != NULL)
386         {
387           if (!_dbus_string_equal_c_str (service_name,
388                                          rule->d.own.service_name))
389             continue;
390         }
391
392       /* Use this rule */
393       allowed = rule->allow;
394     }
395
396   return allowed;
397 }