bus.c revision 29c71168cd17b11eed65023c97aff401d5305b01
1/* -*- mode: C; c-file-style: "gnu" -*- */ 2/* bus.c message bus context object 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 "bus.h" 25#include "loop.h" 26#include "activation.h" 27#include "connection.h" 28#include "services.h" 29#include "utils.h" 30#include "policy.h" 31#include "config-parser.h" 32#include <dbus/dbus-list.h> 33#include <dbus/dbus-hash.h> 34#include <dbus/dbus-internals.h> 35 36struct BusContext 37{ 38 int refcount; 39 char *address; 40 DBusList *servers; 41 BusConnections *connections; 42 BusActivation *activation; 43 BusRegistry *registry; 44 DBusList *default_rules; /**< Default policy rules */ 45 DBusList *mandatory_rules; /**< Mandatory policy rules */ 46 DBusHashTable *rules_by_uid; /**< per-UID policy rules */ 47 DBusHashTable *rules_by_gid; /**< per-GID policy rules */ 48}; 49 50static dbus_bool_t 51server_watch_callback (DBusWatch *watch, 52 unsigned int condition, 53 void *data) 54{ 55 DBusServer *server = data; 56 57 return dbus_server_handle_watch (server, watch, condition); 58} 59 60static dbus_bool_t 61add_server_watch (DBusWatch *watch, 62 void *data) 63{ 64 return bus_loop_add_watch (watch, server_watch_callback, data, 65 NULL); 66} 67 68static void 69remove_server_watch (DBusWatch *watch, 70 void *data) 71{ 72 bus_loop_remove_watch (watch, server_watch_callback, data); 73} 74 75 76static void 77server_timeout_callback (DBusTimeout *timeout, 78 void *data) 79{ 80 /* can return FALSE on OOM but we just let it fire again later */ 81 dbus_timeout_handle (timeout); 82} 83 84static dbus_bool_t 85add_server_timeout (DBusTimeout *timeout, 86 void *data) 87{ 88 return bus_loop_add_timeout (timeout, server_timeout_callback, data, NULL); 89} 90 91static void 92remove_server_timeout (DBusTimeout *timeout, 93 void *data) 94{ 95 bus_loop_remove_timeout (timeout, server_timeout_callback, data); 96} 97 98static void 99new_connection_callback (DBusServer *server, 100 DBusConnection *new_connection, 101 void *data) 102{ 103 BusContext *context = data; 104 105 if (!bus_connections_setup_connection (context->connections, new_connection)) 106 { 107 _dbus_verbose ("No memory to setup new connection\n"); 108 109 /* if we don't do this, it will get unref'd without 110 * being disconnected... kind of strange really 111 * that we have to do this, people won't get it right 112 * in general. 113 */ 114 dbus_connection_disconnect (new_connection); 115 } 116 117 /* on OOM, we won't have ref'd the connection so it will die. */ 118} 119 120static void 121free_rule_func (void *data, 122 void *user_data) 123{ 124 BusPolicyRule *rule = data; 125 126 bus_policy_rule_unref (rule); 127} 128 129static void 130free_rule_list_func (void *data) 131{ 132 DBusList **list = data; 133 134 _dbus_list_foreach (list, free_rule_func, NULL); 135 136 _dbus_list_clear (list); 137 138 dbus_free (list); 139} 140 141static dbus_bool_t 142setup_server (BusContext *context, 143 DBusServer *server, 144 DBusError *error) 145{ 146 dbus_server_set_new_connection_function (server, 147 new_connection_callback, 148 context, NULL); 149 150 if (!dbus_server_set_watch_functions (server, 151 add_server_watch, 152 remove_server_watch, 153 NULL, 154 server, 155 NULL)) 156 { 157 BUS_SET_OOM (error); 158 return FALSE; 159 } 160 161 if (!dbus_server_set_timeout_functions (server, 162 add_server_timeout, 163 remove_server_timeout, 164 NULL, 165 server, NULL)) 166 { 167 BUS_SET_OOM (error); 168 return FALSE; 169 } 170 171 return TRUE; 172} 173 174BusContext* 175bus_context_new (const DBusString *config_file, 176 DBusError *error) 177{ 178 BusContext *context; 179 DBusList *link; 180 DBusList **addresses; 181 BusConfigParser *parser; 182 DBusString full_address; 183 const char *service_dirs[] = { NULL, NULL }; 184 185 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 186 187 if (!_dbus_string_init (&full_address, _DBUS_INT_MAX)) 188 return NULL; 189 190 parser = NULL; 191 context = NULL; 192 193 parser = bus_config_load (config_file, error); 194 if (parser == NULL) 195 goto failed; 196 197 context = dbus_new0 (BusContext, 1); 198 if (context == NULL) 199 { 200 BUS_SET_OOM (error); 201 goto failed; 202 } 203 204 context->refcount = 1; 205 206 addresses = bus_config_parser_get_addresses (parser); 207 208 link = _dbus_list_get_first_link (addresses); 209 while (link != NULL) 210 { 211 DBusServer *server; 212 213 server = dbus_server_listen (link->data, error); 214 if (server == NULL) 215 goto failed; 216 else if (!setup_server (context, server, error)) 217 goto failed; 218 219 if (!_dbus_list_append (&context->servers, server)) 220 { 221 BUS_SET_OOM (error); 222 goto failed; 223 } 224 225 link = _dbus_list_get_next_link (addresses, link); 226 } 227 228 /* We have to build the address backward, so that 229 * <listen> later in the config file have priority 230 */ 231 link = _dbus_list_get_last_link (&context->servers); 232 while (link != NULL) 233 { 234 char *addr; 235 236 addr = dbus_server_get_address (link->data); 237 if (addr == NULL) 238 { 239 BUS_SET_OOM (error); 240 goto failed; 241 } 242 243 if (_dbus_string_get_length (&full_address) > 0) 244 { 245 if (!_dbus_string_append (&full_address, ";")) 246 { 247 BUS_SET_OOM (error); 248 goto failed; 249 } 250 } 251 252 if (!_dbus_string_append (&full_address, addr)) 253 { 254 BUS_SET_OOM (error); 255 goto failed; 256 } 257 258 dbus_free (addr); 259 260 link = _dbus_list_get_prev_link (&context->servers, link); 261 } 262 263 if (!_dbus_string_copy_data (&full_address, &context->address)) 264 { 265 BUS_SET_OOM (error); 266 goto failed; 267 } 268 269 context->activation = bus_activation_new (context, &full_address, 270 service_dirs, error); 271 if (context->activation == NULL) 272 { 273 _DBUS_ASSERT_ERROR_IS_SET (error); 274 goto failed; 275 } 276 277 context->connections = bus_connections_new (context); 278 if (context->connections == NULL) 279 { 280 BUS_SET_OOM (error); 281 goto failed; 282 } 283 284 context->registry = bus_registry_new (context); 285 if (context->registry == NULL) 286 { 287 BUS_SET_OOM (error); 288 goto failed; 289 } 290 291 context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG, 292 NULL, 293 free_rule_list_func); 294 if (context->rules_by_uid == NULL) 295 { 296 BUS_SET_OOM (error); 297 goto failed; 298 } 299 300 context->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG, 301 NULL, 302 free_rule_list_func); 303 if (context->rules_by_gid == NULL) 304 { 305 BUS_SET_OOM (error); 306 goto failed; 307 } 308 309 bus_config_parser_unref (parser); 310 _dbus_string_free (&full_address); 311 312 return context; 313 314 failed: 315 if (parser != NULL) 316 bus_config_parser_unref (parser); 317 318 if (context != NULL) 319 bus_context_unref (context); 320 321 _dbus_string_free (&full_address); 322 return NULL; 323} 324 325static void 326shutdown_server (BusContext *context, 327 DBusServer *server) 328{ 329 if (server == NULL || 330 !dbus_server_get_is_connected (server)) 331 return; 332 333 if (!dbus_server_set_watch_functions (server, 334 NULL, NULL, NULL, 335 context, 336 NULL)) 337 _dbus_assert_not_reached ("setting watch functions to NULL failed"); 338 339 if (!dbus_server_set_timeout_functions (server, 340 NULL, NULL, NULL, 341 context, 342 NULL)) 343 _dbus_assert_not_reached ("setting timeout functions to NULL failed"); 344 345 dbus_server_disconnect (server); 346} 347 348void 349bus_context_shutdown (BusContext *context) 350{ 351 DBusList *link; 352 353 link = _dbus_list_get_first_link (&context->servers); 354 while (link != NULL) 355 { 356 shutdown_server (context, link->data); 357 358 link = _dbus_list_get_next_link (&context->servers, link); 359 } 360} 361 362void 363bus_context_ref (BusContext *context) 364{ 365 _dbus_assert (context->refcount > 0); 366 context->refcount += 1; 367} 368 369void 370bus_context_unref (BusContext *context) 371{ 372 _dbus_assert (context->refcount > 0); 373 context->refcount -= 1; 374 375 if (context->refcount == 0) 376 { 377 DBusList *link; 378 379 _dbus_verbose ("Finalizing bus context %p\n", context); 380 381 bus_context_shutdown (context); 382 383 if (context->connections) 384 { 385 bus_connections_unref (context->connections); 386 context->connections = NULL; 387 } 388 389 if (context->registry) 390 { 391 bus_registry_unref (context->registry); 392 context->registry = NULL; 393 } 394 395 if (context->activation) 396 { 397 bus_activation_unref (context->activation); 398 context->activation = NULL; 399 } 400 401 link = _dbus_list_get_first_link (&context->servers); 402 while (link != NULL) 403 { 404 dbus_server_unref (link->data); 405 406 link = _dbus_list_get_next_link (&context->servers, link); 407 } 408 _dbus_list_clear (&context->servers); 409 410 if (context->rules_by_uid) 411 { 412 _dbus_hash_table_unref (context->rules_by_uid); 413 context->rules_by_uid = NULL; 414 } 415 416 if (context->rules_by_gid) 417 { 418 _dbus_hash_table_unref (context->rules_by_gid); 419 context->rules_by_gid = NULL; 420 } 421 422 dbus_free (context->address); 423 dbus_free (context); 424 } 425} 426 427BusRegistry* 428bus_context_get_registry (BusContext *context) 429{ 430 return context->registry; 431} 432 433BusConnections* 434bus_context_get_connections (BusContext *context) 435{ 436 return context->connections; 437} 438 439BusActivation* 440bus_context_get_activation (BusContext *context) 441{ 442 return context->activation; 443} 444 445static dbus_bool_t 446list_allows_user (dbus_bool_t def, 447 DBusList **list, 448 unsigned long uid, 449 const unsigned long *group_ids, 450 int n_group_ids) 451{ 452 DBusList *link; 453 dbus_bool_t allowed; 454 455 allowed = def; 456 457 link = _dbus_list_get_first_link (list); 458 while (link != NULL) 459 { 460 BusPolicyRule *rule = link->data; 461 link = _dbus_list_get_next_link (list, link); 462 463 if (rule->type == BUS_POLICY_RULE_USER) 464 { 465 if (rule->d.user.uid != uid) 466 continue; 467 } 468 else if (rule->type == BUS_POLICY_RULE_GROUP) 469 { 470 int i; 471 472 i = 0; 473 while (i < n_group_ids) 474 { 475 if (rule->d.group.gid == group_ids[i]) 476 break; 477 ++i; 478 } 479 480 if (i == n_group_ids) 481 continue; 482 } 483 else 484 continue; 485 486 allowed = rule->allow; 487 } 488 489 return allowed; 490} 491 492dbus_bool_t 493bus_context_allow_user (BusContext *context, 494 unsigned long uid) 495{ 496 dbus_bool_t allowed; 497 unsigned long *group_ids; 498 int n_group_ids; 499 500 /* On OOM or error we always reject the user */ 501 if (!_dbus_get_groups (uid, &group_ids, &n_group_ids)) 502 { 503 _dbus_verbose ("Did not get any groups for UID %lu\n", 504 uid); 505 return FALSE; 506 } 507 508 allowed = FALSE; 509 510 allowed = list_allows_user (allowed, 511 &context->default_rules, 512 uid, 513 group_ids, n_group_ids); 514 515 allowed = list_allows_user (allowed, 516 &context->mandatory_rules, 517 uid, 518 group_ids, n_group_ids); 519 520 dbus_free (group_ids); 521 522 return allowed; 523} 524 525static dbus_bool_t 526add_list_to_policy (DBusList **list, 527 BusPolicy *policy) 528{ 529 DBusList *link; 530 531 link = _dbus_list_get_first_link (list); 532 while (link != NULL) 533 { 534 BusPolicyRule *rule = link->data; 535 link = _dbus_list_get_next_link (list, link); 536 537 switch (rule->type) 538 { 539 case BUS_POLICY_RULE_USER: 540 case BUS_POLICY_RULE_GROUP: 541 /* These aren't per-connection policies */ 542 break; 543 544 case BUS_POLICY_RULE_OWN: 545 case BUS_POLICY_RULE_SEND: 546 case BUS_POLICY_RULE_RECEIVE: 547 /* These are per-connection */ 548 if (!bus_policy_append_rule (policy, rule)) 549 return FALSE; 550 break; 551 } 552 } 553 554 return TRUE; 555} 556 557BusPolicy* 558bus_context_create_connection_policy (BusContext *context, 559 DBusConnection *connection) 560{ 561 BusPolicy *policy; 562 unsigned long uid; 563 DBusList **list; 564 565 _dbus_assert (dbus_connection_get_is_authenticated (connection)); 566 567 policy = bus_policy_new (); 568 if (policy == NULL) 569 return NULL; 570 571 if (!add_list_to_policy (&context->default_rules, 572 policy)) 573 goto failed; 574 575 /* we avoid the overhead of looking up user's groups 576 * if we don't have any group rules anyway 577 */ 578 if (_dbus_hash_table_get_n_entries (context->rules_by_gid) > 0) 579 { 580 const unsigned long *groups; 581 int n_groups; 582 int i; 583 584 if (!bus_connection_get_groups (connection, &groups, &n_groups)) 585 goto failed; 586 587 i = 0; 588 while (i < n_groups) 589 { 590 list = _dbus_hash_table_lookup_ulong (context->rules_by_gid, 591 groups[i]); 592 593 if (list != NULL) 594 { 595 if (!add_list_to_policy (list, policy)) 596 goto failed; 597 } 598 599 ++i; 600 } 601 } 602 603 if (!dbus_connection_get_unix_user (connection, &uid)) 604 goto failed; 605 606 list = _dbus_hash_table_lookup_ulong (context->rules_by_uid, 607 uid); 608 609 if (!add_list_to_policy (list, policy)) 610 goto failed; 611 612 if (!add_list_to_policy (&context->mandatory_rules, 613 policy)) 614 goto failed; 615 616 bus_policy_optimize (policy); 617 618 return policy; 619 620 failed: 621 bus_policy_unref (policy); 622 return NULL; 623} 624