2003-04-13 Havoc Pennington <hp@pobox.com>
[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   switch (rule->type)
46     {
47     case BUS_POLICY_RULE_USER:
48       rule->d.user.uid = DBUS_UID_UNSET;
49       break;
50     case BUS_POLICY_RULE_GROUP:
51       rule->d.group.gid = DBUS_GID_UNSET;
52       break;
53     case BUS_POLICY_RULE_SEND:
54     case BUS_POLICY_RULE_RECEIVE:
55     case BUS_POLICY_RULE_OWN:
56       break;
57     }
58   
59   return rule;
60 }
61
62 void
63 bus_policy_rule_ref (BusPolicyRule *rule)
64 {
65   _dbus_assert (rule->refcount > 0);
66
67   rule->refcount += 1;
68 }
69
70 void
71 bus_policy_rule_unref (BusPolicyRule *rule)
72 {
73   _dbus_assert (rule->refcount > 0);
74
75   rule->refcount -= 1;
76
77   if (rule->refcount == 0)
78     {
79       switch (rule->type)
80         {
81         case BUS_POLICY_RULE_SEND:
82           dbus_free (rule->d.send.message_name);
83           dbus_free (rule->d.send.destination);
84           break;
85         case BUS_POLICY_RULE_RECEIVE:
86           dbus_free (rule->d.receive.message_name);
87           dbus_free (rule->d.receive.origin);
88           break;
89         case BUS_POLICY_RULE_OWN:
90           dbus_free (rule->d.own.service_name);
91           break;
92         case BUS_POLICY_RULE_USER:
93           dbus_free (rule->d.user.user);
94           break;
95         case BUS_POLICY_RULE_GROUP:
96           dbus_free (rule->d.group.group);
97           break;
98         }
99       
100       dbus_free (rule);
101     }
102 }
103
104 struct BusPolicy
105 {
106   int refcount;
107
108   DBusList *default_rules;       /**< Default policy rules */
109   DBusList *mandatory_rules;     /**< Mandatory policy rules */
110   DBusHashTable *rules_by_uid;   /**< per-UID policy rules */
111   DBusHashTable *rules_by_gid;   /**< per-GID policy rules */
112 };
113
114 static void
115 free_rule_func (void *data,
116                 void *user_data)
117 {
118   BusPolicyRule *rule = data;
119
120   bus_policy_rule_unref (rule);
121 }
122
123 static void
124 free_rule_list_func (void *data)
125 {
126   DBusList **list = data;
127
128   _dbus_list_foreach (list, free_rule_func, NULL);
129   
130   _dbus_list_clear (list);
131
132   dbus_free (list);
133 }
134
135 BusPolicy*
136 bus_policy_new (void)
137 {
138   BusPolicy *policy;
139
140   policy = dbus_new0 (BusPolicy, 1);
141   if (policy == NULL)
142     return NULL;
143
144   policy->refcount = 1;
145   
146   policy->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
147                                                NULL,
148                                                free_rule_list_func);
149   if (policy->rules_by_uid == NULL)
150     goto failed;
151
152   policy->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG,
153                                                NULL,
154                                                free_rule_list_func);
155   if (policy->rules_by_gid == NULL)
156     goto failed;
157   
158   return policy;
159   
160  failed:
161   bus_policy_unref (policy);
162   return NULL;
163 }
164
165 void
166 bus_policy_ref (BusPolicy *policy)
167 {
168   _dbus_assert (policy->refcount > 0);
169
170   policy->refcount += 1;
171 }
172
173 void
174 bus_policy_unref (BusPolicy *policy)
175 {
176   _dbus_assert (policy->refcount > 0);
177
178   policy->refcount -= 1;
179
180   if (policy->refcount == 0)
181     {
182       _dbus_list_foreach (&policy->default_rules, free_rule_func, NULL);
183       _dbus_list_clear (&policy->default_rules);
184
185       _dbus_list_foreach (&policy->mandatory_rules, free_rule_func, NULL);
186       _dbus_list_clear (&policy->mandatory_rules);
187       
188       if (policy->rules_by_uid)
189         {
190           _dbus_hash_table_unref (policy->rules_by_uid);
191           policy->rules_by_uid = NULL;
192         }
193
194       if (policy->rules_by_gid)
195         {
196           _dbus_hash_table_unref (policy->rules_by_gid);
197           policy->rules_by_gid = NULL;
198         }
199       
200       dbus_free (policy);
201     }
202 }
203
204 static dbus_bool_t
205 add_list_to_client (DBusList        **list,
206                     BusClientPolicy  *client)
207 {
208   DBusList *link;
209
210   link = _dbus_list_get_first_link (list);
211   while (link != NULL)
212     {
213       BusPolicyRule *rule = link->data;
214       link = _dbus_list_get_next_link (list, link);
215
216       switch (rule->type)
217         {
218         case BUS_POLICY_RULE_USER:
219         case BUS_POLICY_RULE_GROUP:
220           /* These aren't per-connection policies */
221           break;
222
223         case BUS_POLICY_RULE_OWN:
224         case BUS_POLICY_RULE_SEND:
225         case BUS_POLICY_RULE_RECEIVE:
226           /* These are per-connection */
227           if (!bus_client_policy_append_rule (client, rule))
228             return FALSE;
229           break;
230         }
231     }
232   
233   return TRUE;
234 }
235
236 BusClientPolicy*
237 bus_policy_create_client_policy (BusPolicy      *policy,
238                                  DBusConnection *connection)
239 {
240   BusClientPolicy *client;
241   unsigned long uid;
242   DBusList **list;
243
244   _dbus_assert (dbus_connection_get_is_authenticated (connection));
245   
246   client = bus_client_policy_new ();
247   if (client == NULL)
248     return NULL;
249
250   if (!add_list_to_client (&policy->default_rules,
251                            client))
252     goto failed;
253
254   /* we avoid the overhead of looking up user's groups
255    * if we don't have any group rules anyway
256    */
257   if (_dbus_hash_table_get_n_entries (policy->rules_by_gid) > 0)
258     {
259       const unsigned long *groups;
260       int n_groups;
261       int i;
262       
263       if (!bus_connection_get_groups (connection, &groups, &n_groups))
264         goto failed;
265       
266       i = 0;
267       while (i < n_groups)
268         {
269           list = _dbus_hash_table_lookup_ulong (policy->rules_by_gid,
270                                                 groups[i]);
271           
272           if (list != NULL)
273             {
274               if (!add_list_to_client (list, client))
275                 goto failed;
276             }
277           
278           ++i;
279         }
280     }
281
282   if (!dbus_connection_get_unix_user (connection, &uid))
283     goto failed;
284
285   list = _dbus_hash_table_lookup_ulong (policy->rules_by_uid,
286                                         uid);
287
288   if (!add_list_to_client (list, client))
289     goto failed;
290   
291   if (!add_list_to_client (&policy->mandatory_rules,
292                            client))
293     goto failed;
294
295   bus_client_policy_optimize (client);
296   
297   return client;
298   
299  failed:
300   bus_client_policy_unref (client);
301   return NULL;
302 }
303
304 static dbus_bool_t
305 list_allows_user (dbus_bool_t           def,
306                   DBusList            **list,
307                   unsigned long         uid,
308                   const unsigned long  *group_ids,
309                   int                   n_group_ids)
310 {
311   DBusList *link;
312   dbus_bool_t allowed;
313
314   /* FIXME there's currently no handling of wildcard user/group rules.
315    */
316   
317   allowed = def;
318
319   link = _dbus_list_get_first_link (list);
320   while (link != NULL)
321     {
322       BusPolicyRule *rule = link->data;
323       link = _dbus_list_get_next_link (list, link);
324       
325       if (rule->type == BUS_POLICY_RULE_USER)
326         {
327           if (rule->d.user.uid != uid)
328             continue;
329         }
330       else if (rule->type == BUS_POLICY_RULE_GROUP)
331         {
332           int i;
333
334           i = 0;
335           while (i < n_group_ids)
336             {
337               if (rule->d.group.gid == group_ids[i])
338                 break;
339               ++i;
340             }
341
342           if (i == n_group_ids)
343             continue;
344         }
345       else
346         continue;
347
348       allowed = rule->allow;
349     }
350   
351   return allowed;
352 }
353
354 dbus_bool_t
355 bus_policy_allow_user (BusPolicy    *policy,
356                        unsigned long uid)
357 {
358   dbus_bool_t allowed;
359   unsigned long *group_ids;
360   int n_group_ids;
361
362   /* On OOM or error we always reject the user */
363   if (!_dbus_get_groups (uid, &group_ids, &n_group_ids))
364     {
365       _dbus_verbose ("Did not get any groups for UID %lu\n",
366                      uid);
367       return FALSE;
368     }
369   
370   allowed = FALSE;
371
372   allowed = list_allows_user (allowed,
373                               &policy->default_rules,
374                               uid,
375                               group_ids, n_group_ids);
376
377   allowed = list_allows_user (allowed,
378                               &policy->mandatory_rules,
379                               uid,
380                               group_ids, n_group_ids);
381
382   dbus_free (group_ids);
383
384   return allowed;
385 }
386
387 dbus_bool_t
388 bus_policy_append_default_rule (BusPolicy      *policy,
389                                 BusPolicyRule  *rule)
390 {
391   if (!_dbus_list_append (&policy->default_rules, rule))
392     return FALSE;
393
394   bus_policy_rule_ref (rule);
395
396   return TRUE;
397 }
398
399 dbus_bool_t
400 bus_policy_append_mandatory_rule (BusPolicy      *policy,
401                                   BusPolicyRule  *rule)
402 {
403   if (!_dbus_list_append (&policy->mandatory_rules, rule))
404     return FALSE;
405
406   bus_policy_rule_ref (rule);
407
408   return TRUE;
409 }
410
411 static DBusList**
412 get_list (DBusHashTable *hash,
413           unsigned long  key)
414 {
415   DBusList **list;
416
417   list = _dbus_hash_table_lookup_ulong (hash, key);
418
419   if (list == NULL)
420     {
421       list = dbus_new0 (DBusList*, 1);
422       if (list == NULL)
423         return NULL;
424
425       if (!_dbus_hash_table_insert_ulong (hash, key, list))
426         {
427           dbus_free (list);
428           return NULL;
429         }
430     }
431
432   return list;
433 }
434
435 dbus_bool_t
436 bus_policy_append_user_rule (BusPolicy      *policy,
437                              dbus_uid_t      uid,
438                              BusPolicyRule  *rule)
439 {
440   DBusList **list;
441
442   list = get_list (policy->rules_by_uid, uid);
443
444   if (list == NULL)
445     return FALSE;
446
447   if (!_dbus_list_append (list, rule))
448     return FALSE;
449
450   bus_policy_rule_ref (rule);
451
452   return TRUE;
453 }
454
455 dbus_bool_t
456 bus_policy_append_group_rule (BusPolicy      *policy,
457                               dbus_gid_t      gid,
458                               BusPolicyRule  *rule)
459 {
460   DBusList **list;
461
462   list = get_list (policy->rules_by_gid, gid);
463
464   if (list == NULL)
465     return FALSE;
466
467   if (!_dbus_list_append (list, rule))
468     return FALSE;
469
470   bus_policy_rule_ref (rule);
471
472   return TRUE;
473 }
474
475 struct BusClientPolicy
476 {
477   int refcount;
478
479   DBusList *rules;
480 };
481
482 BusClientPolicy*
483 bus_client_policy_new (void)
484 {
485   BusClientPolicy *policy;
486
487   policy = dbus_new0 (BusClientPolicy, 1);
488   if (policy == NULL)
489     return NULL;
490
491   policy->refcount = 1;
492
493   return policy;
494 }
495
496 void
497 bus_client_policy_ref (BusClientPolicy *policy)
498 {
499   _dbus_assert (policy->refcount > 0);
500
501   policy->refcount += 1;
502 }
503
504 static void
505 rule_unref_foreach (void *data,
506                     void *user_data)
507 {
508   BusPolicyRule *rule = data;
509
510   bus_policy_rule_unref (rule);
511 }
512
513 void
514 bus_client_policy_unref (BusClientPolicy *policy)
515 {
516   _dbus_assert (policy->refcount > 0);
517
518   policy->refcount -= 1;
519
520   if (policy->refcount == 0)
521     {
522       _dbus_list_foreach (&policy->rules,
523                           rule_unref_foreach,
524                           NULL);
525
526       _dbus_list_clear (&policy->rules);
527       
528       dbus_free (policy);
529     }
530 }
531
532 static void
533 remove_rules_by_type_up_to (BusClientPolicy   *policy,
534                             BusPolicyRuleType  type,
535                             DBusList          *up_to)
536 {
537   DBusList *link;
538
539   link = _dbus_list_get_first (&policy->rules);
540   while (link != up_to)
541     {
542       BusPolicyRule *rule = link->data;
543       DBusList *next = _dbus_list_get_next_link (&policy->rules, link);
544
545       bus_policy_rule_unref (rule);
546       _dbus_list_remove_link (&policy->rules, link);
547
548       link = next;
549     }
550 }
551
552 void
553 bus_client_policy_optimize (BusClientPolicy *policy)
554 {
555   DBusList *link;
556
557   /* The idea here is that if we have:
558    * 
559    * <allow send="foo"/>
560    * <deny send="*"/>
561    *
562    * (for example) the deny will always override the allow.  So we
563    * delete the allow. Ditto for deny followed by allow, etc. This is
564    * a dumb thing to put in a config file, but the <include> feature
565    * of files allows for an "inheritance and override" pattern where
566    * it could make sense. If an included file wants to "start over"
567    * with a blanket deny, no point keeping the rules from the parent
568    * file.
569    */
570
571   _dbus_verbose ("Optimizing policy with %d rules\n",
572                  _dbus_list_get_length (&policy->rules));
573   
574   link = _dbus_list_get_first (&policy->rules);
575   while (link != NULL)
576     {
577       BusPolicyRule *rule = link->data;
578       DBusList *next = _dbus_list_get_next_link (&policy->rules, link);
579       dbus_bool_t remove_preceding;
580
581       remove_preceding = FALSE;
582       
583       switch (rule->type)
584         {
585         case BUS_POLICY_RULE_SEND:
586           remove_preceding =
587             rule->d.send.message_name == NULL &&
588             rule->d.send.destination == NULL;
589           break;
590         case BUS_POLICY_RULE_RECEIVE:
591           remove_preceding =
592             rule->d.receive.message_name == NULL &&
593             rule->d.receive.origin == NULL;
594           break;
595         case BUS_POLICY_RULE_OWN:
596           remove_preceding =
597             rule->d.own.service_name == NULL;
598           break;
599         case BUS_POLICY_RULE_USER:
600         case BUS_POLICY_RULE_GROUP:
601           _dbus_assert_not_reached ("invalid rule");
602           break;
603         }
604                 
605       if (remove_preceding)
606         remove_rules_by_type_up_to (policy, rule->type,
607                                     link);
608       
609       link = next;
610     }
611
612   _dbus_verbose ("After optimization, policy has %d rules\n",
613                  _dbus_list_get_length (&policy->rules));
614 }
615
616 dbus_bool_t
617 bus_client_policy_append_rule (BusClientPolicy *policy,
618                                BusPolicyRule   *rule)
619 {
620   if (!_dbus_list_append (&policy->rules, rule))
621     return FALSE;
622
623   bus_policy_rule_ref (rule);
624
625   return TRUE;
626 }
627
628 dbus_bool_t
629 bus_client_policy_check_can_send (BusClientPolicy *policy,
630                                   BusRegistry     *registry,
631                                   DBusConnection  *receiver,
632                                   DBusMessage     *message)
633 {
634   DBusList *link;
635   dbus_bool_t allowed;
636   
637   /* policy->rules is in the order the rules appeared
638    * in the config file, i.e. last rule that applies wins
639    */
640
641   allowed = FALSE;
642   link = _dbus_list_get_first (&policy->rules);
643   while (link != NULL)
644     {
645       BusPolicyRule *rule = link->data;
646
647       link = _dbus_list_get_next_link (&policy->rules, link);
648       
649       /* Rule is skipped if it specifies a different
650        * message name from the message, or a different
651        * destination from the message
652        */
653       
654       if (rule->type != BUS_POLICY_RULE_SEND)
655         continue;
656
657       if (rule->d.send.message_name != NULL)
658         {
659           if (!dbus_message_name_is (message,
660                                      rule->d.send.message_name))
661             continue;
662         }
663
664       if (rule->d.send.destination != NULL)
665         {
666           /* receiver can be NULL for messages that are sent to the
667            * message bus itself, we check the strings in that case as
668            * built-in services don't have a DBusConnection but messages
669            * to them have a destination service name.
670            */
671           if (receiver == NULL)
672             {
673               if (!dbus_message_sender_is (message,
674                                            rule->d.send.destination))
675                 continue;
676             }
677           else
678             {
679               DBusString str;
680               BusService *service;
681               
682               _dbus_string_init_const (&str, rule->d.send.destination);
683               
684               service = bus_registry_lookup (registry, &str);
685               if (service == NULL)
686                 continue;
687
688               if (!bus_service_has_owner (service, receiver))
689                 continue;
690             }
691         }
692
693       /* Use this rule */
694       allowed = rule->allow;
695     }
696
697   return allowed;
698 }
699
700 dbus_bool_t
701 bus_client_policy_check_can_receive (BusClientPolicy *policy,
702                                      BusRegistry     *registry,
703                                      DBusConnection  *sender,
704                                      DBusMessage     *message)
705 {
706   DBusList *link;
707   dbus_bool_t allowed;
708   
709   /* policy->rules is in the order the rules appeared
710    * in the config file, i.e. last rule that applies wins
711    */
712
713   allowed = FALSE;
714   link = _dbus_list_get_first (&policy->rules);
715   while (link != NULL)
716     {
717       BusPolicyRule *rule = link->data;
718
719       link = _dbus_list_get_next_link (&policy->rules, link);
720       
721       /* Rule is skipped if it specifies a different
722        * message name from the message, or a different
723        * origin from the message
724        */
725       
726       if (rule->type != BUS_POLICY_RULE_RECEIVE)
727         continue;
728
729       if (rule->d.receive.message_name != NULL)
730         {
731           if (!dbus_message_name_is (message,
732                                      rule->d.receive.message_name))
733             continue;
734         }
735
736       if (rule->d.receive.origin != NULL)
737         {          
738           /* sender can be NULL for messages that originate from the
739            * message bus itself, we check the strings in that case as
740            * built-in services don't have a DBusConnection but will
741            * still set the sender on their messages.
742            */
743           if (sender == NULL)
744             {
745               if (!dbus_message_sender_is (message,
746                                            rule->d.receive.origin))
747                 continue;
748             }
749           else
750             {
751               BusService *service;
752               DBusString str;
753
754               _dbus_string_init_const (&str, rule->d.receive.origin);
755               
756               service = bus_registry_lookup (registry, &str);
757               
758               if (service == NULL)
759                 continue;
760
761               if (!bus_service_has_owner (service, sender))
762                 continue;
763             }
764         }
765
766       /* Use this rule */
767       allowed = rule->allow;
768     }
769
770   return allowed;
771 }
772
773 dbus_bool_t
774 bus_client_policy_check_can_own (BusClientPolicy  *policy,
775                                  DBusConnection   *connection,
776                                  const DBusString *service_name)
777 {
778   DBusList *link;
779   dbus_bool_t allowed;
780   
781   /* policy->rules is in the order the rules appeared
782    * in the config file, i.e. last rule that applies wins
783    */
784
785   allowed = FALSE;
786   link = _dbus_list_get_first (&policy->rules);
787   while (link != NULL)
788     {
789       BusPolicyRule *rule = link->data;
790
791       link = _dbus_list_get_next_link (&policy->rules, link);
792       
793       /* Rule is skipped if it specifies a different service name from
794        * the desired one.
795        */
796       
797       if (rule->type != BUS_POLICY_RULE_OWN)
798         continue;
799
800       if (rule->d.own.service_name != NULL)
801         {
802           if (!_dbus_string_equal_c_str (service_name,
803                                          rule->d.own.service_name))
804             continue;
805         }
806
807       /* Use this rule */
808       allowed = rule->allow;
809     }
810
811   return allowed;
812 }
813
814 #ifdef DBUS_BUILD_TESTS
815
816 dbus_bool_t
817 bus_policy_test (const DBusString *test_data_dir)
818 {
819   /* This doesn't do anything for now because I decided to do it in
820    * dispatch.c instead by having some of the clients in dispatch.c
821    * have particular policies applied to them.
822    */
823   
824   return TRUE;
825 }
826
827 #endif /* DBUS_BUILD_TESTS */