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