ad0cfaefe0f714b08fdddc1853426823ac9acdc0
[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       const 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                 goto failed;
275             }
276           
277           ++i;
278         }
279     }
280
281   if (!dbus_connection_get_unix_user (connection, &uid))
282     goto failed;
283
284   if (_dbus_hash_table_get_n_entries (policy->rules_by_uid) > 0)
285     {
286       DBusList **list;
287       
288       list = _dbus_hash_table_lookup_ulong (policy->rules_by_uid,
289                                             uid);
290
291       if (list != NULL)
292         {
293           if (!add_list_to_client (list, client))
294             goto failed;
295         }
296     }
297
298   if (!add_list_to_client (&policy->mandatory_rules,
299                            client))
300     goto failed;
301
302   bus_client_policy_optimize (client);
303   
304   return client;
305   
306  failed:
307   bus_client_policy_unref (client);
308   return NULL;
309 }
310
311 static dbus_bool_t
312 list_allows_user (dbus_bool_t           def,
313                   DBusList            **list,
314                   unsigned long         uid,
315                   const unsigned long  *group_ids,
316                   int                   n_group_ids)
317 {
318   DBusList *link;
319   dbus_bool_t allowed;
320   
321   allowed = def;
322
323   link = _dbus_list_get_first_link (list);
324   while (link != NULL)
325     {
326       BusPolicyRule *rule = link->data;
327       link = _dbus_list_get_next_link (list, link);
328       
329       if (rule->type == BUS_POLICY_RULE_USER)
330         {
331           _dbus_verbose ("List %p user rule uid="DBUS_UID_FORMAT"\n",
332                          list, rule->d.user.uid);
333           
334           if (rule->d.user.uid == DBUS_UID_UNSET)
335             ; /* '*' wildcard */
336           else if (rule->d.user.uid != uid)
337             continue;
338         }
339       else if (rule->type == BUS_POLICY_RULE_GROUP)
340         {
341           _dbus_verbose ("List %p group rule uid="DBUS_UID_FORMAT"\n",
342                          list, rule->d.user.uid);
343           
344           if (rule->d.group.gid == DBUS_GID_UNSET)
345             ;  /* '*' wildcard */
346           else
347             {
348               int i;
349               
350               i = 0;
351               while (i < n_group_ids)
352                 {
353                   if (rule->d.group.gid == group_ids[i])
354                     break;
355                   ++i;
356                 }
357               
358               if (i == n_group_ids)
359                 continue;
360             }
361         }
362       else
363         continue;
364
365       allowed = rule->allow;
366     }
367   
368   return allowed;
369 }
370
371 dbus_bool_t
372 bus_policy_allow_user (BusPolicy    *policy,
373                        unsigned long uid)
374 {
375   dbus_bool_t allowed;
376   unsigned long *group_ids;
377   int n_group_ids;
378
379   /* On OOM or error we always reject the user */
380   if (!_dbus_get_groups (uid, &group_ids, &n_group_ids, NULL))
381     {
382       _dbus_verbose ("Did not get any groups for UID %lu\n",
383                      uid);
384       return FALSE;
385     }
386   
387   allowed = FALSE;
388
389   allowed = list_allows_user (allowed,
390                               &policy->default_rules,
391                               uid,
392                               group_ids, n_group_ids);
393
394   allowed = list_allows_user (allowed,
395                               &policy->mandatory_rules,
396                               uid,
397                               group_ids, n_group_ids);
398
399   dbus_free (group_ids);
400
401   _dbus_verbose ("UID %lu allowed = %d\n", uid, allowed);
402   
403   return allowed;
404 }
405
406 dbus_bool_t
407 bus_policy_append_default_rule (BusPolicy      *policy,
408                                 BusPolicyRule  *rule)
409 {
410   if (!_dbus_list_append (&policy->default_rules, rule))
411     return FALSE;
412
413   bus_policy_rule_ref (rule);
414
415   return TRUE;
416 }
417
418 dbus_bool_t
419 bus_policy_append_mandatory_rule (BusPolicy      *policy,
420                                   BusPolicyRule  *rule)
421 {
422   if (!_dbus_list_append (&policy->mandatory_rules, rule))
423     return FALSE;
424
425   bus_policy_rule_ref (rule);
426
427   return TRUE;
428 }
429
430 static DBusList**
431 get_list (DBusHashTable *hash,
432           unsigned long  key)
433 {
434   DBusList **list;
435
436   list = _dbus_hash_table_lookup_ulong (hash, key);
437
438   if (list == NULL)
439     {
440       list = dbus_new0 (DBusList*, 1);
441       if (list == NULL)
442         return NULL;
443
444       if (!_dbus_hash_table_insert_ulong (hash, key, list))
445         {
446           dbus_free (list);
447           return NULL;
448         }
449     }
450
451   return list;
452 }
453
454 dbus_bool_t
455 bus_policy_append_user_rule (BusPolicy      *policy,
456                              dbus_uid_t      uid,
457                              BusPolicyRule  *rule)
458 {
459   DBusList **list;
460
461   list = get_list (policy->rules_by_uid, uid);
462
463   if (list == NULL)
464     return FALSE;
465
466   if (!_dbus_list_append (list, rule))
467     return FALSE;
468
469   bus_policy_rule_ref (rule);
470
471   return TRUE;
472 }
473
474 dbus_bool_t
475 bus_policy_append_group_rule (BusPolicy      *policy,
476                               dbus_gid_t      gid,
477                               BusPolicyRule  *rule)
478 {
479   DBusList **list;
480
481   list = get_list (policy->rules_by_gid, gid);
482
483   if (list == NULL)
484     return FALSE;
485
486   if (!_dbus_list_append (list, rule))
487     return FALSE;
488
489   bus_policy_rule_ref (rule);
490
491   return TRUE;
492 }
493
494 struct BusClientPolicy
495 {
496   int refcount;
497
498   DBusList *rules;
499 };
500
501 BusClientPolicy*
502 bus_client_policy_new (void)
503 {
504   BusClientPolicy *policy;
505
506   policy = dbus_new0 (BusClientPolicy, 1);
507   if (policy == NULL)
508     return NULL;
509
510   policy->refcount = 1;
511
512   return policy;
513 }
514
515 void
516 bus_client_policy_ref (BusClientPolicy *policy)
517 {
518   _dbus_assert (policy->refcount > 0);
519
520   policy->refcount += 1;
521 }
522
523 static void
524 rule_unref_foreach (void *data,
525                     void *user_data)
526 {
527   BusPolicyRule *rule = data;
528
529   bus_policy_rule_unref (rule);
530 }
531
532 void
533 bus_client_policy_unref (BusClientPolicy *policy)
534 {
535   _dbus_assert (policy->refcount > 0);
536
537   policy->refcount -= 1;
538
539   if (policy->refcount == 0)
540     {
541       _dbus_list_foreach (&policy->rules,
542                           rule_unref_foreach,
543                           NULL);
544
545       _dbus_list_clear (&policy->rules);
546       
547       dbus_free (policy);
548     }
549 }
550
551 static void
552 remove_rules_by_type_up_to (BusClientPolicy   *policy,
553                             BusPolicyRuleType  type,
554                             DBusList          *up_to)
555 {
556   DBusList *link;
557
558   link = _dbus_list_get_first_link (&policy->rules);
559   while (link != up_to)
560     {
561       BusPolicyRule *rule = link->data;
562       DBusList *next = _dbus_list_get_next_link (&policy->rules, link);
563
564       if (rule->type == type)
565         {
566           _dbus_list_remove_link (&policy->rules, link);
567           bus_policy_rule_unref (rule);
568         }
569       
570       link = next;
571     }
572 }
573
574 void
575 bus_client_policy_optimize (BusClientPolicy *policy)
576 {
577   DBusList *link;
578
579   /* The idea here is that if we have:
580    * 
581    * <allow send="foo"/>
582    * <deny send="*"/>
583    *
584    * (for example) the deny will always override the allow.  So we
585    * delete the allow. Ditto for deny followed by allow, etc. This is
586    * a dumb thing to put in a config file, but the <include> feature
587    * of files allows for an "inheritance and override" pattern where
588    * it could make sense. If an included file wants to "start over"
589    * with a blanket deny, no point keeping the rules from the parent
590    * file.
591    */
592
593   _dbus_verbose ("Optimizing policy with %d rules\n",
594                  _dbus_list_get_length (&policy->rules));
595   
596   link = _dbus_list_get_first_link (&policy->rules);
597   while (link != NULL)
598     {
599       BusPolicyRule *rule;
600       DBusList *next;
601       dbus_bool_t remove_preceding;
602
603       next = _dbus_list_get_next_link (&policy->rules, link);
604       rule = link->data;
605       
606       remove_preceding = FALSE;
607
608       _dbus_assert (rule != NULL);
609       
610       switch (rule->type)
611         {
612         case BUS_POLICY_RULE_SEND:
613           remove_preceding =
614             rule->d.send.message_name == NULL &&
615             rule->d.send.destination == NULL;
616           break;
617         case BUS_POLICY_RULE_RECEIVE:
618           remove_preceding =
619             rule->d.receive.message_name == NULL &&
620             rule->d.receive.origin == NULL;
621           break;
622         case BUS_POLICY_RULE_OWN:
623           remove_preceding =
624             rule->d.own.service_name == NULL;
625           break;
626         case BUS_POLICY_RULE_USER:
627         case BUS_POLICY_RULE_GROUP:
628           _dbus_assert_not_reached ("invalid rule");
629           break;
630         }
631
632       if (remove_preceding)
633         remove_rules_by_type_up_to (policy, rule->type,
634                                     link);
635       
636       link = next;
637     }
638
639   _dbus_verbose ("After optimization, policy has %d rules\n",
640                  _dbus_list_get_length (&policy->rules));
641 }
642
643 dbus_bool_t
644 bus_client_policy_append_rule (BusClientPolicy *policy,
645                                BusPolicyRule   *rule)
646 {
647   _dbus_verbose ("Appending rule %p with type %d to policy %p\n",
648                  rule, rule->type, policy);
649   
650   if (!_dbus_list_append (&policy->rules, rule))
651     return FALSE;
652
653   bus_policy_rule_ref (rule);
654
655   return TRUE;
656 }
657
658 dbus_bool_t
659 bus_client_policy_check_can_send (BusClientPolicy *policy,
660                                   BusRegistry     *registry,
661                                   DBusConnection  *receiver,
662                                   DBusMessage     *message)
663 {
664   DBusList *link;
665   dbus_bool_t allowed;
666   
667   /* policy->rules is in the order the rules appeared
668    * in the config file, i.e. last rule that applies wins
669    */
670
671   allowed = FALSE;
672   link = _dbus_list_get_first_link (&policy->rules);
673   while (link != NULL)
674     {
675       BusPolicyRule *rule = link->data;
676
677       link = _dbus_list_get_next_link (&policy->rules, link);
678       
679       /* Rule is skipped if it specifies a different
680        * message name from the message, or a different
681        * destination from the message
682        */
683       
684       if (rule->type != BUS_POLICY_RULE_SEND)
685         continue;
686
687       if (rule->d.send.message_name != NULL)
688         {
689           if (!dbus_message_name_is (message,
690                                      rule->d.send.message_name))
691             continue;
692         }
693
694       if (rule->d.send.destination != NULL)
695         {
696           /* receiver can be NULL for messages that are sent to the
697            * message bus itself, we check the strings in that case as
698            * built-in services don't have a DBusConnection but messages
699            * to them have a destination service name.
700            */
701           if (receiver == NULL)
702             {
703               if (!dbus_message_sender_is (message,
704                                            rule->d.send.destination))
705                 continue;
706             }
707           else
708             {
709               DBusString str;
710               BusService *service;
711               
712               _dbus_string_init_const (&str, rule->d.send.destination);
713               
714               service = bus_registry_lookup (registry, &str);
715               if (service == NULL)
716                 continue;
717
718               if (!bus_service_has_owner (service, receiver))
719                 continue;
720             }
721         }
722
723       /* Use this rule */
724       allowed = rule->allow;
725     }
726
727   return allowed;
728 }
729
730 dbus_bool_t
731 bus_client_policy_check_can_receive (BusClientPolicy *policy,
732                                      BusRegistry     *registry,
733                                      DBusConnection  *sender,
734                                      DBusMessage     *message)
735 {
736   DBusList *link;
737   dbus_bool_t allowed;
738   
739   /* policy->rules is in the order the rules appeared
740    * in the config file, i.e. last rule that applies wins
741    */
742
743   allowed = FALSE;
744   link = _dbus_list_get_first_link (&policy->rules);
745   while (link != NULL)
746     {
747       BusPolicyRule *rule = link->data;
748
749       link = _dbus_list_get_next_link (&policy->rules, link);
750       
751       /* Rule is skipped if it specifies a different
752        * message name from the message, or a different
753        * origin from the message
754        */
755       
756       if (rule->type != BUS_POLICY_RULE_RECEIVE)
757         continue;
758
759       if (rule->d.receive.message_name != NULL)
760         {
761           if (!dbus_message_name_is (message,
762                                      rule->d.receive.message_name))
763             continue;
764         }
765
766       if (rule->d.receive.origin != NULL)
767         {          
768           /* sender can be NULL for messages that originate from the
769            * message bus itself, we check the strings in that case as
770            * built-in services don't have a DBusConnection but will
771            * still set the sender on their messages.
772            */
773           if (sender == NULL)
774             {
775               if (!dbus_message_sender_is (message,
776                                            rule->d.receive.origin))
777                 continue;
778             }
779           else
780             {
781               BusService *service;
782               DBusString str;
783
784               _dbus_string_init_const (&str, rule->d.receive.origin);
785               
786               service = bus_registry_lookup (registry, &str);
787               
788               if (service == NULL)
789                 continue;
790
791               if (!bus_service_has_owner (service, sender))
792                 continue;
793             }
794         }
795
796       /* Use this rule */
797       allowed = rule->allow;
798     }
799
800   return allowed;
801 }
802
803 dbus_bool_t
804 bus_client_policy_check_can_own (BusClientPolicy  *policy,
805                                  DBusConnection   *connection,
806                                  const DBusString *service_name)
807 {
808   DBusList *link;
809   dbus_bool_t allowed;
810   
811   /* policy->rules is in the order the rules appeared
812    * in the config file, i.e. last rule that applies wins
813    */
814
815   allowed = FALSE;
816   link = _dbus_list_get_first_link (&policy->rules);
817   while (link != NULL)
818     {
819       BusPolicyRule *rule = link->data;
820
821       link = _dbus_list_get_next_link (&policy->rules, link);
822       
823       /* Rule is skipped if it specifies a different service name from
824        * the desired one.
825        */
826       
827       if (rule->type != BUS_POLICY_RULE_OWN)
828         continue;
829
830       if (rule->d.own.service_name != NULL)
831         {
832           if (!_dbus_string_equal_c_str (service_name,
833                                          rule->d.own.service_name))
834             continue;
835         }
836
837       /* Use this rule */
838       allowed = rule->allow;
839     }
840
841   return allowed;
842 }
843
844 #ifdef DBUS_BUILD_TESTS
845
846 dbus_bool_t
847 bus_policy_test (const DBusString *test_data_dir)
848 {
849   /* This doesn't do anything for now because I decided to do it in
850    * dispatch.c instead by having some of the clients in dispatch.c
851    * have particular policies applied to them.
852    */
853   
854   return TRUE;
855 }
856
857 #endif /* DBUS_BUILD_TESTS */