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