1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/ossrv/ofdbus/dbus/bus/policy.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1263 @@
1.4 +/* -*- mode: C; c-file-style: "gnu" -*- */
1.5 +/* policy.c Bus security policy
1.6 + *
1.7 + * Copyright (C) 2003, 2004 Red Hat, Inc.
1.8 + * Portion Copyright © 2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
1.9 + * Licensed under the Academic Free License version 2.1
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify
1.12 + * it under the terms of the GNU General Public License as published by
1.13 + * the Free Software Foundation; either version 2 of the License, or
1.14 + * (at your option) any later version.
1.15 + *
1.16 + * This program is distributed in the hope that it will be useful,
1.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.19 + * GNU General Public License for more details.
1.20 + *
1.21 + * You should have received a copy of the GNU General Public License
1.22 + * along with this program; if not, write to the Free Software
1.23 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1.24 + *
1.25 + */
1.26 +
1.27 +#include "policy.h"
1.28 +#include "services.h"
1.29 +#include "test.h"
1.30 +#include "utils.h"
1.31 +#ifndef __SYMBIAN32__
1.32 +#include <dbus/dbus-list.h>
1.33 +#include <dbus/dbus-hash.h>
1.34 +#include <dbus/dbus-internals.h>
1.35 +#else
1.36 +#include "dbus-list.h"
1.37 +#include "dbus-hash.h"
1.38 +#include "dbus-internals.h"
1.39 +#endif //__SYMBIAN32__
1.40 +
1.41 +BusPolicyRule*
1.42 +bus_policy_rule_new (BusPolicyRuleType type,
1.43 + dbus_bool_t allow)
1.44 +{
1.45 + BusPolicyRule *rule;
1.46 +
1.47 + rule = dbus_new0 (BusPolicyRule, 1);
1.48 + if (rule == NULL)
1.49 + return NULL;
1.50 +
1.51 + rule->type = type;
1.52 + rule->refcount = 1;
1.53 + rule->allow = allow;
1.54 +
1.55 + switch (rule->type)
1.56 + {
1.57 + case BUS_POLICY_RULE_USER:
1.58 + rule->d.user.uid = DBUS_UID_UNSET;
1.59 + break;
1.60 + case BUS_POLICY_RULE_GROUP:
1.61 + rule->d.group.gid = DBUS_GID_UNSET;
1.62 + break;
1.63 + case BUS_POLICY_RULE_SEND:
1.64 + rule->d.send.message_type = DBUS_MESSAGE_TYPE_INVALID;
1.65 +
1.66 + /* allow rules default to TRUE (only requested replies allowed)
1.67 + * deny rules default to FALSE (only unrequested replies denied)
1.68 + */
1.69 + rule->d.send.requested_reply = rule->allow;
1.70 + break;
1.71 + case BUS_POLICY_RULE_RECEIVE:
1.72 + rule->d.receive.message_type = DBUS_MESSAGE_TYPE_INVALID;
1.73 + /* allow rules default to TRUE (only requested replies allowed)
1.74 + * deny rules default to FALSE (only unrequested replies denied)
1.75 + */
1.76 + rule->d.receive.requested_reply = rule->allow;
1.77 + break;
1.78 + case BUS_POLICY_RULE_OWN:
1.79 + break;
1.80 + }
1.81 +
1.82 + return rule;
1.83 +}
1.84 +
1.85 +BusPolicyRule *
1.86 +bus_policy_rule_ref (BusPolicyRule *rule)
1.87 +{
1.88 + _dbus_assert (rule->refcount > 0);
1.89 +
1.90 + rule->refcount += 1;
1.91 +
1.92 + return rule;
1.93 +}
1.94 +
1.95 +void
1.96 +bus_policy_rule_unref (BusPolicyRule *rule)
1.97 +{
1.98 + _dbus_assert (rule->refcount > 0);
1.99 +
1.100 + rule->refcount -= 1;
1.101 +
1.102 + if (rule->refcount == 0)
1.103 + {
1.104 + switch (rule->type)
1.105 + {
1.106 + case BUS_POLICY_RULE_SEND:
1.107 + dbus_free (rule->d.send.path);
1.108 + dbus_free (rule->d.send.interface);
1.109 + dbus_free (rule->d.send.member);
1.110 + dbus_free (rule->d.send.error);
1.111 + dbus_free (rule->d.send.destination);
1.112 + break;
1.113 + case BUS_POLICY_RULE_RECEIVE:
1.114 + dbus_free (rule->d.receive.path);
1.115 + dbus_free (rule->d.receive.interface);
1.116 + dbus_free (rule->d.receive.member);
1.117 + dbus_free (rule->d.receive.error);
1.118 + dbus_free (rule->d.receive.origin);
1.119 + break;
1.120 + case BUS_POLICY_RULE_OWN:
1.121 + dbus_free (rule->d.own.service_name);
1.122 + break;
1.123 + case BUS_POLICY_RULE_USER:
1.124 + break;
1.125 + case BUS_POLICY_RULE_GROUP:
1.126 + break;
1.127 + }
1.128 +
1.129 + dbus_free (rule);
1.130 + }
1.131 +}
1.132 +
1.133 +struct BusPolicy
1.134 +{
1.135 + int refcount;
1.136 +
1.137 + DBusList *default_rules; /**< Default policy rules */
1.138 + DBusList *mandatory_rules; /**< Mandatory policy rules */
1.139 + DBusHashTable *rules_by_uid; /**< per-UID policy rules */
1.140 + DBusHashTable *rules_by_gid; /**< per-GID policy rules */
1.141 + DBusList *at_console_true_rules; /**< console user policy rules where at_console="true"*/
1.142 + DBusList *at_console_false_rules; /**< console user policy rules where at_console="false"*/
1.143 +};
1.144 +
1.145 +static void
1.146 +free_rule_func (void *data,
1.147 + void *user_data)
1.148 +{
1.149 + BusPolicyRule *rule = data;
1.150 +
1.151 + bus_policy_rule_unref (rule);
1.152 +}
1.153 +
1.154 +static void
1.155 +free_rule_list_func (void *data)
1.156 +{
1.157 + DBusList **list = data;
1.158 +
1.159 + if (list == NULL) /* DBusHashTable is on crack */
1.160 + return;
1.161 +
1.162 + _dbus_list_foreach (list, free_rule_func, NULL);
1.163 +
1.164 + _dbus_list_clear (list);
1.165 +
1.166 + dbus_free (list);
1.167 +}
1.168 +
1.169 +BusPolicy*
1.170 +bus_policy_new (void)
1.171 +{
1.172 + BusPolicy *policy;
1.173 +
1.174 + policy = dbus_new0 (BusPolicy, 1);
1.175 + if (policy == NULL)
1.176 + return NULL;
1.177 +
1.178 + policy->refcount = 1;
1.179 +
1.180 + policy->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
1.181 + NULL,
1.182 + free_rule_list_func);
1.183 + if (policy->rules_by_uid == NULL)
1.184 + goto failed;
1.185 +
1.186 + policy->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG,
1.187 + NULL,
1.188 + free_rule_list_func);
1.189 + if (policy->rules_by_gid == NULL)
1.190 + goto failed;
1.191 +
1.192 + return policy;
1.193 +
1.194 + failed:
1.195 + bus_policy_unref (policy);
1.196 + return NULL;
1.197 +}
1.198 +
1.199 +BusPolicy *
1.200 +bus_policy_ref (BusPolicy *policy)
1.201 +{
1.202 + _dbus_assert (policy->refcount > 0);
1.203 +
1.204 + policy->refcount += 1;
1.205 +
1.206 + return policy;
1.207 +}
1.208 +
1.209 +void
1.210 +bus_policy_unref (BusPolicy *policy)
1.211 +{
1.212 + _dbus_assert (policy->refcount > 0);
1.213 +
1.214 + policy->refcount -= 1;
1.215 +
1.216 + if (policy->refcount == 0)
1.217 + {
1.218 + _dbus_list_foreach (&policy->default_rules, free_rule_func, NULL);
1.219 + _dbus_list_clear (&policy->default_rules);
1.220 +
1.221 + _dbus_list_foreach (&policy->mandatory_rules, free_rule_func, NULL);
1.222 + _dbus_list_clear (&policy->mandatory_rules);
1.223 +
1.224 + _dbus_list_foreach (&policy->at_console_true_rules, free_rule_func, NULL);
1.225 + _dbus_list_clear (&policy->at_console_true_rules);
1.226 +
1.227 + _dbus_list_foreach (&policy->at_console_false_rules, free_rule_func, NULL);
1.228 + _dbus_list_clear (&policy->at_console_false_rules);
1.229 +
1.230 + if (policy->rules_by_uid)
1.231 + {
1.232 + _dbus_hash_table_unref (policy->rules_by_uid);
1.233 + policy->rules_by_uid = NULL;
1.234 + }
1.235 +
1.236 + if (policy->rules_by_gid)
1.237 + {
1.238 + _dbus_hash_table_unref (policy->rules_by_gid);
1.239 + policy->rules_by_gid = NULL;
1.240 + }
1.241 +
1.242 + dbus_free (policy);
1.243 + }
1.244 +}
1.245 +
1.246 +static dbus_bool_t
1.247 +add_list_to_client (DBusList **list,
1.248 + BusClientPolicy *client)
1.249 +{
1.250 + DBusList *link;
1.251 +
1.252 + link = _dbus_list_get_first_link (list);
1.253 + while (link != NULL)
1.254 + {
1.255 + BusPolicyRule *rule = link->data;
1.256 + link = _dbus_list_get_next_link (list, link);
1.257 +
1.258 + switch (rule->type)
1.259 + {
1.260 + case BUS_POLICY_RULE_USER:
1.261 + case BUS_POLICY_RULE_GROUP:
1.262 + /* These aren't per-connection policies */
1.263 + break;
1.264 +
1.265 + case BUS_POLICY_RULE_OWN:
1.266 + case BUS_POLICY_RULE_SEND:
1.267 + case BUS_POLICY_RULE_RECEIVE:
1.268 + /* These are per-connection */
1.269 + if (!bus_client_policy_append_rule (client, rule))
1.270 + return FALSE;
1.271 + break;
1.272 + }
1.273 + }
1.274 +
1.275 + return TRUE;
1.276 +}
1.277 +
1.278 +BusClientPolicy*
1.279 +bus_policy_create_client_policy (BusPolicy *policy,
1.280 + DBusConnection *connection,
1.281 + DBusError *error)
1.282 +{
1.283 + BusClientPolicy *client;
1.284 + dbus_uid_t uid;
1.285 + dbus_bool_t at_console;
1.286 +
1.287 + _dbus_assert (dbus_connection_get_is_authenticated (connection));
1.288 + _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1.289 +
1.290 + client = bus_client_policy_new ();
1.291 + if (client == NULL)
1.292 + goto nomem;
1.293 +
1.294 + if (!add_list_to_client (&policy->default_rules,
1.295 + client))
1.296 + goto nomem;
1.297 +
1.298 + /* we avoid the overhead of looking up user's groups
1.299 + * if we don't have any group rules anyway
1.300 + */
1.301 + if (_dbus_hash_table_get_n_entries (policy->rules_by_gid) > 0)
1.302 + {
1.303 + unsigned long *groups;
1.304 + int n_groups;
1.305 + int i;
1.306 +
1.307 + if (!bus_connection_get_groups (connection, &groups, &n_groups, error))
1.308 + goto failed;
1.309 +
1.310 + i = 0;
1.311 + while (i < n_groups)
1.312 + {
1.313 + DBusList **list;
1.314 +
1.315 + list = _dbus_hash_table_lookup_ulong (policy->rules_by_gid,
1.316 + groups[i]);
1.317 +
1.318 + if (list != NULL)
1.319 + {
1.320 + if (!add_list_to_client (list, client))
1.321 + {
1.322 + dbus_free (groups);
1.323 + goto nomem;
1.324 + }
1.325 + }
1.326 +
1.327 + ++i;
1.328 + }
1.329 +
1.330 + dbus_free (groups);
1.331 + }
1.332 +
1.333 + if (!dbus_connection_get_unix_user (connection, &uid))
1.334 + {
1.335 + dbus_set_error (error, DBUS_ERROR_FAILED,
1.336 + "No user ID known for connection, cannot determine security policy\n");
1.337 + goto failed;
1.338 + }
1.339 +
1.340 + if (_dbus_hash_table_get_n_entries (policy->rules_by_uid) > 0)
1.341 + {
1.342 + DBusList **list;
1.343 +
1.344 + list = _dbus_hash_table_lookup_ulong (policy->rules_by_uid,
1.345 + uid);
1.346 +
1.347 + if (list != NULL)
1.348 + {
1.349 + if (!add_list_to_client (list, client))
1.350 + goto nomem;
1.351 + }
1.352 + }
1.353 +
1.354 + /* Add console rules */
1.355 + at_console = _dbus_is_console_user (uid, error);
1.356 +
1.357 + if (at_console)
1.358 + {
1.359 + if (!add_list_to_client (&policy->at_console_true_rules, client))
1.360 + goto nomem;
1.361 + }
1.362 + else if (dbus_error_is_set (error) == TRUE)
1.363 + {
1.364 + goto failed;
1.365 + }
1.366 + else if (!add_list_to_client (&policy->at_console_false_rules, client))
1.367 + {
1.368 + goto nomem;
1.369 + }
1.370 +
1.371 + if (!add_list_to_client (&policy->mandatory_rules,
1.372 + client))
1.373 + goto nomem;
1.374 +
1.375 + bus_client_policy_optimize (client);
1.376 +
1.377 + return client;
1.378 +
1.379 + nomem:
1.380 + BUS_SET_OOM (error);
1.381 + failed:
1.382 + _DBUS_ASSERT_ERROR_IS_SET (error);
1.383 + if (client)
1.384 + bus_client_policy_unref (client);
1.385 + return NULL;
1.386 +}
1.387 +
1.388 +static dbus_bool_t
1.389 +list_allows_user (dbus_bool_t def,
1.390 + DBusList **list,
1.391 + unsigned long uid,
1.392 + const unsigned long *group_ids,
1.393 + int n_group_ids)
1.394 +{
1.395 + DBusList *link;
1.396 + dbus_bool_t allowed;
1.397 +
1.398 + allowed = def;
1.399 +
1.400 + link = _dbus_list_get_first_link (list);
1.401 + while (link != NULL)
1.402 + {
1.403 + BusPolicyRule *rule = link->data;
1.404 + link = _dbus_list_get_next_link (list, link);
1.405 +
1.406 + if (rule->type == BUS_POLICY_RULE_USER)
1.407 + {
1.408 + _dbus_verbose ("List %p user rule uid="DBUS_UID_FORMAT"\n",
1.409 + list, rule->d.user.uid);
1.410 +
1.411 + if (rule->d.user.uid == DBUS_UID_UNSET)
1.412 + ; /* '*' wildcard */
1.413 + else if (rule->d.user.uid != uid)
1.414 + continue;
1.415 + }
1.416 + else if (rule->type == BUS_POLICY_RULE_GROUP)
1.417 + {
1.418 + _dbus_verbose ("List %p group rule uid="DBUS_UID_FORMAT"\n",
1.419 + list, rule->d.user.uid);
1.420 +
1.421 + if (rule->d.group.gid == DBUS_GID_UNSET)
1.422 + ; /* '*' wildcard */
1.423 + else
1.424 + {
1.425 + int i;
1.426 +
1.427 + i = 0;
1.428 + while (i < n_group_ids)
1.429 + {
1.430 + if (rule->d.group.gid == group_ids[i])
1.431 + break;
1.432 + ++i;
1.433 + }
1.434 +
1.435 + if (i == n_group_ids)
1.436 + continue;
1.437 + }
1.438 + }
1.439 + else
1.440 + continue;
1.441 +
1.442 + allowed = rule->allow;
1.443 + }
1.444 +
1.445 + return allowed;
1.446 +}
1.447 +
1.448 +dbus_bool_t
1.449 +bus_policy_allow_user (BusPolicy *policy,
1.450 + DBusUserDatabase *user_database,
1.451 + unsigned long uid)
1.452 +{
1.453 + dbus_bool_t allowed;
1.454 + unsigned long *group_ids;
1.455 + int n_group_ids;
1.456 +
1.457 + /* On OOM or error we always reject the user */
1.458 + if (!_dbus_user_database_get_groups (user_database,
1.459 + uid, &group_ids, &n_group_ids, NULL))
1.460 + {
1.461 + _dbus_verbose ("Did not get any groups for UID %lu\n",
1.462 + uid);
1.463 + return FALSE;
1.464 + }
1.465 +
1.466 + /* Default to "user owning bus" or root can connect */
1.467 + allowed = uid == _dbus_getuid ();
1.468 +
1.469 + allowed = list_allows_user (allowed,
1.470 + &policy->default_rules,
1.471 + uid,
1.472 + group_ids, n_group_ids);
1.473 +
1.474 + allowed = list_allows_user (allowed,
1.475 + &policy->mandatory_rules,
1.476 + uid,
1.477 + group_ids, n_group_ids);
1.478 +
1.479 + dbus_free (group_ids);
1.480 +
1.481 + _dbus_verbose ("UID %lu allowed = %d\n", uid, allowed);
1.482 +
1.483 + return allowed;
1.484 +}
1.485 +
1.486 +dbus_bool_t
1.487 +bus_policy_append_default_rule (BusPolicy *policy,
1.488 + BusPolicyRule *rule)
1.489 +{
1.490 + if (!_dbus_list_append (&policy->default_rules, rule))
1.491 + return FALSE;
1.492 +
1.493 + bus_policy_rule_ref (rule);
1.494 +
1.495 + return TRUE;
1.496 +}
1.497 +
1.498 +dbus_bool_t
1.499 +bus_policy_append_mandatory_rule (BusPolicy *policy,
1.500 + BusPolicyRule *rule)
1.501 +{
1.502 + if (!_dbus_list_append (&policy->mandatory_rules, rule))
1.503 + return FALSE;
1.504 +
1.505 + bus_policy_rule_ref (rule);
1.506 +
1.507 + return TRUE;
1.508 +}
1.509 +
1.510 +
1.511 +
1.512 +static DBusList**
1.513 +get_list (DBusHashTable *hash,
1.514 + unsigned long key)
1.515 +{
1.516 + DBusList **list;
1.517 +
1.518 + list = _dbus_hash_table_lookup_ulong (hash, key);
1.519 +
1.520 + if (list == NULL)
1.521 + {
1.522 + list = dbus_new0 (DBusList*, 1);
1.523 + if (list == NULL)
1.524 + return NULL;
1.525 +
1.526 + if (!_dbus_hash_table_insert_ulong (hash, key, list))
1.527 + {
1.528 + dbus_free (list);
1.529 + return NULL;
1.530 + }
1.531 + }
1.532 +
1.533 + return list;
1.534 +}
1.535 +
1.536 +dbus_bool_t
1.537 +bus_policy_append_user_rule (BusPolicy *policy,
1.538 + dbus_uid_t uid,
1.539 + BusPolicyRule *rule)
1.540 +{
1.541 + DBusList **list;
1.542 +
1.543 + list = get_list (policy->rules_by_uid, uid);
1.544 +
1.545 + if (list == NULL)
1.546 + return FALSE;
1.547 +
1.548 + if (!_dbus_list_append (list, rule))
1.549 + return FALSE;
1.550 +
1.551 + bus_policy_rule_ref (rule);
1.552 +
1.553 + return TRUE;
1.554 +}
1.555 +
1.556 +dbus_bool_t
1.557 +bus_policy_append_group_rule (BusPolicy *policy,
1.558 + dbus_gid_t gid,
1.559 + BusPolicyRule *rule)
1.560 +{
1.561 + DBusList **list;
1.562 +
1.563 + list = get_list (policy->rules_by_gid, gid);
1.564 +
1.565 + if (list == NULL)
1.566 + return FALSE;
1.567 +
1.568 + if (!_dbus_list_append (list, rule))
1.569 + return FALSE;
1.570 +
1.571 + bus_policy_rule_ref (rule);
1.572 +
1.573 + return TRUE;
1.574 +}
1.575 +
1.576 +dbus_bool_t
1.577 +bus_policy_append_console_rule (BusPolicy *policy,
1.578 + dbus_bool_t at_console,
1.579 + BusPolicyRule *rule)
1.580 +{
1.581 + if (at_console)
1.582 + {
1.583 + if (!_dbus_list_append (&policy->at_console_true_rules, rule))
1.584 + return FALSE;
1.585 + }
1.586 + else
1.587 + {
1.588 + if (!_dbus_list_append (&policy->at_console_false_rules, rule))
1.589 + return FALSE;
1.590 + }
1.591 +
1.592 + bus_policy_rule_ref (rule);
1.593 +
1.594 + return TRUE;
1.595 +
1.596 +}
1.597 +
1.598 +static dbus_bool_t
1.599 +append_copy_of_policy_list (DBusList **list,
1.600 + DBusList **to_append)
1.601 +{
1.602 + DBusList *link;
1.603 + DBusList *tmp_list;
1.604 +
1.605 + tmp_list = NULL;
1.606 +
1.607 + /* Preallocate all our links */
1.608 + link = _dbus_list_get_first_link (to_append);
1.609 + while (link != NULL)
1.610 + {
1.611 + if (!_dbus_list_append (&tmp_list, link->data))
1.612 + {
1.613 + _dbus_list_clear (&tmp_list);
1.614 + return FALSE;
1.615 + }
1.616 +
1.617 + link = _dbus_list_get_next_link (to_append, link);
1.618 + }
1.619 +
1.620 + /* Now append them */
1.621 + while ((link = _dbus_list_pop_first_link (&tmp_list)))
1.622 + {
1.623 + bus_policy_rule_ref (link->data);
1.624 + _dbus_list_append_link (list, link);
1.625 + }
1.626 +
1.627 + return TRUE;
1.628 +}
1.629 +
1.630 +static dbus_bool_t
1.631 +merge_id_hash (DBusHashTable *dest,
1.632 + DBusHashTable *to_absorb)
1.633 +{
1.634 + DBusHashIter iter;
1.635 +
1.636 + _dbus_hash_iter_init (to_absorb, &iter);
1.637 + while (_dbus_hash_iter_next (&iter))
1.638 + {
1.639 + unsigned long id = _dbus_hash_iter_get_ulong_key (&iter);
1.640 + DBusList **list = _dbus_hash_iter_get_value (&iter);
1.641 + DBusList **target = get_list (dest, id);
1.642 +
1.643 + if (target == NULL)
1.644 + return FALSE;
1.645 +
1.646 + if (!append_copy_of_policy_list (target, list))
1.647 + return FALSE;
1.648 + }
1.649 +
1.650 + return TRUE;
1.651 +}
1.652 +
1.653 +dbus_bool_t
1.654 +bus_policy_merge (BusPolicy *policy,
1.655 + BusPolicy *to_absorb)
1.656 +{
1.657 + /* FIXME Not properly atomic, but as used for configuration files we
1.658 + * don't rely on it quite so much.
1.659 + */
1.660 +
1.661 + if (!append_copy_of_policy_list (&policy->default_rules,
1.662 + &to_absorb->default_rules))
1.663 + return FALSE;
1.664 +
1.665 + if (!append_copy_of_policy_list (&policy->mandatory_rules,
1.666 + &to_absorb->mandatory_rules))
1.667 + return FALSE;
1.668 +
1.669 + if (!append_copy_of_policy_list (&policy->at_console_true_rules,
1.670 + &to_absorb->at_console_true_rules))
1.671 + return FALSE;
1.672 +
1.673 + if (!append_copy_of_policy_list (&policy->at_console_false_rules,
1.674 + &to_absorb->at_console_false_rules))
1.675 + return FALSE;
1.676 +
1.677 + if (!merge_id_hash (policy->rules_by_uid,
1.678 + to_absorb->rules_by_uid))
1.679 + return FALSE;
1.680 +
1.681 + if (!merge_id_hash (policy->rules_by_gid,
1.682 + to_absorb->rules_by_gid))
1.683 + return FALSE;
1.684 +
1.685 + return TRUE;
1.686 +}
1.687 +
1.688 +struct BusClientPolicy
1.689 +{
1.690 + int refcount;
1.691 +
1.692 + DBusList *rules;
1.693 +};
1.694 +
1.695 +BusClientPolicy*
1.696 +bus_client_policy_new (void)
1.697 +{
1.698 + BusClientPolicy *policy;
1.699 +
1.700 + policy = dbus_new0 (BusClientPolicy, 1);
1.701 + if (policy == NULL)
1.702 + return NULL;
1.703 +
1.704 + policy->refcount = 1;
1.705 +
1.706 + return policy;
1.707 +}
1.708 +
1.709 +BusClientPolicy *
1.710 +bus_client_policy_ref (BusClientPolicy *policy)
1.711 +{
1.712 + _dbus_assert (policy->refcount > 0);
1.713 +
1.714 + policy->refcount += 1;
1.715 +
1.716 + return policy;
1.717 +}
1.718 +
1.719 +static void
1.720 +rule_unref_foreach (void *data,
1.721 + void *user_data)
1.722 +{
1.723 + BusPolicyRule *rule = data;
1.724 +
1.725 + bus_policy_rule_unref (rule);
1.726 +}
1.727 +
1.728 +void
1.729 +bus_client_policy_unref (BusClientPolicy *policy)
1.730 +{
1.731 + _dbus_assert (policy->refcount > 0);
1.732 +
1.733 + policy->refcount -= 1;
1.734 +
1.735 + if (policy->refcount == 0)
1.736 + {
1.737 + _dbus_list_foreach (&policy->rules,
1.738 + rule_unref_foreach,
1.739 + NULL);
1.740 +
1.741 + _dbus_list_clear (&policy->rules);
1.742 +
1.743 + dbus_free (policy);
1.744 + }
1.745 +}
1.746 +
1.747 +static void
1.748 +remove_rules_by_type_up_to (BusClientPolicy *policy,
1.749 + BusPolicyRuleType type,
1.750 + DBusList *up_to)
1.751 +{
1.752 + DBusList *link;
1.753 +
1.754 + link = _dbus_list_get_first_link (&policy->rules);
1.755 + while (link != up_to)
1.756 + {
1.757 + BusPolicyRule *rule = link->data;
1.758 + DBusList *next = _dbus_list_get_next_link (&policy->rules, link);
1.759 +
1.760 + if (rule->type == type)
1.761 + {
1.762 + _dbus_list_remove_link (&policy->rules, link);
1.763 + bus_policy_rule_unref (rule);
1.764 + }
1.765 +
1.766 + link = next;
1.767 + }
1.768 +}
1.769 +
1.770 +void
1.771 +bus_client_policy_optimize (BusClientPolicy *policy)
1.772 +{
1.773 + DBusList *link;
1.774 +
1.775 + /* The idea here is that if we have:
1.776 + *
1.777 + * <allow send_interface="foo.bar"/>
1.778 + * <deny send_interface="*"/>
1.779 + *
1.780 + * (for example) the deny will always override the allow. So we
1.781 + * delete the allow. Ditto for deny followed by allow, etc. This is
1.782 + * a dumb thing to put in a config file, but the <include> feature
1.783 + * of files allows for an "inheritance and override" pattern where
1.784 + * it could make sense. If an included file wants to "start over"
1.785 + * with a blanket deny, no point keeping the rules from the parent
1.786 + * file.
1.787 + */
1.788 +
1.789 + _dbus_verbose ("Optimizing policy with %d rules\n",
1.790 + _dbus_list_get_length (&policy->rules));
1.791 +
1.792 + link = _dbus_list_get_first_link (&policy->rules);
1.793 + while (link != NULL)
1.794 + {
1.795 + BusPolicyRule *rule;
1.796 + DBusList *next;
1.797 + dbus_bool_t remove_preceding;
1.798 +
1.799 + next = _dbus_list_get_next_link (&policy->rules, link);
1.800 + rule = link->data;
1.801 +
1.802 + remove_preceding = FALSE;
1.803 +
1.804 + _dbus_assert (rule != NULL);
1.805 +
1.806 + switch (rule->type)
1.807 + {
1.808 + case BUS_POLICY_RULE_SEND:
1.809 + remove_preceding =
1.810 + rule->d.send.message_type == DBUS_MESSAGE_TYPE_INVALID &&
1.811 + rule->d.send.path == NULL &&
1.812 + rule->d.send.interface == NULL &&
1.813 + rule->d.send.member == NULL &&
1.814 + rule->d.send.error == NULL &&
1.815 + rule->d.send.destination == NULL;
1.816 + break;
1.817 + case BUS_POLICY_RULE_RECEIVE:
1.818 + remove_preceding =
1.819 + rule->d.receive.message_type == DBUS_MESSAGE_TYPE_INVALID &&
1.820 + rule->d.receive.path == NULL &&
1.821 + rule->d.receive.interface == NULL &&
1.822 + rule->d.receive.member == NULL &&
1.823 + rule->d.receive.error == NULL &&
1.824 + rule->d.receive.origin == NULL;
1.825 + break;
1.826 + case BUS_POLICY_RULE_OWN:
1.827 + remove_preceding =
1.828 + rule->d.own.service_name == NULL;
1.829 + break;
1.830 + case BUS_POLICY_RULE_USER:
1.831 + case BUS_POLICY_RULE_GROUP:
1.832 + _dbus_assert_not_reached ("invalid rule");
1.833 + break;
1.834 + }
1.835 +
1.836 + if (remove_preceding)
1.837 + remove_rules_by_type_up_to (policy, rule->type,
1.838 + link);
1.839 +
1.840 + link = next;
1.841 + }
1.842 +
1.843 + _dbus_verbose ("After optimization, policy has %d rules\n",
1.844 + _dbus_list_get_length (&policy->rules));
1.845 +}
1.846 +
1.847 +dbus_bool_t
1.848 +bus_client_policy_append_rule (BusClientPolicy *policy,
1.849 + BusPolicyRule *rule)
1.850 +{
1.851 + _dbus_verbose ("Appending rule %p with type %d to policy %p\n",
1.852 + rule, rule->type, policy);
1.853 +
1.854 + if (!_dbus_list_append (&policy->rules, rule))
1.855 + return FALSE;
1.856 +
1.857 + bus_policy_rule_ref (rule);
1.858 +
1.859 + return TRUE;
1.860 +}
1.861 +
1.862 +dbus_bool_t
1.863 +bus_client_policy_check_can_send (BusClientPolicy *policy,
1.864 + BusRegistry *registry,
1.865 + dbus_bool_t requested_reply,
1.866 + DBusConnection *receiver,
1.867 + DBusMessage *message)
1.868 +{
1.869 + DBusList *link;
1.870 + dbus_bool_t allowed;
1.871 +
1.872 + /* policy->rules is in the order the rules appeared
1.873 + * in the config file, i.e. last rule that applies wins
1.874 + */
1.875 +
1.876 + _dbus_verbose (" (policy) checking send rules\n");
1.877 +
1.878 + allowed = FALSE;
1.879 + link = _dbus_list_get_first_link (&policy->rules);
1.880 + while (link != NULL)
1.881 + {
1.882 + BusPolicyRule *rule = link->data;
1.883 +
1.884 + link = _dbus_list_get_next_link (&policy->rules, link);
1.885 +
1.886 + /* Rule is skipped if it specifies a different
1.887 + * message name from the message, or a different
1.888 + * destination from the message
1.889 + */
1.890 +
1.891 + if (rule->type != BUS_POLICY_RULE_SEND)
1.892 + {
1.893 + _dbus_verbose (" (policy) skipping non-send rule\n");
1.894 + continue;
1.895 + }
1.896 +
1.897 + if (rule->d.send.message_type != DBUS_MESSAGE_TYPE_INVALID)
1.898 + {
1.899 + if (dbus_message_get_type (message) != rule->d.send.message_type)
1.900 + {
1.901 + _dbus_verbose (" (policy) skipping rule for different message type\n");
1.902 + continue;
1.903 + }
1.904 + }
1.905 +
1.906 + /* If it's a reply, the requested_reply flag kicks in */
1.907 + if (dbus_message_get_reply_serial (message) != 0)
1.908 + {
1.909 + /* for allow, requested_reply=true means the rule applies
1.910 + * only when reply was requested. requested_reply=false means
1.911 + * always allow.
1.912 + */
1.913 + if (!requested_reply && rule->allow && rule->d.send.requested_reply)
1.914 + {
1.915 + _dbus_verbose (" (policy) skipping allow rule since it only applies to requested replies\n");
1.916 + continue;
1.917 + }
1.918 +
1.919 + /* for deny, requested_reply=false means the rule applies only
1.920 + * when the reply was not requested. requested_reply=true means the
1.921 + * rule always applies.
1.922 + */
1.923 + if (requested_reply && !rule->allow && !rule->d.send.requested_reply)
1.924 + {
1.925 + _dbus_verbose (" (policy) skipping deny rule since it only applies to unrequested replies\n");
1.926 + continue;
1.927 + }
1.928 + }
1.929 +
1.930 + if (rule->d.send.path != NULL)
1.931 + {
1.932 + if (dbus_message_get_path (message) != NULL &&
1.933 + strcmp (dbus_message_get_path (message),
1.934 + rule->d.send.path) != 0)
1.935 + {
1.936 + _dbus_verbose (" (policy) skipping rule for different path\n");
1.937 + continue;
1.938 + }
1.939 + }
1.940 +
1.941 + if (rule->d.send.interface != NULL)
1.942 + {
1.943 + if (dbus_message_get_interface (message) != NULL &&
1.944 + strcmp (dbus_message_get_interface (message),
1.945 + rule->d.send.interface) != 0)
1.946 + {
1.947 + _dbus_verbose (" (policy) skipping rule for different interface\n");
1.948 + continue;
1.949 + }
1.950 + }
1.951 +
1.952 + if (rule->d.send.member != NULL)
1.953 + {
1.954 + if (dbus_message_get_member (message) != NULL &&
1.955 + strcmp (dbus_message_get_member (message),
1.956 + rule->d.send.member) != 0)
1.957 + {
1.958 + _dbus_verbose (" (policy) skipping rule for different member\n");
1.959 + continue;
1.960 + }
1.961 + }
1.962 +
1.963 + if (rule->d.send.error != NULL)
1.964 + {
1.965 + if (dbus_message_get_error_name (message) != NULL &&
1.966 + strcmp (dbus_message_get_error_name (message),
1.967 + rule->d.send.error) != 0)
1.968 + {
1.969 + _dbus_verbose (" (policy) skipping rule for different error name\n");
1.970 + continue;
1.971 + }
1.972 + }
1.973 +
1.974 + if (rule->d.send.destination != NULL)
1.975 + {
1.976 + /* receiver can be NULL for messages that are sent to the
1.977 + * message bus itself, we check the strings in that case as
1.978 + * built-in services don't have a DBusConnection but messages
1.979 + * to them have a destination service name.
1.980 + */
1.981 + if (receiver == NULL)
1.982 + {
1.983 + if (!dbus_message_has_destination (message,
1.984 + rule->d.send.destination))
1.985 + {
1.986 + _dbus_verbose (" (policy) skipping rule because message dest is not %s\n",
1.987 + rule->d.send.destination);
1.988 + continue;
1.989 + }
1.990 + }
1.991 + else
1.992 + {
1.993 + DBusString str;
1.994 + BusService *service;
1.995 +
1.996 + _dbus_string_init_const (&str, rule->d.send.destination);
1.997 +
1.998 + service = bus_registry_lookup (registry, &str);
1.999 + if (service == NULL)
1.1000 + {
1.1001 + _dbus_verbose (" (policy) skipping rule because dest %s doesn't exist\n",
1.1002 + rule->d.send.destination);
1.1003 + continue;
1.1004 + }
1.1005 +
1.1006 + if (!bus_service_has_owner (service, receiver))
1.1007 + {
1.1008 + _dbus_verbose (" (policy) skipping rule because dest %s isn't owned by receiver\n",
1.1009 + rule->d.send.destination);
1.1010 + continue;
1.1011 + }
1.1012 + }
1.1013 + }
1.1014 +
1.1015 + /* Use this rule */
1.1016 + allowed = rule->allow;
1.1017 +
1.1018 + _dbus_verbose (" (policy) used rule, allow now = %d\n",
1.1019 + allowed);
1.1020 + }
1.1021 +
1.1022 + return allowed;
1.1023 +}
1.1024 +
1.1025 +/* See docs on what the args mean on bus_context_check_security_policy()
1.1026 + * comment
1.1027 + */
1.1028 +dbus_bool_t
1.1029 +bus_client_policy_check_can_receive (BusClientPolicy *policy,
1.1030 + BusRegistry *registry,
1.1031 + dbus_bool_t requested_reply,
1.1032 + DBusConnection *sender,
1.1033 + DBusConnection *addressed_recipient,
1.1034 + DBusConnection *proposed_recipient,
1.1035 + DBusMessage *message)
1.1036 +{
1.1037 + DBusList *link;
1.1038 + dbus_bool_t allowed;
1.1039 + dbus_bool_t eavesdropping;
1.1040 +
1.1041 + eavesdropping =
1.1042 + addressed_recipient != proposed_recipient &&
1.1043 + dbus_message_get_destination (message) != NULL;
1.1044 +
1.1045 + /* policy->rules is in the order the rules appeared
1.1046 + * in the config file, i.e. last rule that applies wins
1.1047 + */
1.1048 +
1.1049 + _dbus_verbose (" (policy) checking receive rules, eavesdropping = %d\n", eavesdropping);
1.1050 +
1.1051 + allowed = FALSE;
1.1052 + link = _dbus_list_get_first_link (&policy->rules);
1.1053 + while (link != NULL)
1.1054 + {
1.1055 + BusPolicyRule *rule = link->data;
1.1056 +
1.1057 + link = _dbus_list_get_next_link (&policy->rules, link);
1.1058 +
1.1059 + if (rule->type != BUS_POLICY_RULE_RECEIVE)
1.1060 + {
1.1061 + _dbus_verbose (" (policy) skipping non-receive rule\n");
1.1062 + continue;
1.1063 + }
1.1064 +
1.1065 + if (rule->d.receive.message_type != DBUS_MESSAGE_TYPE_INVALID)
1.1066 + {
1.1067 + if (dbus_message_get_type (message) != rule->d.receive.message_type)
1.1068 + {
1.1069 + _dbus_verbose (" (policy) skipping rule for different message type\n");
1.1070 + continue;
1.1071 + }
1.1072 + }
1.1073 +
1.1074 + /* for allow, eavesdrop=false means the rule doesn't apply when
1.1075 + * eavesdropping. eavesdrop=true means always allow.
1.1076 + */
1.1077 + if (eavesdropping && rule->allow && !rule->d.receive.eavesdrop)
1.1078 + {
1.1079 + _dbus_verbose (" (policy) skipping allow rule since it doesn't apply to eavesdropping\n");
1.1080 + continue;
1.1081 + }
1.1082 +
1.1083 + /* for deny, eavesdrop=true means the rule applies only when
1.1084 + * eavesdropping; eavesdrop=false means always deny.
1.1085 + */
1.1086 + if (!eavesdropping && !rule->allow && rule->d.receive.eavesdrop)
1.1087 + {
1.1088 + _dbus_verbose (" (policy) skipping deny rule since it only applies to eavesdropping\n");
1.1089 + continue;
1.1090 + }
1.1091 +
1.1092 + /* If it's a reply, the requested_reply flag kicks in */
1.1093 + if (dbus_message_get_reply_serial (message) != 0)
1.1094 + {
1.1095 + /* for allow, requested_reply=true means the rule applies
1.1096 + * only when reply was requested. requested_reply=false means
1.1097 + * always allow.
1.1098 + */
1.1099 + if (!requested_reply && rule->allow && rule->d.receive.requested_reply)
1.1100 + {
1.1101 + _dbus_verbose (" (policy) skipping allow rule since it only applies to requested replies\n");
1.1102 + continue;
1.1103 + }
1.1104 +
1.1105 + /* for deny, requested_reply=false means the rule applies only
1.1106 + * when the reply was not requested. requested_reply=true means the
1.1107 + * rule always applies.
1.1108 + */
1.1109 + if (requested_reply && !rule->allow && !rule->d.receive.requested_reply)
1.1110 + {
1.1111 + _dbus_verbose (" (policy) skipping deny rule since it only applies to unrequested replies\n");
1.1112 + continue;
1.1113 + }
1.1114 + }
1.1115 +
1.1116 + if (rule->d.receive.path != NULL)
1.1117 + {
1.1118 + if (dbus_message_get_path (message) != NULL &&
1.1119 + strcmp (dbus_message_get_path (message),
1.1120 + rule->d.receive.path) != 0)
1.1121 + {
1.1122 + _dbus_verbose (" (policy) skipping rule for different path\n");
1.1123 + continue;
1.1124 + }
1.1125 + }
1.1126 +
1.1127 + if (rule->d.receive.interface != NULL)
1.1128 + {
1.1129 + if (dbus_message_get_interface (message) != NULL &&
1.1130 + strcmp (dbus_message_get_interface (message),
1.1131 + rule->d.receive.interface) != 0)
1.1132 + {
1.1133 + _dbus_verbose (" (policy) skipping rule for different interface\n");
1.1134 + continue;
1.1135 + }
1.1136 + }
1.1137 +
1.1138 + if (rule->d.receive.member != NULL)
1.1139 + {
1.1140 + if (dbus_message_get_member (message) != NULL &&
1.1141 + strcmp (dbus_message_get_member (message),
1.1142 + rule->d.receive.member) != 0)
1.1143 + {
1.1144 + _dbus_verbose (" (policy) skipping rule for different member\n");
1.1145 + continue;
1.1146 + }
1.1147 + }
1.1148 +
1.1149 + if (rule->d.receive.error != NULL)
1.1150 + {
1.1151 + if (dbus_message_get_error_name (message) != NULL &&
1.1152 + strcmp (dbus_message_get_error_name (message),
1.1153 + rule->d.receive.error) != 0)
1.1154 + {
1.1155 + _dbus_verbose (" (policy) skipping rule for different error name\n");
1.1156 + continue;
1.1157 + }
1.1158 + }
1.1159 +
1.1160 + if (rule->d.receive.origin != NULL)
1.1161 + {
1.1162 + /* sender can be NULL for messages that originate from the
1.1163 + * message bus itself, we check the strings in that case as
1.1164 + * built-in services don't have a DBusConnection but will
1.1165 + * still set the sender on their messages.
1.1166 + */
1.1167 + if (sender == NULL)
1.1168 + {
1.1169 + if (!dbus_message_has_sender (message,
1.1170 + rule->d.receive.origin))
1.1171 + {
1.1172 + _dbus_verbose (" (policy) skipping rule because message sender is not %s\n",
1.1173 + rule->d.receive.origin);
1.1174 + continue;
1.1175 + }
1.1176 + }
1.1177 + else
1.1178 + {
1.1179 + BusService *service;
1.1180 + DBusString str;
1.1181 +
1.1182 + _dbus_string_init_const (&str, rule->d.receive.origin);
1.1183 +
1.1184 + service = bus_registry_lookup (registry, &str);
1.1185 +
1.1186 + if (service == NULL)
1.1187 + {
1.1188 + _dbus_verbose (" (policy) skipping rule because origin %s doesn't exist\n",
1.1189 + rule->d.receive.origin);
1.1190 + continue;
1.1191 + }
1.1192 +
1.1193 + if (!bus_service_has_owner (service, sender))
1.1194 + {
1.1195 + _dbus_verbose (" (policy) skipping rule because origin %s isn't owned by sender\n",
1.1196 + rule->d.receive.origin);
1.1197 + continue;
1.1198 + }
1.1199 + }
1.1200 + }
1.1201 +
1.1202 + /* Use this rule */
1.1203 + allowed = rule->allow;
1.1204 +
1.1205 + _dbus_verbose (" (policy) used rule, allow now = %d\n",
1.1206 + allowed);
1.1207 + }
1.1208 +
1.1209 + return allowed;
1.1210 +}
1.1211 +
1.1212 +dbus_bool_t
1.1213 +bus_client_policy_check_can_own (BusClientPolicy *policy,
1.1214 + DBusConnection *connection,
1.1215 + const DBusString *service_name)
1.1216 +{
1.1217 + DBusList *link;
1.1218 + dbus_bool_t allowed;
1.1219 +
1.1220 + /* policy->rules is in the order the rules appeared
1.1221 + * in the config file, i.e. last rule that applies wins
1.1222 + */
1.1223 +
1.1224 + allowed = FALSE;
1.1225 + link = _dbus_list_get_first_link (&policy->rules);
1.1226 + while (link != NULL)
1.1227 + {
1.1228 + BusPolicyRule *rule = link->data;
1.1229 +
1.1230 + link = _dbus_list_get_next_link (&policy->rules, link);
1.1231 +
1.1232 + /* Rule is skipped if it specifies a different service name from
1.1233 + * the desired one.
1.1234 + */
1.1235 +
1.1236 + if (rule->type != BUS_POLICY_RULE_OWN)
1.1237 + continue;
1.1238 +
1.1239 + if (rule->d.own.service_name != NULL)
1.1240 + {
1.1241 + if (!_dbus_string_equal_c_str (service_name,
1.1242 + rule->d.own.service_name))
1.1243 + continue;
1.1244 + }
1.1245 +
1.1246 + /* Use this rule */
1.1247 + allowed = rule->allow;
1.1248 + }
1.1249 +
1.1250 + return allowed;
1.1251 +}
1.1252 +
1.1253 +#ifdef DBUS_BUILD_TESTS
1.1254 +
1.1255 +dbus_bool_t
1.1256 +bus_policy_test (const DBusString *test_data_dir)
1.1257 +{
1.1258 + /* This doesn't do anything for now because I decided to do it in
1.1259 + * dispatch.c instead by having some of the clients in dispatch.c
1.1260 + * have particular policies applied to them.
1.1261 + */
1.1262 +
1.1263 + return TRUE;
1.1264 +}
1.1265 +
1.1266 +#endif /* DBUS_BUILD_TESTS */