dbus-auth.c revision 44ed933284589134603913b05f55ca55e8c5a566
1/* -*- mode: C; c-file-style: "gnu" -*- */ 2/* dbus-auth.c Authentication 3 * 4 * Copyright (C) 2002, 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#include "dbus-auth.h" 24#include "dbus-string.h" 25#include "dbus-list.h" 26#include "dbus-internals.h" 27#include "dbus-keyring.h" 28#include "dbus-sha.h" 29 30/* See doc/dbus-sasl-profile.txt */ 31 32/** 33 * @defgroup DBusAuth Authentication 34 * @ingroup DBusInternals 35 * @brief DBusAuth object 36 * 37 * DBusAuth manages the authentication negotiation when a connection 38 * is first established, and also manage any encryption used over a 39 * connection. 40 * 41 * The file doc/dbus-sasl-profile.txt documents the network protocol 42 * used for authentication. 43 * 44 * @todo some SASL profiles require sending the empty string as a 45 * challenge/response, but we don't currently allow that in our 46 * protocol. 47 * 48 * @todo DBusAuth really needs to be rewritten as an explicit state 49 * machine. Right now it's too hard to prove to yourself by inspection 50 * that it works. 51 * 52 * @todo right now sometimes both ends will block waiting for input 53 * from the other end, e.g. if there's an error during 54 * DBUS_COOKIE_SHA1. 55 * 56 * @todo the cookie keyring needs to be cached globally not just 57 * per-auth (which raises threadsafety issues too) 58 * 59 * @todo grep FIXME in dbus-auth.c 60 */ 61 62/** 63 * @defgroup DBusAuthInternals Authentication implementation details 64 * @ingroup DBusInternals 65 * @brief DBusAuth implementation details 66 * 67 * Private details of authentication code. 68 * 69 * @{ 70 */ 71 72/** 73 * Processes a command. Returns whether we had enough memory to 74 * complete the operation. 75 */ 76typedef dbus_bool_t (* DBusProcessAuthCommandFunction) (DBusAuth *auth, 77 const DBusString *command, 78 const DBusString *args); 79 80typedef struct 81{ 82 const char *command; 83 DBusProcessAuthCommandFunction func; 84} DBusAuthCommandHandler; 85 86/** 87 * This function appends an initial client response to the given string 88 */ 89typedef dbus_bool_t (* DBusInitialResponseFunction) (DBusAuth *auth, 90 DBusString *response); 91 92/** 93 * This function processes a block of data received from the peer. 94 * i.e. handles a DATA command. 95 */ 96typedef dbus_bool_t (* DBusAuthDataFunction) (DBusAuth *auth, 97 const DBusString *data); 98 99/** 100 * This function encodes a block of data from the peer. 101 */ 102typedef dbus_bool_t (* DBusAuthEncodeFunction) (DBusAuth *auth, 103 const DBusString *data, 104 DBusString *encoded); 105 106/** 107 * This function decodes a block of data from the peer. 108 */ 109typedef dbus_bool_t (* DBusAuthDecodeFunction) (DBusAuth *auth, 110 const DBusString *data, 111 DBusString *decoded); 112 113/** 114 * This function is called when the mechanism is abandoned. 115 */ 116typedef void (* DBusAuthShutdownFunction) (DBusAuth *auth); 117 118typedef struct 119{ 120 const char *mechanism; 121 DBusAuthDataFunction server_data_func; 122 DBusAuthEncodeFunction server_encode_func; 123 DBusAuthDecodeFunction server_decode_func; 124 DBusAuthShutdownFunction server_shutdown_func; 125 DBusInitialResponseFunction client_initial_response_func; 126 DBusAuthDataFunction client_data_func; 127 DBusAuthEncodeFunction client_encode_func; 128 DBusAuthDecodeFunction client_decode_func; 129 DBusAuthShutdownFunction client_shutdown_func; 130} DBusAuthMechanismHandler; 131 132/** 133 * Internal members of DBusAuth. 134 */ 135struct DBusAuth 136{ 137 int refcount; /**< reference count */ 138 139 DBusString incoming; /**< Incoming data buffer */ 140 DBusString outgoing; /**< Outgoing data buffer */ 141 142 const DBusAuthCommandHandler *handlers; /**< Handlers for commands */ 143 144 const DBusAuthMechanismHandler *mech; /**< Current auth mechanism */ 145 146 DBusString identity; /**< Current identity we're authorizing 147 * as. 148 */ 149 150 DBusCredentials credentials; /**< Credentials read from socket, 151 * fields may be -1 152 */ 153 154 DBusCredentials authorized_identity; /**< Credentials that are authorized */ 155 156 DBusCredentials desired_identity; /**< Identity client has requested */ 157 158 DBusString context; /**< Cookie scope */ 159 DBusKeyring *keyring; /**< Keyring for cookie mechanism. */ 160 int cookie_id; /**< ID of cookie to use */ 161 DBusString challenge; /**< Challenge sent to client */ 162 163 char **allowed_mechs; /**< Mechanisms we're allowed to use, 164 * or #NULL if we can use any 165 */ 166 167 unsigned int needed_memory : 1; /**< We needed memory to continue since last 168 * successful getting something done 169 */ 170 unsigned int need_disconnect : 1; /**< We've given up, time to disconnect */ 171 unsigned int authenticated : 1; /**< We are authenticated */ 172 unsigned int authenticated_pending_output : 1; /**< Authenticated once we clear outgoing buffer */ 173 unsigned int authenticated_pending_begin : 1; /**< Authenticated once we get BEGIN */ 174 unsigned int already_got_mechanisms : 1; /**< Client already got mech list */ 175 unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */ 176 unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */ 177}; 178 179typedef struct 180{ 181 DBusAuth base; 182 183 DBusList *mechs_to_try; /**< Mechanisms we got from the server that we're going to try using */ 184 185} DBusAuthClient; 186 187typedef struct 188{ 189 DBusAuth base; 190 191 int failures; /**< Number of times client has been rejected */ 192 int max_failures; /**< Number of times we reject before disconnect */ 193 194} DBusAuthServer; 195 196static dbus_bool_t process_auth (DBusAuth *auth, 197 const DBusString *command, 198 const DBusString *args); 199static dbus_bool_t process_cancel (DBusAuth *auth, 200 const DBusString *command, 201 const DBusString *args); 202static dbus_bool_t process_begin (DBusAuth *auth, 203 const DBusString *command, 204 const DBusString *args); 205static dbus_bool_t process_data_server (DBusAuth *auth, 206 const DBusString *command, 207 const DBusString *args); 208static dbus_bool_t process_error_server (DBusAuth *auth, 209 const DBusString *command, 210 const DBusString *args); 211static dbus_bool_t process_rejected (DBusAuth *auth, 212 const DBusString *command, 213 const DBusString *args); 214static dbus_bool_t process_ok (DBusAuth *auth, 215 const DBusString *command, 216 const DBusString *args); 217static dbus_bool_t process_data_client (DBusAuth *auth, 218 const DBusString *command, 219 const DBusString *args); 220static dbus_bool_t process_error_client (DBusAuth *auth, 221 const DBusString *command, 222 const DBusString *args); 223 224 225static dbus_bool_t client_try_next_mechanism (DBusAuth *auth); 226static dbus_bool_t send_rejected (DBusAuth *auth); 227 228static DBusAuthCommandHandler 229server_handlers[] = { 230 { "AUTH", process_auth }, 231 { "CANCEL", process_cancel }, 232 { "BEGIN", process_begin }, 233 { "DATA", process_data_server }, 234 { "ERROR", process_error_server }, 235 { NULL, NULL } 236}; 237 238static DBusAuthCommandHandler 239client_handlers[] = { 240 { "REJECTED", process_rejected }, 241 { "OK", process_ok }, 242 { "DATA", process_data_client }, 243 { "ERROR", process_error_client }, 244 { NULL, NULL } 245}; 246 247/** 248 * @param auth the auth conversation 249 * @returns #TRUE if the conversation is the server side 250 */ 251#define DBUS_AUTH_IS_SERVER(auth) ((auth)->handlers == server_handlers) 252/** 253 * @param auth the auth conversation 254 * @returns #TRUE if the conversation is the client side 255 */ 256#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->handlers == client_handlers) 257/** 258 * @param auth the auth conversation 259 * @returns auth cast to DBusAuthClient 260 */ 261#define DBUS_AUTH_CLIENT(auth) ((DBusAuthClient*)(auth)) 262/** 263 * @param auth the auth conversation 264 * @returns auth cast to DBusAuthServer 265 */ 266#define DBUS_AUTH_SERVER(auth) ((DBusAuthServer*)(auth)) 267 268static DBusAuth* 269_dbus_auth_new (int size) 270{ 271 DBusAuth *auth; 272 273 auth = dbus_malloc0 (size); 274 if (auth == NULL) 275 return NULL; 276 277 auth->refcount = 1; 278 279 auth->credentials.pid = -1; 280 auth->credentials.uid = -1; 281 auth->credentials.gid = -1; 282 283 auth->authorized_identity.pid = -1; 284 auth->authorized_identity.uid = -1; 285 auth->authorized_identity.gid = -1; 286 287 auth->desired_identity.pid = -1; 288 auth->desired_identity.uid = -1; 289 auth->desired_identity.gid = -1; 290 291 auth->keyring = NULL; 292 auth->cookie_id = -1; 293 294 /* note that we don't use the max string length feature, 295 * because you can't use that feature if you're going to 296 * try to recover from out-of-memory (it creates 297 * what looks like unrecoverable inability to alloc 298 * more space in the string). But we do handle 299 * overlong buffers in _dbus_auth_do_work(). 300 */ 301 302 if (!_dbus_string_init (&auth->incoming)) 303 goto enomem_0; 304 305 if (!_dbus_string_init (&auth->outgoing)) 306 goto enomem_1; 307 308 if (!_dbus_string_init (&auth->identity)) 309 goto enomem_2; 310 311 if (!_dbus_string_init (&auth->context)) 312 goto enomem_3; 313 314 if (!_dbus_string_init (&auth->challenge)) 315 goto enomem_4; 316 317 /* default context if none is specified */ 318 if (!_dbus_string_append (&auth->context, "org_freedesktop_general")) 319 goto enomem_5; 320 321 return auth; 322 323 enomem_5: 324 _dbus_string_free (&auth->challenge); 325 enomem_4: 326 _dbus_string_free (&auth->context); 327 enomem_3: 328 _dbus_string_free (&auth->identity); 329 enomem_2: 330 _dbus_string_free (&auth->outgoing); 331 enomem_1: 332 _dbus_string_free (&auth->incoming); 333 enomem_0: 334 dbus_free (auth); 335 return NULL; 336} 337 338static void 339shutdown_mech (DBusAuth *auth) 340{ 341 /* Cancel any auth */ 342 auth->authenticated_pending_begin = FALSE; 343 auth->authenticated = FALSE; 344 auth->already_asked_for_initial_response = FALSE; 345 _dbus_string_set_length (&auth->identity, 0); 346 347 auth->authorized_identity.pid = -1; 348 auth->authorized_identity.uid = -1; 349 auth->authorized_identity.gid = -1; 350 351 auth->desired_identity.pid = -1; 352 auth->desired_identity.uid = -1; 353 auth->desired_identity.gid = -1; 354 355 if (auth->mech != NULL) 356 { 357 _dbus_verbose ("Shutting down mechanism %s\n", 358 auth->mech->mechanism); 359 360 if (DBUS_AUTH_IS_CLIENT (auth)) 361 (* auth->mech->client_shutdown_func) (auth); 362 else 363 (* auth->mech->server_shutdown_func) (auth); 364 365 auth->mech = NULL; 366 } 367} 368 369/* Returns TRUE but with an empty string hash if the 370 * cookie_id isn't known. As with all this code 371 * TRUE just means we had enough memory. 372 */ 373static dbus_bool_t 374sha1_compute_hash (DBusAuth *auth, 375 int cookie_id, 376 const DBusString *server_challenge, 377 const DBusString *client_challenge, 378 DBusString *hash) 379{ 380 DBusString cookie; 381 DBusString to_hash; 382 dbus_bool_t retval; 383 384 _dbus_assert (auth->keyring != NULL); 385 386 retval = FALSE; 387 388 if (!_dbus_string_init (&cookie)) 389 return FALSE; 390 391 if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id, 392 &cookie)) 393 goto out_0; 394 395 if (_dbus_string_get_length (&cookie) == 0) 396 { 397 retval = TRUE; 398 goto out_0; 399 } 400 401 if (!_dbus_string_init (&to_hash)) 402 goto out_0; 403 404 if (!_dbus_string_copy (server_challenge, 0, 405 &to_hash, _dbus_string_get_length (&to_hash))) 406 goto out_1; 407 408 if (!_dbus_string_append (&to_hash, ":")) 409 goto out_1; 410 411 if (!_dbus_string_copy (client_challenge, 0, 412 &to_hash, _dbus_string_get_length (&to_hash))) 413 goto out_1; 414 415 if (!_dbus_string_append (&to_hash, ":")) 416 goto out_1; 417 418 if (!_dbus_string_copy (&cookie, 0, 419 &to_hash, _dbus_string_get_length (&to_hash))) 420 goto out_1; 421 422 if (!_dbus_sha_compute (&to_hash, hash)) 423 goto out_1; 424 425 retval = TRUE; 426 427 out_1: 428 _dbus_string_zero (&to_hash); 429 _dbus_string_free (&to_hash); 430 out_0: 431 _dbus_string_zero (&cookie); 432 _dbus_string_free (&cookie); 433 return retval; 434} 435 436/** http://www.ietf.org/rfc/rfc2831.txt suggests at least 64 bits of 437 * entropy, we use 128. This is the number of bytes in the random 438 * challenge. 439 */ 440#define N_CHALLENGE_BYTES (128/8) 441 442static dbus_bool_t 443sha1_handle_first_client_response (DBusAuth *auth, 444 const DBusString *data) 445{ 446 /* We haven't sent a challenge yet, we're expecting a desired 447 * username from the client. 448 */ 449 DBusString tmp; 450 DBusString tmp2; 451 dbus_bool_t retval; 452 int old_len; 453 DBusError error; 454 455 retval = FALSE; 456 457 _dbus_string_set_length (&auth->challenge, 0); 458 459 if (_dbus_string_get_length (data) > 0) 460 { 461 if (_dbus_string_get_length (&auth->identity) > 0) 462 { 463 /* Tried to send two auth identities, wtf */ 464 _dbus_verbose ("client tried to send auth identity, but we already have one\n"); 465 return send_rejected (auth); 466 } 467 else 468 { 469 /* this is our auth identity */ 470 if (!_dbus_string_copy (data, 0, &auth->identity, 0)) 471 return FALSE; 472 } 473 } 474 475 if (!_dbus_credentials_from_username (data, &auth->desired_identity)) 476 { 477 _dbus_verbose ("Did not get a valid username from client\n"); 478 return send_rejected (auth); 479 } 480 481 if (!_dbus_string_init (&tmp)) 482 return FALSE; 483 484 if (!_dbus_string_init (&tmp2)) 485 { 486 _dbus_string_free (&tmp); 487 return FALSE; 488 } 489 490 old_len = _dbus_string_get_length (&auth->outgoing); 491 492 /* we cache the keyring for speed, so here we drop it if it's the 493 * wrong one. FIXME caching the keyring here is useless since we use 494 * a different DBusAuth for every connection. 495 */ 496 if (auth->keyring && 497 !_dbus_keyring_is_for_user (auth->keyring, 498 data)) 499 { 500 _dbus_keyring_unref (auth->keyring); 501 auth->keyring = NULL; 502 } 503 504 if (auth->keyring == NULL) 505 { 506 DBusError error; 507 508 dbus_error_init (&error); 509 auth->keyring = _dbus_keyring_new_homedir (data, 510 &auth->context, 511 &error); 512 513 if (auth->keyring == NULL) 514 { 515 if (dbus_error_has_name (&error, 516 DBUS_ERROR_NO_MEMORY)) 517 { 518 dbus_error_free (&error); 519 goto out; 520 } 521 else 522 { 523 _DBUS_ASSERT_ERROR_IS_SET (&error); 524 _dbus_verbose ("Error loading keyring: %s\n", 525 error.message); 526 if (send_rejected (auth)) 527 retval = TRUE; /* retval is only about mem */ 528 dbus_error_free (&error); 529 goto out; 530 } 531 } 532 else 533 { 534 _dbus_assert (!dbus_error_is_set (&error)); 535 } 536 } 537 538 _dbus_assert (auth->keyring != NULL); 539 540 dbus_error_init (&error); 541 auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error); 542 if (auth->cookie_id < 0) 543 { 544 _DBUS_ASSERT_ERROR_IS_SET (&error); 545 _dbus_verbose ("Could not get a cookie ID to send to client: %s\n", 546 error.message); 547 if (send_rejected (auth)) 548 retval = TRUE; 549 dbus_error_free (&error); 550 goto out; 551 } 552 else 553 { 554 _dbus_assert (!dbus_error_is_set (&error)); 555 } 556 557 if (!_dbus_string_copy (&auth->context, 0, 558 &tmp2, _dbus_string_get_length (&tmp2))) 559 goto out; 560 561 if (!_dbus_string_append (&tmp2, " ")) 562 goto out; 563 564 if (!_dbus_string_append_int (&tmp2, auth->cookie_id)) 565 goto out; 566 567 if (!_dbus_string_append (&tmp2, " ")) 568 goto out; 569 570 if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES)) 571 goto out; 572 573 _dbus_string_set_length (&auth->challenge, 0); 574 if (!_dbus_string_hex_encode (&tmp, 0, &auth->challenge, 0)) 575 goto out; 576 577 if (!_dbus_string_hex_encode (&tmp, 0, &tmp2, 578 _dbus_string_get_length (&tmp2))) 579 goto out; 580 581 if (!_dbus_string_append (&auth->outgoing, 582 "DATA ")) 583 goto out; 584 585 if (!_dbus_string_base64_encode (&tmp2, 0, &auth->outgoing, 586 _dbus_string_get_length (&auth->outgoing))) 587 goto out; 588 589 if (!_dbus_string_append (&auth->outgoing, 590 "\r\n")) 591 goto out; 592 593 retval = TRUE; 594 595 out: 596 _dbus_string_zero (&tmp); 597 _dbus_string_free (&tmp); 598 _dbus_string_zero (&tmp2); 599 _dbus_string_free (&tmp2); 600 if (!retval) 601 _dbus_string_set_length (&auth->outgoing, old_len); 602 return retval; 603} 604 605static dbus_bool_t 606sha1_handle_second_client_response (DBusAuth *auth, 607 const DBusString *data) 608{ 609 /* We are expecting a response which is the hex-encoded client 610 * challenge, space, then SHA-1 hash of the concatenation of our 611 * challenge, ":", client challenge, ":", secret key, all 612 * hex-encoded. 613 */ 614 int i; 615 DBusString client_challenge; 616 DBusString client_hash; 617 dbus_bool_t retval; 618 DBusString correct_hash; 619 620 retval = FALSE; 621 622 if (!_dbus_string_find_blank (data, 0, &i)) 623 { 624 _dbus_verbose ("no space separator in client response\n"); 625 return send_rejected (auth); 626 } 627 628 if (!_dbus_string_init (&client_challenge)) 629 goto out_0; 630 631 if (!_dbus_string_init (&client_hash)) 632 goto out_1; 633 634 if (!_dbus_string_copy_len (data, 0, i, &client_challenge, 635 0)) 636 goto out_2; 637 638 _dbus_string_skip_blank (data, i, &i); 639 640 if (!_dbus_string_copy_len (data, i, 641 _dbus_string_get_length (data) - i, 642 &client_hash, 643 0)) 644 goto out_2; 645 646 if (_dbus_string_get_length (&client_challenge) == 0 || 647 _dbus_string_get_length (&client_hash) == 0) 648 { 649 _dbus_verbose ("zero-length client challenge or hash\n"); 650 if (send_rejected (auth)) 651 retval = TRUE; 652 goto out_2; 653 } 654 655 if (!_dbus_string_init (&correct_hash)) 656 goto out_2; 657 658 if (!sha1_compute_hash (auth, auth->cookie_id, 659 &auth->challenge, 660 &client_challenge, 661 &correct_hash)) 662 goto out_3; 663 664 /* if cookie_id was invalid, then we get an empty hash */ 665 if (_dbus_string_get_length (&correct_hash) == 0) 666 { 667 if (send_rejected (auth)) 668 retval = TRUE; 669 goto out_3; 670 } 671 672 if (!_dbus_string_equal (&client_hash, &correct_hash)) 673 { 674 if (send_rejected (auth)) 675 retval = TRUE; 676 goto out_3; 677 } 678 679 if (!_dbus_string_append (&auth->outgoing, 680 "OK\r\n")) 681 goto out_3; 682 683 _dbus_verbose ("authenticated client with UID %d using DBUS_COOKIE_SHA1\n", 684 auth->desired_identity.uid); 685 686 auth->authorized_identity = auth->desired_identity; 687 auth->authenticated_pending_begin = TRUE; 688 retval = TRUE; 689 690 out_3: 691 _dbus_string_zero (&correct_hash); 692 _dbus_string_free (&correct_hash); 693 out_2: 694 _dbus_string_zero (&client_hash); 695 _dbus_string_free (&client_hash); 696 out_1: 697 _dbus_string_free (&client_challenge); 698 out_0: 699 return retval; 700} 701 702static dbus_bool_t 703handle_server_data_cookie_sha1_mech (DBusAuth *auth, 704 const DBusString *data) 705{ 706 if (auth->cookie_id < 0) 707 return sha1_handle_first_client_response (auth, data); 708 else 709 return sha1_handle_second_client_response (auth, data); 710} 711 712static void 713handle_server_shutdown_cookie_sha1_mech (DBusAuth *auth) 714{ 715 auth->cookie_id = -1; 716 _dbus_string_set_length (&auth->challenge, 0); 717} 718 719static dbus_bool_t 720handle_client_initial_response_cookie_sha1_mech (DBusAuth *auth, 721 DBusString *response) 722{ 723 const DBusString *username; 724 dbus_bool_t retval; 725 726 retval = FALSE; 727 728 if (!_dbus_user_info_from_current_process (&username, 729 NULL, NULL)) 730 goto out_0; 731 732 if (!_dbus_string_base64_encode (username, 0, 733 response, 734 _dbus_string_get_length (response))) 735 goto out_0; 736 737 retval = TRUE; 738 739 out_0: 740 return retval; 741} 742 743/* FIXME if we send the server an error, right now both sides 744 * just hang. Server has to reject on getting an error, or 745 * client has to cancel. Should be in the spec. 746 */ 747static dbus_bool_t 748handle_client_data_cookie_sha1_mech (DBusAuth *auth, 749 const DBusString *data) 750{ 751 /* The data we get from the server should be the cookie context 752 * name, the cookie ID, and the server challenge, separated by 753 * spaces. We send back our challenge string and the correct hash. 754 */ 755 dbus_bool_t retval; 756 DBusString context; 757 DBusString cookie_id_str; 758 DBusString server_challenge; 759 DBusString client_challenge; 760 DBusString correct_hash; 761 DBusString tmp; 762 int i, j; 763 long val; 764 int old_len; 765 766 retval = FALSE; 767 768 if (!_dbus_string_find_blank (data, 0, &i)) 769 { 770 if (_dbus_string_append (&auth->outgoing, 771 "ERROR \"Server did not send context/ID/challenge properly\"\r\n")) 772 retval = TRUE; 773 goto out_0; 774 } 775 776 if (!_dbus_string_init (&context)) 777 goto out_0; 778 779 if (!_dbus_string_copy_len (data, 0, i, 780 &context, 0)) 781 goto out_1; 782 783 _dbus_string_skip_blank (data, i, &i); 784 if (!_dbus_string_find_blank (data, i, &j)) 785 { 786 if (_dbus_string_append (&auth->outgoing, 787 "ERROR \"Server did not send context/ID/challenge properly\"\r\n")) 788 retval = TRUE; 789 goto out_1; 790 } 791 792 if (!_dbus_string_init (&cookie_id_str)) 793 goto out_1; 794 795 if (!_dbus_string_copy_len (data, i, j - i, 796 &cookie_id_str, 0)) 797 goto out_2; 798 799 if (!_dbus_string_init (&server_challenge)) 800 goto out_2; 801 802 i = j; 803 _dbus_string_skip_blank (data, i, &i); 804 j = _dbus_string_get_length (data); 805 806 if (!_dbus_string_copy_len (data, i, j - i, 807 &server_challenge, 0)) 808 goto out_3; 809 810 if (!_dbus_keyring_validate_context (&context)) 811 { 812 if (_dbus_string_append (&auth->outgoing, 813 "ERROR \"Server sent invalid cookie context\"\r\n")) 814 retval = TRUE; 815 goto out_3; 816 } 817 818 if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL)) 819 { 820 if (_dbus_string_append (&auth->outgoing, 821 "ERROR \"Could not parse cookie ID as an integer\"\r\n")) 822 retval = TRUE; 823 goto out_3; 824 } 825 826 if (_dbus_string_get_length (&server_challenge) == 0) 827 { 828 if (_dbus_string_append (&auth->outgoing, 829 "ERROR \"Empty server challenge string\"\r\n")) 830 retval = TRUE; 831 goto out_3; 832 } 833 834 if (auth->keyring == NULL) 835 { 836 DBusError error; 837 838 dbus_error_init (&error); 839 auth->keyring = _dbus_keyring_new_homedir (NULL, 840 &context, 841 &error); 842 843 if (auth->keyring == NULL) 844 { 845 if (dbus_error_has_name (&error, 846 DBUS_ERROR_NO_MEMORY)) 847 { 848 dbus_error_free (&error); 849 goto out_3; 850 } 851 else 852 { 853 _DBUS_ASSERT_ERROR_IS_SET (&error); 854 855 _dbus_verbose ("Error loading keyring: %s\n", 856 error.message); 857 858 if (_dbus_string_append (&auth->outgoing, 859 "ERROR \"Could not load cookie file\"\r\n")) 860 retval = TRUE; /* retval is only about mem */ 861 862 dbus_error_free (&error); 863 goto out_3; 864 } 865 } 866 else 867 { 868 _dbus_assert (!dbus_error_is_set (&error)); 869 } 870 } 871 872 _dbus_assert (auth->keyring != NULL); 873 874 if (!_dbus_string_init (&tmp)) 875 goto out_3; 876 877 if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES)) 878 goto out_4; 879 880 if (!_dbus_string_init (&client_challenge)) 881 goto out_4; 882 883 if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0)) 884 goto out_5; 885 886 if (!_dbus_string_init (&correct_hash)) 887 goto out_6; 888 889 if (!sha1_compute_hash (auth, val, 890 &server_challenge, 891 &client_challenge, 892 &correct_hash)) 893 goto out_6; 894 895 if (_dbus_string_get_length (&correct_hash) == 0) 896 { 897 /* couldn't find the cookie ID or something */ 898 if (_dbus_string_append (&auth->outgoing, 899 "ERROR \"Don't have the requested cookie ID\"\r\n")) 900 retval = TRUE; 901 goto out_6; 902 } 903 904 _dbus_string_set_length (&tmp, 0); 905 906 if (!_dbus_string_copy (&client_challenge, 0, &tmp, 907 _dbus_string_get_length (&tmp))) 908 goto out_6; 909 910 if (!_dbus_string_append (&tmp, " ")) 911 goto out_6; 912 913 if (!_dbus_string_copy (&correct_hash, 0, &tmp, 914 _dbus_string_get_length (&tmp))) 915 goto out_6; 916 917 old_len = _dbus_string_get_length (&auth->outgoing); 918 if (!_dbus_string_append (&auth->outgoing, "DATA ")) 919 goto out_6; 920 921 if (!_dbus_string_base64_encode (&tmp, 0, 922 &auth->outgoing, 923 _dbus_string_get_length (&auth->outgoing))) 924 { 925 _dbus_string_set_length (&auth->outgoing, old_len); 926 goto out_6; 927 } 928 929 if (!_dbus_string_append (&auth->outgoing, "\r\n")) 930 { 931 _dbus_string_set_length (&auth->outgoing, old_len); 932 goto out_6; 933 } 934 935 retval = TRUE; 936 937 out_6: 938 _dbus_string_zero (&correct_hash); 939 _dbus_string_free (&correct_hash); 940 out_5: 941 _dbus_string_free (&client_challenge); 942 out_4: 943 _dbus_string_zero (&tmp); 944 _dbus_string_free (&tmp); 945 out_3: 946 _dbus_string_free (&server_challenge); 947 out_2: 948 _dbus_string_free (&cookie_id_str); 949 out_1: 950 _dbus_string_free (&context); 951 out_0: 952 return retval; 953} 954 955static void 956handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth) 957{ 958 auth->cookie_id = -1; 959 _dbus_string_set_length (&auth->challenge, 0); 960} 961 962static dbus_bool_t 963handle_server_data_external_mech (DBusAuth *auth, 964 const DBusString *data) 965{ 966 if (auth->credentials.uid < 0) 967 { 968 _dbus_verbose ("no credentials, mechanism EXTERNAL can't authenticate\n"); 969 return send_rejected (auth); 970 } 971 972 if (_dbus_string_get_length (data) > 0) 973 { 974 if (_dbus_string_get_length (&auth->identity) > 0) 975 { 976 /* Tried to send two auth identities, wtf */ 977 _dbus_verbose ("client tried to send auth identity, but we already have one\n"); 978 return send_rejected (auth); 979 } 980 else 981 { 982 /* this is our auth identity */ 983 if (!_dbus_string_copy (data, 0, &auth->identity, 0)) 984 return FALSE; 985 } 986 } 987 988 /* Poke client for an auth identity, if none given */ 989 if (_dbus_string_get_length (&auth->identity) == 0 && 990 !auth->already_asked_for_initial_response) 991 { 992 if (_dbus_string_append (&auth->outgoing, 993 "DATA\r\n")) 994 { 995 _dbus_verbose ("sending empty challenge asking client for auth identity\n"); 996 auth->already_asked_for_initial_response = TRUE; 997 return TRUE; 998 } 999 else 1000 return FALSE; 1001 } 1002 1003 auth->desired_identity.pid = -1; 1004 auth->desired_identity.uid = -1; 1005 auth->desired_identity.gid = -1; 1006 1007 /* If auth->identity is still empty here, then client 1008 * responded with an empty string after we poked it for 1009 * an initial response. This means to try to auth the 1010 * identity provided in the credentials. 1011 */ 1012 if (_dbus_string_get_length (&auth->identity) == 0) 1013 { 1014 auth->desired_identity.uid = auth->credentials.uid; 1015 } 1016 else 1017 { 1018 if (!_dbus_credentials_from_uid_string (&auth->identity, 1019 &auth->desired_identity)) 1020 { 1021 _dbus_verbose ("could not get credentials from uid string\n"); 1022 return send_rejected (auth); 1023 } 1024 } 1025 1026 if (auth->desired_identity.uid < 0) 1027 { 1028 _dbus_verbose ("desired UID %d is no good\n", auth->desired_identity.uid); 1029 return send_rejected (auth); 1030 } 1031 1032 if (_dbus_credentials_match (&auth->desired_identity, 1033 &auth->credentials)) 1034 { 1035 /* client has authenticated */ 1036 if (!_dbus_string_append (&auth->outgoing, 1037 "OK\r\n")) 1038 return FALSE; 1039 1040 _dbus_verbose ("authenticated client with UID %d matching socket credentials UID %d\n", 1041 auth->desired_identity.uid, 1042 auth->credentials.uid); 1043 1044 auth->authorized_identity.uid = auth->desired_identity.uid; 1045 1046 auth->authenticated_pending_begin = TRUE; 1047 1048 return TRUE; 1049 } 1050 else 1051 { 1052 _dbus_verbose ("credentials uid=%d gid=%d do not allow uid=%d gid=%d\n", 1053 auth->credentials.uid, auth->credentials.gid, 1054 auth->desired_identity.uid, auth->desired_identity.gid); 1055 return send_rejected (auth); 1056 } 1057} 1058 1059static void 1060handle_server_shutdown_external_mech (DBusAuth *auth) 1061{ 1062 1063} 1064 1065static dbus_bool_t 1066handle_client_initial_response_external_mech (DBusAuth *auth, 1067 DBusString *response) 1068{ 1069 /* We always append our UID as an initial response, so the server 1070 * doesn't have to send back an empty challenge to check whether we 1071 * want to specify an identity. i.e. this avoids a round trip that 1072 * the spec for the EXTERNAL mechanism otherwise requires. 1073 */ 1074 DBusString plaintext; 1075 1076 if (!_dbus_string_init (&plaintext)) 1077 return FALSE; 1078 1079 if (!_dbus_string_append_our_uid (&plaintext)) 1080 goto failed; 1081 1082 if (!_dbus_string_base64_encode (&plaintext, 0, 1083 response, 1084 _dbus_string_get_length (response))) 1085 goto failed; 1086 1087 _dbus_string_free (&plaintext); 1088 1089 return TRUE; 1090 1091 failed: 1092 _dbus_string_free (&plaintext); 1093 return FALSE; 1094} 1095 1096static dbus_bool_t 1097handle_client_data_external_mech (DBusAuth *auth, 1098 const DBusString *data) 1099{ 1100 1101 return TRUE; 1102} 1103 1104static void 1105handle_client_shutdown_external_mech (DBusAuth *auth) 1106{ 1107 1108} 1109 1110/* Put mechanisms here in order of preference. 1111 * What I eventually want to have is: 1112 * 1113 * - a mechanism that checks UNIX domain socket credentials 1114 * - a simple magic cookie mechanism like X11 or ICE 1115 * - mechanisms that chain to Cyrus SASL, so we can use anything it 1116 * offers such as Kerberos, X509, whatever. 1117 * 1118 */ 1119static const DBusAuthMechanismHandler 1120all_mechanisms[] = { 1121 { "EXTERNAL", 1122 handle_server_data_external_mech, 1123 NULL, NULL, 1124 handle_server_shutdown_external_mech, 1125 handle_client_initial_response_external_mech, 1126 handle_client_data_external_mech, 1127 NULL, NULL, 1128 handle_client_shutdown_external_mech }, 1129 { "DBUS_COOKIE_SHA1", 1130 handle_server_data_cookie_sha1_mech, 1131 NULL, NULL, 1132 handle_server_shutdown_cookie_sha1_mech, 1133 handle_client_initial_response_cookie_sha1_mech, 1134 handle_client_data_cookie_sha1_mech, 1135 NULL, NULL, 1136 handle_client_shutdown_cookie_sha1_mech }, 1137 { NULL, NULL } 1138}; 1139 1140static const DBusAuthMechanismHandler* 1141find_mech (const DBusString *name, 1142 char **allowed_mechs) 1143{ 1144 int i; 1145 1146 if (allowed_mechs != NULL && 1147 !_dbus_string_array_contains ((const char**) allowed_mechs, 1148 _dbus_string_get_const_data (name))) 1149 return NULL; 1150 1151 i = 0; 1152 while (all_mechanisms[i].mechanism != NULL) 1153 { 1154 if (_dbus_string_equal_c_str (name, 1155 all_mechanisms[i].mechanism)) 1156 1157 return &all_mechanisms[i]; 1158 1159 ++i; 1160 } 1161 1162 return NULL; 1163} 1164 1165static dbus_bool_t 1166send_rejected (DBusAuth *auth) 1167{ 1168 DBusString command; 1169 DBusAuthServer *server_auth; 1170 int i; 1171 1172 if (!_dbus_string_init (&command)) 1173 return FALSE; 1174 1175 if (!_dbus_string_append (&command, 1176 "REJECTED")) 1177 goto nomem; 1178 1179 i = 0; 1180 while (all_mechanisms[i].mechanism != NULL) 1181 { 1182 if (!_dbus_string_append (&command, 1183 " ")) 1184 goto nomem; 1185 1186 if (!_dbus_string_append (&command, 1187 all_mechanisms[i].mechanism)) 1188 goto nomem; 1189 1190 ++i; 1191 } 1192 1193 if (!_dbus_string_append (&command, "\r\n")) 1194 goto nomem; 1195 1196 if (!_dbus_string_copy (&command, 0, &auth->outgoing, 1197 _dbus_string_get_length (&auth->outgoing))) 1198 goto nomem; 1199 1200 shutdown_mech (auth); 1201 1202 _dbus_assert (DBUS_AUTH_IS_SERVER (auth)); 1203 server_auth = DBUS_AUTH_SERVER (auth); 1204 server_auth->failures += 1; 1205 1206 _dbus_string_free (&command); 1207 1208 return TRUE; 1209 1210 nomem: 1211 _dbus_string_free (&command); 1212 return FALSE; 1213} 1214 1215static dbus_bool_t 1216process_auth (DBusAuth *auth, 1217 const DBusString *command, 1218 const DBusString *args) 1219{ 1220 if (auth->mech) 1221 { 1222 /* We are already using a mechanism, client is on crack */ 1223 if (!_dbus_string_append (&auth->outgoing, 1224 "ERROR \"Sent AUTH while another AUTH in progress\"\r\n")) 1225 return FALSE; 1226 1227 return TRUE; 1228 } 1229 else if (_dbus_string_get_length (args) == 0) 1230 { 1231 /* No args to the auth, send mechanisms */ 1232 if (!send_rejected (auth)) 1233 return FALSE; 1234 1235 return TRUE; 1236 } 1237 else 1238 { 1239 int i; 1240 DBusString mech; 1241 DBusString base64_response; 1242 DBusString decoded_response; 1243 1244 _dbus_string_find_blank (args, 0, &i); 1245 1246 if (!_dbus_string_init (&mech)) 1247 return FALSE; 1248 1249 if (!_dbus_string_init (&base64_response)) 1250 { 1251 _dbus_string_free (&mech); 1252 return FALSE; 1253 } 1254 1255 if (!_dbus_string_init (&decoded_response)) 1256 { 1257 _dbus_string_free (&mech); 1258 _dbus_string_free (&base64_response); 1259 return FALSE; 1260 } 1261 1262 if (!_dbus_string_copy_len (args, 0, i, &mech, 0)) 1263 goto failed; 1264 1265 if (!_dbus_string_copy (args, i, &base64_response, 0)) 1266 goto failed; 1267 1268 if (!_dbus_string_base64_decode (&base64_response, 0, 1269 &decoded_response, 0)) 1270 goto failed; 1271 1272 auth->mech = find_mech (&mech, auth->allowed_mechs); 1273 if (auth->mech != NULL) 1274 { 1275 _dbus_verbose ("Trying mechanism %s with initial response of %d bytes\n", 1276 auth->mech->mechanism, 1277 _dbus_string_get_length (&decoded_response)); 1278 1279 if (!(* auth->mech->server_data_func) (auth, 1280 &decoded_response)) 1281 goto failed; 1282 } 1283 else 1284 { 1285 /* Unsupported mechanism */ 1286 if (!send_rejected (auth)) 1287 goto failed; 1288 } 1289 1290 _dbus_string_free (&mech); 1291 _dbus_string_free (&base64_response); 1292 _dbus_string_free (&decoded_response); 1293 1294 return TRUE; 1295 1296 failed: 1297 auth->mech = NULL; 1298 _dbus_string_free (&mech); 1299 _dbus_string_free (&base64_response); 1300 _dbus_string_free (&decoded_response); 1301 return FALSE; 1302 } 1303} 1304 1305static dbus_bool_t 1306process_cancel (DBusAuth *auth, 1307 const DBusString *command, 1308 const DBusString *args) 1309{ 1310 shutdown_mech (auth); 1311 1312 return TRUE; 1313} 1314 1315static dbus_bool_t 1316process_begin (DBusAuth *auth, 1317 const DBusString *command, 1318 const DBusString *args) 1319{ 1320 if (auth->authenticated_pending_begin) 1321 auth->authenticated = TRUE; 1322 else 1323 { 1324 auth->need_disconnect = TRUE; /* client trying to send data before auth, 1325 * kick it 1326 */ 1327 shutdown_mech (auth); 1328 } 1329 1330 return TRUE; 1331} 1332 1333static dbus_bool_t 1334process_data_server (DBusAuth *auth, 1335 const DBusString *command, 1336 const DBusString *args) 1337{ 1338 if (auth->mech != NULL) 1339 { 1340 DBusString decoded; 1341 1342 if (!_dbus_string_init (&decoded)) 1343 return FALSE; 1344 1345 if (!_dbus_string_base64_decode (args, 0, &decoded, 0)) 1346 { 1347 _dbus_string_free (&decoded); 1348 return FALSE; 1349 } 1350 1351#ifdef DBUS_ENABLE_VERBOSE_MODE 1352 if (_dbus_string_validate_ascii (&decoded, 0, 1353 _dbus_string_get_length (&decoded))) 1354 _dbus_verbose ("data: '%s'\n", _dbus_string_get_const_data (&decoded)); 1355#endif 1356 1357 if (!(* auth->mech->server_data_func) (auth, &decoded)) 1358 { 1359 _dbus_string_free (&decoded); 1360 return FALSE; 1361 } 1362 1363 _dbus_string_free (&decoded); 1364 } 1365 else 1366 { 1367 if (!_dbus_string_append (&auth->outgoing, 1368 "ERROR \"Not currently in an auth conversation\"\r\n")) 1369 return FALSE; 1370 } 1371 1372 return TRUE; 1373} 1374 1375static dbus_bool_t 1376process_error_server (DBusAuth *auth, 1377 const DBusString *command, 1378 const DBusString *args) 1379{ 1380 1381 return TRUE; 1382} 1383 1384/* return FALSE if no memory, TRUE if all OK */ 1385static dbus_bool_t 1386get_word (const DBusString *str, 1387 int *start, 1388 DBusString *word) 1389{ 1390 int i; 1391 1392 _dbus_string_skip_blank (str, *start, start); 1393 _dbus_string_find_blank (str, *start, &i); 1394 1395 if (i > *start) 1396 { 1397 if (!_dbus_string_copy_len (str, *start, i - *start, word, 0)) 1398 return FALSE; 1399 1400 *start = i; 1401 } 1402 1403 return TRUE; 1404} 1405 1406static dbus_bool_t 1407record_mechanisms (DBusAuth *auth, 1408 const DBusString *command, 1409 const DBusString *args) 1410{ 1411 int next; 1412 int len; 1413 1414 if (auth->already_got_mechanisms) 1415 return TRUE; 1416 1417 len = _dbus_string_get_length (args); 1418 1419 next = 0; 1420 while (next < len) 1421 { 1422 DBusString m; 1423 const DBusAuthMechanismHandler *mech; 1424 1425 if (!_dbus_string_init (&m)) 1426 goto nomem; 1427 1428 if (!get_word (args, &next, &m)) 1429 goto nomem; 1430 1431 mech = find_mech (&m, auth->allowed_mechs); 1432 1433 if (mech != NULL) 1434 { 1435 /* FIXME right now we try mechanisms in the order 1436 * the server lists them; should we do them in 1437 * some more deterministic order? 1438 * 1439 * Probably in all_mechanisms order, our order of 1440 * preference. Of course when the server is us, 1441 * it lists things in that order anyhow. 1442 */ 1443 1444 _dbus_verbose ("Adding mechanism %s to list we will try\n", 1445 mech->mechanism); 1446 1447 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try, 1448 (void*) mech)) 1449 goto nomem; 1450 } 1451 else 1452 { 1453 _dbus_verbose ("Server offered mechanism \"%s\" that we don't know how to use\n", 1454 _dbus_string_get_const_data (&m)); 1455 } 1456 1457 _dbus_string_free (&m); 1458 } 1459 1460 auth->already_got_mechanisms = TRUE; 1461 1462 return TRUE; 1463 1464 nomem: 1465 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try); 1466 1467 return FALSE; 1468} 1469 1470static dbus_bool_t 1471client_try_next_mechanism (DBusAuth *auth) 1472{ 1473 const DBusAuthMechanismHandler *mech; 1474 DBusString auth_command; 1475 DBusAuthClient *client; 1476 1477 client = DBUS_AUTH_CLIENT (auth); 1478 1479 /* Pop any mechs not in the list of allowed mechanisms */ 1480 mech = NULL; 1481 while (client->mechs_to_try != NULL) 1482 { 1483 mech = client->mechs_to_try->data; 1484 1485 if (auth->allowed_mechs != NULL && 1486 !_dbus_string_array_contains ((const char**) auth->allowed_mechs, 1487 mech->mechanism)) 1488 { 1489 /* don't try this one after all */ 1490 _dbus_verbose ("Mechanism %s isn't in the list of allowed mechanisms\n", 1491 mech->mechanism); 1492 mech = NULL; 1493 _dbus_list_pop_first (& client->mechs_to_try); 1494 } 1495 else 1496 break; /* we'll try this one */ 1497 } 1498 1499 if (mech == NULL) 1500 return FALSE; 1501 1502 if (!_dbus_string_init (&auth_command)) 1503 return FALSE; 1504 1505 if (!_dbus_string_append (&auth_command, 1506 "AUTH ")) 1507 { 1508 _dbus_string_free (&auth_command); 1509 return FALSE; 1510 } 1511 1512 if (!_dbus_string_append (&auth_command, 1513 mech->mechanism)) 1514 { 1515 _dbus_string_free (&auth_command); 1516 return FALSE; 1517 } 1518 1519 if (mech->client_initial_response_func != NULL) 1520 { 1521 if (!_dbus_string_append (&auth_command, " ")) 1522 { 1523 _dbus_string_free (&auth_command); 1524 return FALSE; 1525 } 1526 1527 if (!(* mech->client_initial_response_func) (auth, &auth_command)) 1528 { 1529 _dbus_string_free (&auth_command); 1530 return FALSE; 1531 } 1532 } 1533 1534 if (!_dbus_string_append (&auth_command, 1535 "\r\n")) 1536 { 1537 _dbus_string_free (&auth_command); 1538 return FALSE; 1539 } 1540 1541 if (!_dbus_string_copy (&auth_command, 0, 1542 &auth->outgoing, 1543 _dbus_string_get_length (&auth->outgoing))) 1544 { 1545 _dbus_string_free (&auth_command); 1546 return FALSE; 1547 } 1548 1549 auth->mech = mech; 1550 _dbus_list_pop_first (& DBUS_AUTH_CLIENT (auth)->mechs_to_try); 1551 1552 _dbus_verbose ("Trying mechanism %s\n", 1553 auth->mech->mechanism); 1554 1555 _dbus_string_free (&auth_command); 1556 1557 return TRUE; 1558} 1559 1560static dbus_bool_t 1561process_rejected (DBusAuth *auth, 1562 const DBusString *command, 1563 const DBusString *args) 1564{ 1565 shutdown_mech (auth); 1566 1567 if (!auth->already_got_mechanisms) 1568 { 1569 if (!record_mechanisms (auth, command, args)) 1570 return FALSE; 1571 } 1572 1573 if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL) 1574 { 1575 client_try_next_mechanism (auth); 1576 } 1577 else 1578 { 1579 /* Give up */ 1580 auth->need_disconnect = TRUE; 1581 } 1582 1583 return TRUE; 1584} 1585 1586static dbus_bool_t 1587process_ok (DBusAuth *auth, 1588 const DBusString *command, 1589 const DBusString *args) 1590{ 1591 if (!_dbus_string_append (&auth->outgoing, 1592 "BEGIN\r\n")) 1593 return FALSE; 1594 1595 auth->authenticated_pending_output = TRUE; 1596 1597 return TRUE; 1598} 1599 1600static dbus_bool_t 1601process_data_client (DBusAuth *auth, 1602 const DBusString *command, 1603 const DBusString *args) 1604{ 1605 if (auth->mech != NULL) 1606 { 1607 DBusString decoded; 1608 1609 if (!_dbus_string_init (&decoded)) 1610 return FALSE; 1611 1612 if (!_dbus_string_base64_decode (args, 0, &decoded, 0)) 1613 { 1614 _dbus_string_free (&decoded); 1615 return FALSE; 1616 } 1617 1618#ifdef DBUS_ENABLE_VERBOSE_MODE 1619 if (_dbus_string_validate_ascii (&decoded, 0, 1620 _dbus_string_get_length (&decoded))) 1621 { 1622 _dbus_verbose ("data: '%s'\n", 1623 _dbus_string_get_const_data (&decoded)); 1624 } 1625#endif 1626 1627 if (!(* auth->mech->client_data_func) (auth, &decoded)) 1628 { 1629 _dbus_string_free (&decoded); 1630 return FALSE; 1631 } 1632 1633 _dbus_string_free (&decoded); 1634 } 1635 else 1636 { 1637 if (!_dbus_string_append (&auth->outgoing, 1638 "ERROR \"Got DATA when not in an auth exchange\"\r\n")) 1639 return FALSE; 1640 } 1641 1642 return TRUE; 1643} 1644 1645static dbus_bool_t 1646process_error_client (DBusAuth *auth, 1647 const DBusString *command, 1648 const DBusString *args) 1649{ 1650 return TRUE; 1651} 1652 1653static dbus_bool_t 1654process_unknown (DBusAuth *auth, 1655 const DBusString *command, 1656 const DBusString *args) 1657{ 1658 if (!_dbus_string_append (&auth->outgoing, 1659 "ERROR \"Unknown command\"\r\n")) 1660 return FALSE; 1661 1662 return TRUE; 1663} 1664 1665/* returns whether to call it again right away */ 1666static dbus_bool_t 1667process_command (DBusAuth *auth) 1668{ 1669 DBusString command; 1670 DBusString args; 1671 int eol; 1672 int i, j; 1673 dbus_bool_t retval; 1674 1675 /* _dbus_verbose (" trying process_command()\n"); */ 1676 1677 retval = FALSE; 1678 1679 eol = 0; 1680 if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol)) 1681 return FALSE; 1682 1683 if (!_dbus_string_init (&command)) 1684 { 1685 auth->needed_memory = TRUE; 1686 return FALSE; 1687 } 1688 1689 if (!_dbus_string_init (&args)) 1690 { 1691 _dbus_string_free (&command); 1692 auth->needed_memory = TRUE; 1693 return FALSE; 1694 } 1695 1696 if (eol > _DBUS_ONE_MEGABYTE) 1697 { 1698 /* This is a giant line, someone is trying to hose us. */ 1699 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command too long\"\r\n")) 1700 goto out; 1701 else 1702 goto next_command; 1703 } 1704 1705 if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0)) 1706 goto out; 1707 1708 if (!_dbus_string_validate_ascii (&command, 0, 1709 _dbus_string_get_length (&command))) 1710 { 1711 _dbus_verbose ("Command contained non-ASCII chars or embedded nul\n"); 1712 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command contained non-ASCII\"\r\n")) 1713 goto out; 1714 else 1715 goto next_command; 1716 } 1717 1718 _dbus_verbose ("got command \"%s\"\n", _dbus_string_get_const_data (&command)); 1719 1720 _dbus_string_find_blank (&command, 0, &i); 1721 _dbus_string_skip_blank (&command, i, &j); 1722 1723 if (j > i) 1724 _dbus_string_delete (&command, i, j - i); 1725 1726 if (!_dbus_string_move (&command, i, &args, 0)) 1727 goto out; 1728 1729 i = 0; 1730 while (auth->handlers[i].command != NULL) 1731 { 1732 if (_dbus_string_equal_c_str (&command, 1733 auth->handlers[i].command)) 1734 { 1735 _dbus_verbose ("Processing auth command %s\n", 1736 auth->handlers[i].command); 1737 1738 if (!(* auth->handlers[i].func) (auth, &command, &args)) 1739 goto out; 1740 1741 break; 1742 } 1743 ++i; 1744 } 1745 1746 if (auth->handlers[i].command == NULL) 1747 { 1748 if (!process_unknown (auth, &command, &args)) 1749 goto out; 1750 } 1751 1752 next_command: 1753 1754 /* We've succeeded in processing the whole command so drop it out 1755 * of the incoming buffer and return TRUE to try another command. 1756 */ 1757 1758 _dbus_string_delete (&auth->incoming, 0, eol); 1759 1760 /* kill the \r\n */ 1761 _dbus_string_delete (&auth->incoming, 0, 2); 1762 1763 retval = TRUE; 1764 1765 out: 1766 _dbus_string_free (&args); 1767 _dbus_string_free (&command); 1768 1769 if (!retval) 1770 auth->needed_memory = TRUE; 1771 else 1772 auth->needed_memory = FALSE; 1773 1774 return retval; 1775} 1776 1777 1778/** @} */ 1779 1780/** 1781 * @addtogroup DBusAuth 1782 * @{ 1783 */ 1784 1785/** 1786 * Creates a new auth conversation object for the server side. 1787 * See doc/dbus-sasl-profile.txt for full details on what 1788 * this object does. 1789 * 1790 * @returns the new object or #NULL if no memory 1791 */ 1792DBusAuth* 1793_dbus_auth_server_new (void) 1794{ 1795 DBusAuth *auth; 1796 DBusAuthServer *server_auth; 1797 1798 auth = _dbus_auth_new (sizeof (DBusAuthServer)); 1799 if (auth == NULL) 1800 return NULL; 1801 1802 auth->handlers = server_handlers; 1803 1804 server_auth = DBUS_AUTH_SERVER (auth); 1805 1806 /* perhaps this should be per-mechanism with a lower 1807 * max 1808 */ 1809 server_auth->failures = 0; 1810 server_auth->max_failures = 6; 1811 1812 return auth; 1813} 1814 1815/** 1816 * Creates a new auth conversation object for the client side. 1817 * See doc/dbus-sasl-profile.txt for full details on what 1818 * this object does. 1819 * 1820 * @returns the new object or #NULL if no memory 1821 */ 1822DBusAuth* 1823_dbus_auth_client_new (void) 1824{ 1825 DBusAuth *auth; 1826 1827 auth = _dbus_auth_new (sizeof (DBusAuthClient)); 1828 if (auth == NULL) 1829 return NULL; 1830 1831 auth->handlers = client_handlers; 1832 1833 /* Add a default mechanism to try */ 1834 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try, 1835 (void*) &all_mechanisms[0])) 1836 { 1837 _dbus_auth_unref (auth); 1838 return NULL; 1839 } 1840 1841 /* Now try the mechanism we just added */ 1842 if (!client_try_next_mechanism (auth)) 1843 { 1844 _dbus_auth_unref (auth); 1845 return NULL; 1846 } 1847 1848 return auth; 1849} 1850 1851/** 1852 * Increments the refcount of an auth object. 1853 * 1854 * @param auth the auth conversation 1855 */ 1856void 1857_dbus_auth_ref (DBusAuth *auth) 1858{ 1859 _dbus_assert (auth != NULL); 1860 1861 auth->refcount += 1; 1862} 1863 1864/** 1865 * Decrements the refcount of an auth object. 1866 * 1867 * @param auth the auth conversation 1868 */ 1869void 1870_dbus_auth_unref (DBusAuth *auth) 1871{ 1872 _dbus_assert (auth != NULL); 1873 _dbus_assert (auth->refcount > 0); 1874 1875 auth->refcount -= 1; 1876 if (auth->refcount == 0) 1877 { 1878 shutdown_mech (auth); 1879 1880 if (DBUS_AUTH_IS_CLIENT (auth)) 1881 { 1882 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try); 1883 } 1884 1885 if (auth->keyring) 1886 _dbus_keyring_unref (auth->keyring); 1887 1888 _dbus_string_free (&auth->context); 1889 _dbus_string_free (&auth->challenge); 1890 _dbus_string_free (&auth->identity); 1891 _dbus_string_free (&auth->incoming); 1892 _dbus_string_free (&auth->outgoing); 1893 1894 dbus_free_string_array (auth->allowed_mechs); 1895 1896 dbus_free (auth); 1897 } 1898} 1899 1900/** 1901 * Sets an array of authentication mechanism names 1902 * that we are willing to use. 1903 * 1904 * @param auth the auth conversation 1905 * @param mechanisms #NULL-terminated array of mechanism names 1906 * @returns #FALSE if no memory 1907 */ 1908dbus_bool_t 1909_dbus_auth_set_mechanisms (DBusAuth *auth, 1910 const char **mechanisms) 1911{ 1912 char **copy; 1913 1914 if (mechanisms != NULL) 1915 { 1916 copy = _dbus_dup_string_array (mechanisms); 1917 if (copy == NULL) 1918 return FALSE; 1919 } 1920 else 1921 copy = NULL; 1922 1923 dbus_free_string_array (auth->allowed_mechs); 1924 1925 auth->allowed_mechs = copy; 1926 1927 return TRUE; 1928} 1929 1930/** 1931 * @param auth the auth conversation object 1932 * @returns #TRUE if we're in a final state 1933 */ 1934#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->need_disconnect || (auth)->authenticated) 1935 1936/** 1937 * Analyzes buffered input and moves the auth conversation forward, 1938 * returning the new state of the auth conversation. 1939 * 1940 * @param auth the auth conversation 1941 * @returns the new state 1942 */ 1943DBusAuthState 1944_dbus_auth_do_work (DBusAuth *auth) 1945{ 1946 auth->needed_memory = FALSE; 1947 1948 /* Max amount we'll buffer up before deciding someone's on crack */ 1949#define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE) 1950 1951 do 1952 { 1953 if (DBUS_AUTH_IN_END_STATE (auth)) 1954 break; 1955 1956 if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER || 1957 _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER) 1958 { 1959 auth->need_disconnect = TRUE; 1960 _dbus_verbose ("Disconnecting due to excessive data buffered in auth phase\n"); 1961 break; 1962 } 1963 1964 if (auth->mech == NULL && 1965 auth->already_got_mechanisms && 1966 DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL) 1967 { 1968 auth->need_disconnect = TRUE; 1969 _dbus_verbose ("Disconnecting because we are out of mechanisms to try using\n"); 1970 break; 1971 } 1972 } 1973 while (process_command (auth)); 1974 1975 if (DBUS_AUTH_IS_SERVER (auth) && 1976 DBUS_AUTH_SERVER (auth)->failures >= 1977 DBUS_AUTH_SERVER (auth)->max_failures) 1978 auth->need_disconnect = TRUE; 1979 1980 if (auth->need_disconnect) 1981 return DBUS_AUTH_STATE_NEED_DISCONNECT; 1982 else if (auth->authenticated) 1983 { 1984 if (_dbus_string_get_length (&auth->incoming) > 0) 1985 return DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES; 1986 else 1987 return DBUS_AUTH_STATE_AUTHENTICATED; 1988 } 1989 else if (auth->needed_memory) 1990 return DBUS_AUTH_STATE_WAITING_FOR_MEMORY; 1991 else if (_dbus_string_get_length (&auth->outgoing) > 0) 1992 return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND; 1993 else 1994 return DBUS_AUTH_STATE_WAITING_FOR_INPUT; 1995} 1996 1997/** 1998 * Gets bytes that need to be sent to the peer we're conversing with. 1999 * After writing some bytes, _dbus_auth_bytes_sent() must be called 2000 * to notify the auth object that they were written. 2001 * 2002 * @param auth the auth conversation 2003 * @param str return location for a ref to the buffer to send 2004 * @returns #FALSE if nothing to send 2005 */ 2006dbus_bool_t 2007_dbus_auth_get_bytes_to_send (DBusAuth *auth, 2008 const DBusString **str) 2009{ 2010 _dbus_assert (auth != NULL); 2011 _dbus_assert (str != NULL); 2012 2013 *str = NULL; 2014 2015 if (DBUS_AUTH_IN_END_STATE (auth)) 2016 return FALSE; 2017 2018 if (_dbus_string_get_length (&auth->outgoing) == 0) 2019 return FALSE; 2020 2021 *str = &auth->outgoing; 2022 2023 return TRUE; 2024} 2025 2026/** 2027 * Notifies the auth conversation object that 2028 * the given number of bytes of the outgoing buffer 2029 * have been written out. 2030 * 2031 * @param auth the auth conversation 2032 * @param bytes_sent number of bytes written out 2033 */ 2034void 2035_dbus_auth_bytes_sent (DBusAuth *auth, 2036 int bytes_sent) 2037{ 2038 _dbus_verbose ("Sent %d bytes of: %s\n", bytes_sent, 2039 _dbus_string_get_const_data (&auth->outgoing)); 2040 2041 _dbus_string_delete (&auth->outgoing, 2042 0, bytes_sent); 2043 2044 if (auth->authenticated_pending_output && 2045 _dbus_string_get_length (&auth->outgoing) == 0) 2046 auth->authenticated = TRUE; 2047} 2048 2049/** 2050 * Get a buffer to be used for reading bytes from the peer we're conversing 2051 * with. Bytes should be appended to this buffer. 2052 * 2053 * @param auth the auth conversation 2054 * @param buffer return location for buffer to append bytes to 2055 */ 2056void 2057_dbus_auth_get_buffer (DBusAuth *auth, 2058 DBusString **buffer) 2059{ 2060 _dbus_assert (auth != NULL); 2061 _dbus_assert (!auth->buffer_outstanding); 2062 2063 *buffer = &auth->incoming; 2064 2065 auth->buffer_outstanding = TRUE; 2066} 2067 2068/** 2069 * Returns a buffer with new data read into it. 2070 * 2071 * @param auth the auth conversation 2072 * @param buffer the buffer being returned 2073 * @param bytes_read number of new bytes added 2074 */ 2075void 2076_dbus_auth_return_buffer (DBusAuth *auth, 2077 DBusString *buffer, 2078 int bytes_read) 2079{ 2080 _dbus_assert (buffer == &auth->incoming); 2081 _dbus_assert (auth->buffer_outstanding); 2082 2083 auth->buffer_outstanding = FALSE; 2084} 2085 2086/** 2087 * Returns leftover bytes that were not used as part of the auth 2088 * conversation. These bytes will be part of the message stream 2089 * instead. This function may not be called until authentication has 2090 * succeeded. 2091 * 2092 * @param auth the auth conversation 2093 * @param str return location for pointer to string of unused bytes 2094 */ 2095void 2096_dbus_auth_get_unused_bytes (DBusAuth *auth, 2097 const DBusString **str) 2098{ 2099 if (!DBUS_AUTH_IN_END_STATE (auth)) 2100 return; 2101 2102 *str = &auth->incoming; 2103} 2104 2105 2106/** 2107 * Gets rid of unused bytes returned by _dbus_auth_get_unused_bytes() 2108 * after we've gotten them and successfully moved them elsewhere. 2109 * 2110 * @param auth the auth conversation 2111 */ 2112void 2113_dbus_auth_delete_unused_bytes (DBusAuth *auth) 2114{ 2115 if (!DBUS_AUTH_IN_END_STATE (auth)) 2116 return; 2117 2118 _dbus_string_set_length (&auth->incoming, 0); 2119} 2120 2121/** 2122 * Called post-authentication, indicates whether we need to encode 2123 * the message stream with _dbus_auth_encode_data() prior to 2124 * sending it to the peer. 2125 * 2126 * @param auth the auth conversation 2127 * @returns #TRUE if we need to encode the stream 2128 */ 2129dbus_bool_t 2130_dbus_auth_needs_encoding (DBusAuth *auth) 2131{ 2132 if (!auth->authenticated) 2133 return FALSE; 2134 2135 if (auth->mech != NULL) 2136 { 2137 if (DBUS_AUTH_IS_CLIENT (auth)) 2138 return auth->mech->client_encode_func != NULL; 2139 else 2140 return auth->mech->server_encode_func != NULL; 2141 } 2142 else 2143 return FALSE; 2144} 2145 2146/** 2147 * Called post-authentication, encodes a block of bytes for sending to 2148 * the peer. If no encoding was negotiated, just copies the bytes 2149 * (you can avoid this by checking _dbus_auth_needs_encoding()). 2150 * 2151 * @param auth the auth conversation 2152 * @param plaintext the plain text data 2153 * @param encoded initialized string to where encoded data is appended 2154 * @returns #TRUE if we had enough memory and successfully encoded 2155 */ 2156dbus_bool_t 2157_dbus_auth_encode_data (DBusAuth *auth, 2158 const DBusString *plaintext, 2159 DBusString *encoded) 2160{ 2161 _dbus_assert (plaintext != encoded); 2162 2163 if (!auth->authenticated) 2164 return FALSE; 2165 2166 if (_dbus_auth_needs_encoding (auth)) 2167 { 2168 if (DBUS_AUTH_IS_CLIENT (auth)) 2169 return (* auth->mech->client_encode_func) (auth, plaintext, encoded); 2170 else 2171 return (* auth->mech->server_encode_func) (auth, plaintext, encoded); 2172 } 2173 else 2174 { 2175 return _dbus_string_copy (plaintext, 0, encoded, 2176 _dbus_string_get_length (encoded)); 2177 } 2178} 2179 2180/** 2181 * Called post-authentication, indicates whether we need to decode 2182 * the message stream with _dbus_auth_decode_data() after 2183 * receiving it from the peer. 2184 * 2185 * @param auth the auth conversation 2186 * @returns #TRUE if we need to encode the stream 2187 */ 2188dbus_bool_t 2189_dbus_auth_needs_decoding (DBusAuth *auth) 2190{ 2191 if (!auth->authenticated) 2192 return FALSE; 2193 2194 if (auth->mech != NULL) 2195 { 2196 if (DBUS_AUTH_IS_CLIENT (auth)) 2197 return auth->mech->client_decode_func != NULL; 2198 else 2199 return auth->mech->server_decode_func != NULL; 2200 } 2201 else 2202 return FALSE; 2203} 2204 2205 2206/** 2207 * Called post-authentication, decodes a block of bytes received from 2208 * the peer. If no encoding was negotiated, just copies the bytes (you 2209 * can avoid this by checking _dbus_auth_needs_decoding()). 2210 * 2211 * @todo We need to be able to distinguish "out of memory" error 2212 * from "the data is hosed" error. 2213 * 2214 * @param auth the auth conversation 2215 * @param encoded the encoded data 2216 * @param plaintext initialized string where decoded data is appended 2217 * @returns #TRUE if we had enough memory and successfully decoded 2218 */ 2219dbus_bool_t 2220_dbus_auth_decode_data (DBusAuth *auth, 2221 const DBusString *encoded, 2222 DBusString *plaintext) 2223{ 2224 _dbus_assert (plaintext != encoded); 2225 2226 if (!auth->authenticated) 2227 return FALSE; 2228 2229 if (_dbus_auth_needs_decoding (auth)) 2230 { 2231 if (DBUS_AUTH_IS_CLIENT (auth)) 2232 return (* auth->mech->client_decode_func) (auth, encoded, plaintext); 2233 else 2234 return (* auth->mech->server_decode_func) (auth, encoded, plaintext); 2235 } 2236 else 2237 { 2238 return _dbus_string_copy (encoded, 0, plaintext, 2239 _dbus_string_get_length (plaintext)); 2240 } 2241} 2242 2243/** 2244 * Sets credentials received via reliable means from the operating 2245 * system. 2246 * 2247 * @param auth the auth conversation 2248 * @param credentials the credentials received 2249 */ 2250void 2251_dbus_auth_set_credentials (DBusAuth *auth, 2252 const DBusCredentials *credentials) 2253{ 2254 auth->credentials = *credentials; 2255} 2256 2257/** 2258 * Gets the identity we authorized the client as. Apps may have 2259 * different policies as to what identities they allow. 2260 * 2261 * @param auth the auth conversation 2262 * @param credentials the credentials we've authorized 2263 */ 2264void 2265_dbus_auth_get_identity (DBusAuth *auth, 2266 DBusCredentials *credentials) 2267{ 2268 if (auth->authenticated) 2269 { 2270 *credentials = auth->authorized_identity; 2271 } 2272 else 2273 { 2274 credentials->pid = -1; 2275 credentials->uid = -1; 2276 credentials->gid = -1; 2277 } 2278} 2279 2280/** 2281 * Sets the "authentication context" which scopes cookies 2282 * with the DBUS_COOKIE_SHA1 auth mechanism for example. 2283 * 2284 * @param auth the auth conversation 2285 * @param context the context 2286 * @returns #FALSE if no memory 2287 */ 2288dbus_bool_t 2289_dbus_auth_set_context (DBusAuth *auth, 2290 const DBusString *context) 2291{ 2292 return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context), 2293 &auth->context, 0, _dbus_string_get_length (context)); 2294} 2295 2296/** @} */ 2297 2298#ifdef DBUS_BUILD_TESTS 2299#include "dbus-test.h" 2300#include "dbus-auth-script.h" 2301#include <stdio.h> 2302 2303static dbus_bool_t 2304process_test_subdir (const DBusString *test_base_dir, 2305 const char *subdir) 2306{ 2307 DBusString test_directory; 2308 DBusString filename; 2309 DBusDirIter *dir; 2310 dbus_bool_t retval; 2311 DBusError error; 2312 2313 retval = FALSE; 2314 dir = NULL; 2315 2316 if (!_dbus_string_init (&test_directory)) 2317 _dbus_assert_not_reached ("didn't allocate test_directory\n"); 2318 2319 _dbus_string_init_const (&filename, subdir); 2320 2321 if (!_dbus_string_copy (test_base_dir, 0, 2322 &test_directory, 0)) 2323 _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory"); 2324 2325 if (!_dbus_concat_dir_and_file (&test_directory, &filename)) 2326 _dbus_assert_not_reached ("couldn't allocate full path"); 2327 2328 _dbus_string_free (&filename); 2329 if (!_dbus_string_init (&filename)) 2330 _dbus_assert_not_reached ("didn't allocate filename string\n"); 2331 2332 dbus_error_init (&error); 2333 dir = _dbus_directory_open (&test_directory, &error); 2334 if (dir == NULL) 2335 { 2336 _dbus_warn ("Could not open %s: %s\n", 2337 _dbus_string_get_const_data (&test_directory), 2338 error.message); 2339 dbus_error_free (&error); 2340 goto failed; 2341 } 2342 2343 printf ("Testing:\n"); 2344 2345 next: 2346 while (_dbus_directory_get_next_file (dir, &filename, &error)) 2347 { 2348 DBusString full_path; 2349 2350 if (!_dbus_string_init (&full_path)) 2351 _dbus_assert_not_reached ("couldn't init string"); 2352 2353 if (!_dbus_string_copy (&test_directory, 0, &full_path, 0)) 2354 _dbus_assert_not_reached ("couldn't copy dir to full_path"); 2355 2356 if (!_dbus_concat_dir_and_file (&full_path, &filename)) 2357 _dbus_assert_not_reached ("couldn't concat file to dir"); 2358 2359 if (!_dbus_string_ends_with_c_str (&filename, ".auth-script")) 2360 { 2361 _dbus_verbose ("Skipping non-.auth-script file %s\n", 2362 _dbus_string_get_const_data (&filename)); 2363 _dbus_string_free (&full_path); 2364 goto next; 2365 } 2366 2367 printf (" %s\n", _dbus_string_get_const_data (&filename)); 2368 2369 if (!_dbus_auth_script_run (&full_path)) 2370 { 2371 _dbus_string_free (&full_path); 2372 goto failed; 2373 } 2374 else 2375 _dbus_string_free (&full_path); 2376 } 2377 2378 if (dbus_error_is_set (&error)) 2379 { 2380 _dbus_warn ("Could not get next file in %s: %s\n", 2381 _dbus_string_get_const_data (&test_directory), error.message); 2382 dbus_error_free (&error); 2383 goto failed; 2384 } 2385 2386 retval = TRUE; 2387 2388 failed: 2389 2390 if (dir) 2391 _dbus_directory_close (dir); 2392 _dbus_string_free (&test_directory); 2393 _dbus_string_free (&filename); 2394 2395 return retval; 2396} 2397 2398static dbus_bool_t 2399process_test_dirs (const char *test_data_dir) 2400{ 2401 DBusString test_directory; 2402 dbus_bool_t retval; 2403 2404 retval = FALSE; 2405 2406 _dbus_string_init_const (&test_directory, test_data_dir); 2407 2408 if (!process_test_subdir (&test_directory, "auth")) 2409 goto failed; 2410 2411 retval = TRUE; 2412 2413 failed: 2414 2415 _dbus_string_free (&test_directory); 2416 2417 return retval; 2418} 2419 2420dbus_bool_t 2421_dbus_auth_test (const char *test_data_dir) 2422{ 2423 2424 if (test_data_dir == NULL) 2425 return TRUE; 2426 2427 if (!process_test_dirs (test_data_dir)) 2428 return FALSE; 2429 2430 return TRUE; 2431} 2432 2433#endif /* DBUS_BUILD_TESTS */ 2434