dbus-auth.c revision af4ef42fa5c2f51e2cac37e196f8f4426b2ba0db
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 28/* See doc/dbus-sasl-profile.txt */ 29 30/** 31 * @defgroup DBusAuth Authentication 32 * @ingroup DBusInternals 33 * @brief DBusAuth object 34 * 35 * DBusAuth manages the authentication negotiation when a connection 36 * is first established, and also manage any encryption used over a 37 * connection. 38 * 39 * The file doc/dbus-sasl-profile.txt documents the network protocol 40 * used for authentication. 41 */ 42 43/** 44 * @defgroup DBusAuthInternals Authentication implementation details 45 * @ingroup DBusInternals 46 * @brief DBusAuth implementation details 47 * 48 * Private details of authentication code. 49 * 50 * @{ 51 */ 52 53/** 54 * Processes a command. Returns whether we had enough memory to 55 * complete the operation. 56 */ 57typedef dbus_bool_t (* DBusProcessAuthCommandFunction) (DBusAuth *auth, 58 const DBusString *command, 59 const DBusString *args); 60 61typedef struct 62{ 63 const char *command; 64 DBusProcessAuthCommandFunction func; 65} DBusAuthCommandHandler; 66 67/** 68 * This function appends an initial client response to the given string 69 */ 70typedef dbus_bool_t (* DBusInitialResponseFunction) (DBusAuth *auth, 71 DBusString *response); 72 73/** 74 * This function processes a block of data received from the peer. 75 * i.e. handles a DATA command. 76 */ 77typedef dbus_bool_t (* DBusAuthDataFunction) (DBusAuth *auth, 78 const DBusString *data); 79 80/** 81 * This function encodes a block of data from the peer. 82 */ 83typedef dbus_bool_t (* DBusAuthEncodeFunction) (DBusAuth *auth, 84 const DBusString *data, 85 DBusString *encoded); 86 87/** 88 * This function decodes a block of data from the peer. 89 */ 90typedef dbus_bool_t (* DBusAuthDecodeFunction) (DBusAuth *auth, 91 const DBusString *data, 92 DBusString *decoded); 93 94/** 95 * This function is called when the mechanism is abandoned. 96 */ 97typedef void (* DBusAuthShutdownFunction) (DBusAuth *auth); 98 99typedef struct 100{ 101 const char *mechanism; 102 DBusAuthDataFunction server_data_func; 103 DBusAuthEncodeFunction server_encode_func; 104 DBusAuthDecodeFunction server_decode_func; 105 DBusAuthShutdownFunction server_shutdown_func; 106 DBusInitialResponseFunction client_initial_response_func; 107 DBusAuthDataFunction client_data_func; 108 DBusAuthEncodeFunction client_encode_func; 109 DBusAuthDecodeFunction client_decode_func; 110 DBusAuthShutdownFunction client_shutdown_func; 111} DBusAuthMechanismHandler; 112 113/** 114 * Internal members of DBusAuth. 115 */ 116struct DBusAuth 117{ 118 int refcount; /**< reference count */ 119 120 DBusString incoming; /**< Incoming data buffer */ 121 DBusString outgoing; /**< Outgoing data buffer */ 122 123 const DBusAuthCommandHandler *handlers; /**< Handlers for commands */ 124 125 const DBusAuthMechanismHandler *mech; /**< Current auth mechanism */ 126 127 DBusString identity; /**< Current identity we're authorizing 128 * as. 129 */ 130 131 DBusCredentials credentials; /**< Credentials, fields may be -1 */ 132 133 DBusCredentials authorized_identity; /**< Credentials that are authorized */ 134 135 unsigned int needed_memory : 1; /**< We needed memory to continue since last 136 * successful getting something done 137 */ 138 unsigned int need_disconnect : 1; /**< We've given up, time to disconnect */ 139 unsigned int authenticated : 1; /**< We are authenticated */ 140 unsigned int authenticated_pending_output : 1; /**< Authenticated once we clear outgoing buffer */ 141 unsigned int authenticated_pending_begin : 1; /**< Authenticated once we get BEGIN */ 142 unsigned int already_got_mechanisms : 1; /**< Client already got mech list */ 143 unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */ 144}; 145 146typedef struct 147{ 148 DBusAuth base; 149 150 DBusList *mechs_to_try; 151 152} DBusAuthClient; 153 154typedef struct 155{ 156 DBusAuth base; 157 158} DBusAuthServer; 159 160static dbus_bool_t process_auth (DBusAuth *auth, 161 const DBusString *command, 162 const DBusString *args); 163static dbus_bool_t process_cancel (DBusAuth *auth, 164 const DBusString *command, 165 const DBusString *args); 166static dbus_bool_t process_begin (DBusAuth *auth, 167 const DBusString *command, 168 const DBusString *args); 169static dbus_bool_t process_data_server (DBusAuth *auth, 170 const DBusString *command, 171 const DBusString *args); 172static dbus_bool_t process_error_server (DBusAuth *auth, 173 const DBusString *command, 174 const DBusString *args); 175static dbus_bool_t process_rejected (DBusAuth *auth, 176 const DBusString *command, 177 const DBusString *args); 178static dbus_bool_t process_ok (DBusAuth *auth, 179 const DBusString *command, 180 const DBusString *args); 181static dbus_bool_t process_data_client (DBusAuth *auth, 182 const DBusString *command, 183 const DBusString *args); 184static dbus_bool_t process_error_client (DBusAuth *auth, 185 const DBusString *command, 186 const DBusString *args); 187 188 189static dbus_bool_t client_try_next_mechanism (DBusAuth *auth); 190static dbus_bool_t send_rejected (DBusAuth *auth); 191 192static DBusAuthCommandHandler 193server_handlers[] = { 194 { "AUTH", process_auth }, 195 { "CANCEL", process_cancel }, 196 { "BEGIN", process_begin }, 197 { "DATA", process_data_server }, 198 { "ERROR", process_error_server }, 199 { NULL, NULL } 200}; 201 202static DBusAuthCommandHandler 203client_handlers[] = { 204 { "REJECTED", process_rejected }, 205 { "OK", process_ok }, 206 { "DATA", process_data_client }, 207 { "ERROR", process_error_client }, 208 { NULL, NULL } 209}; 210 211/** 212 * @param auth the auth conversation 213 * @returns #TRUE if the conversation is the server side 214 */ 215#define DBUS_AUTH_IS_SERVER(auth) ((auth)->handlers == server_handlers) 216/** 217 * @param auth the auth conversation 218 * @returns #TRUE if the conversation is the client side 219 */ 220#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->handlers == client_handlers) 221/** 222 * @param auth the auth conversation 223 * @returns auth cast to DBusAuthClient 224 */ 225#define DBUS_AUTH_CLIENT(auth) ((DBusAuthClient*)(auth)) 226/** 227 * @param auth the auth conversation 228 * @returns auth cast to DBusAuthServer 229 */ 230#define DBUS_AUTH_SERVER(auth) ((DBusAuthServer*)(auth)) 231 232static DBusAuth* 233_dbus_auth_new (int size) 234{ 235 DBusAuth *auth; 236 237 auth = dbus_malloc0 (size); 238 if (auth == NULL) 239 return NULL; 240 241 auth->refcount = 1; 242 243 auth->credentials.pid = -1; 244 auth->credentials.uid = -1; 245 auth->credentials.gid = -1; 246 247 auth->authorized_identity.pid = -1; 248 auth->authorized_identity.uid = -1; 249 auth->authorized_identity.gid = -1; 250 251 /* note that we don't use the max string length feature, 252 * because you can't use that feature if you're going to 253 * try to recover from out-of-memory (it creates 254 * what looks like unrecoverable inability to alloc 255 * more space in the string). But we do handle 256 * overlong buffers in _dbus_auth_do_work(). 257 */ 258 259 if (!_dbus_string_init (&auth->incoming, _DBUS_INT_MAX)) 260 { 261 dbus_free (auth); 262 return NULL; 263 } 264 265 if (!_dbus_string_init (&auth->outgoing, _DBUS_INT_MAX)) 266 { 267 _dbus_string_free (&auth->incoming); 268 dbus_free (auth); 269 return NULL; 270 } 271 272 if (!_dbus_string_init (&auth->identity, _DBUS_INT_MAX)) 273 { 274 _dbus_string_free (&auth->incoming); 275 _dbus_string_free (&auth->outgoing); 276 dbus_free (auth); 277 return NULL; 278 } 279 280 return auth; 281} 282 283static DBusAuthState 284get_state (DBusAuth *auth) 285{ 286 if (auth->need_disconnect) 287 return DBUS_AUTH_STATE_NEED_DISCONNECT; 288 else if (auth->authenticated) 289 { 290 if (_dbus_string_get_length (&auth->incoming) > 0) 291 return DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES; 292 else 293 return DBUS_AUTH_STATE_AUTHENTICATED; 294 } 295 else if (auth->needed_memory) 296 return DBUS_AUTH_STATE_WAITING_FOR_MEMORY; 297 else if (_dbus_string_get_length (&auth->outgoing) > 0) 298 return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND; 299 else 300 return DBUS_AUTH_STATE_WAITING_FOR_INPUT; 301} 302 303static void 304shutdown_mech (DBusAuth *auth) 305{ 306 /* Cancel any auth */ 307 auth->authenticated_pending_begin = FALSE; 308 auth->authenticated = FALSE; 309 auth->already_asked_for_initial_response = FALSE; 310 _dbus_string_set_length (&auth->identity, 0); 311 auth->authorized_identity.pid = -1; 312 auth->authorized_identity.uid = -1; 313 auth->authorized_identity.gid = -1; 314 315 if (auth->mech != NULL) 316 { 317 _dbus_verbose ("Shutting down mechanism %s\n", 318 auth->mech->mechanism); 319 320 if (DBUS_AUTH_IS_CLIENT (auth)) 321 (* auth->mech->client_shutdown_func) (auth); 322 else 323 (* auth->mech->server_shutdown_func) (auth); 324 325 auth->mech = NULL; 326 } 327} 328 329static dbus_bool_t 330handle_server_data_stupid_test_mech (DBusAuth *auth, 331 const DBusString *data) 332{ 333 if (!_dbus_string_append (&auth->outgoing, 334 "OK\r\n")) 335 return FALSE; 336 337 auth->authenticated_pending_begin = TRUE; 338 339 return TRUE; 340} 341 342static void 343handle_server_shutdown_stupid_test_mech (DBusAuth *auth) 344{ 345 346} 347 348static dbus_bool_t 349handle_client_data_stupid_test_mech (DBusAuth *auth, 350 const DBusString *data) 351{ 352 353 return TRUE; 354} 355 356static void 357handle_client_shutdown_stupid_test_mech (DBusAuth *auth) 358{ 359 360} 361 362/* the stupid test mech is a base64-encoded string; 363 * all the inefficiency, none of the security! 364 */ 365static dbus_bool_t 366handle_encode_stupid_test_mech (DBusAuth *auth, 367 const DBusString *plaintext, 368 DBusString *encoded) 369{ 370 if (!_dbus_string_base64_encode (plaintext, 0, encoded, 371 _dbus_string_get_length (encoded))) 372 return FALSE; 373 374 return TRUE; 375} 376 377static dbus_bool_t 378handle_decode_stupid_test_mech (DBusAuth *auth, 379 const DBusString *encoded, 380 DBusString *plaintext) 381{ 382 if (!_dbus_string_base64_decode (encoded, 0, plaintext, 383 _dbus_string_get_length (plaintext))) 384 return FALSE; 385 386 return TRUE; 387} 388 389static dbus_bool_t 390handle_server_data_external_mech (DBusAuth *auth, 391 const DBusString *data) 392{ 393 DBusCredentials desired_identity; 394 395 if (auth->credentials.uid < 0) 396 { 397 _dbus_verbose ("no credentials, mechanism EXTERNAL can't authenticate\n"); 398 return send_rejected (auth); 399 } 400 401 if (_dbus_string_get_length (data) > 0) 402 { 403 if (_dbus_string_get_length (&auth->identity) > 0) 404 { 405 /* Tried to send two auth identities, wtf */ 406 return send_rejected (auth); 407 } 408 else 409 { 410 /* this is our auth identity */ 411 if (!_dbus_string_copy (data, 0, &auth->identity, 0)) 412 return FALSE; 413 } 414 } 415 416 /* Poke client for an auth identity, if none given */ 417 if (_dbus_string_get_length (&auth->identity) == 0 && 418 !auth->already_asked_for_initial_response) 419 { 420 if (_dbus_string_append (&auth->outgoing, 421 "DATA\r\n")) 422 { 423 _dbus_verbose ("sending empty challenge asking client for auth identity\n"); 424 auth->already_asked_for_initial_response = TRUE; 425 return TRUE; 426 } 427 else 428 return FALSE; 429 } 430 431 desired_identity.pid = -1; 432 desired_identity.uid = -1; 433 desired_identity.gid = -1; 434 435 /* If auth->identity is still empty here, then client 436 * responded with an empty string after we poked it for 437 * an initial response. This means to try to auth the 438 * identity provided in the credentials. 439 */ 440 if (_dbus_string_get_length (&auth->identity) == 0) 441 { 442 desired_identity.uid = auth->credentials.uid; 443 } 444 else 445 { 446 if (!_dbus_credentials_from_uid_string (&auth->identity, 447 &desired_identity)) 448 return send_rejected (auth); 449 } 450 451 if (desired_identity.uid < 0) 452 { 453 _dbus_verbose ("desired UID %d is no good\n", desired_identity.uid); 454 return send_rejected (auth); 455 } 456 457 if (_dbus_credentials_match (&auth->credentials, 458 &desired_identity)) 459 { 460 /* client has authenticated */ 461 _dbus_verbose ("authenticated client with UID %d matching socket credentials UID %d\n", 462 desired_identity.uid, 463 auth->credentials.uid); 464 465 if (!_dbus_string_append (&auth->outgoing, 466 "OK\r\n")) 467 return FALSE; 468 469 auth->authorized_identity.uid = desired_identity.uid; 470 471 auth->authenticated_pending_begin = TRUE; 472 473 return TRUE; 474 } 475 else 476 { 477 return send_rejected (auth); 478 } 479} 480 481static void 482handle_server_shutdown_external_mech (DBusAuth *auth) 483{ 484 485} 486 487static dbus_bool_t 488handle_client_initial_response_external_mech (DBusAuth *auth, 489 DBusString *response) 490{ 491 /* We always append our UID as an initial response, so the server 492 * doesn't have to send back an empty challenge to check whether we 493 * want to specify an identity. i.e. this avoids a round trip that 494 * the spec for the EXTERNAL mechanism otherwise requires. 495 */ 496 DBusString plaintext; 497 498 if (!_dbus_string_init (&plaintext, _DBUS_INT_MAX)) 499 return FALSE; 500 501 if (!_dbus_string_append_our_uid (&plaintext)) 502 goto failed; 503 504 if (!_dbus_string_base64_encode (&plaintext, 0, 505 response, 506 _dbus_string_get_length (response))) 507 goto failed; 508 509 _dbus_string_free (&plaintext); 510 511 return TRUE; 512 513 failed: 514 _dbus_string_free (&plaintext); 515 return FALSE; 516} 517 518static dbus_bool_t 519handle_client_data_external_mech (DBusAuth *auth, 520 const DBusString *data) 521{ 522 523 return TRUE; 524} 525 526static void 527handle_client_shutdown_external_mech (DBusAuth *auth) 528{ 529 530} 531 532/* Put mechanisms here in order of preference. 533 * What I eventually want to have is: 534 * 535 * - a mechanism that checks UNIX domain socket credentials 536 * - a simple magic cookie mechanism like X11 or ICE 537 * - mechanisms that chain to Cyrus SASL, so we can use anything it 538 * offers such as Kerberos, X509, whatever. 539 * 540 */ 541static const DBusAuthMechanismHandler 542all_mechanisms[] = { 543 { "EXTERNAL", 544 handle_server_data_external_mech, 545 NULL, NULL, 546 handle_server_shutdown_external_mech, 547 handle_client_initial_response_external_mech, 548 handle_client_data_external_mech, 549 NULL, NULL, 550 handle_client_shutdown_external_mech }, 551 /* Obviously this has to die for production use */ 552 { "DBUS_STUPID_TEST_MECH", 553 handle_server_data_stupid_test_mech, 554 handle_encode_stupid_test_mech, 555 handle_decode_stupid_test_mech, 556 handle_server_shutdown_stupid_test_mech, 557 NULL, 558 handle_client_data_stupid_test_mech, 559 handle_encode_stupid_test_mech, 560 handle_decode_stupid_test_mech, 561 handle_client_shutdown_stupid_test_mech }, 562 { NULL, NULL } 563}; 564 565static const DBusAuthMechanismHandler* 566find_mech (const DBusString *name) 567{ 568 int i; 569 570 i = 0; 571 while (all_mechanisms[i].mechanism != NULL) 572 { 573 if (_dbus_string_equal_c_str (name, 574 all_mechanisms[i].mechanism)) 575 576 return &all_mechanisms[i]; 577 578 ++i; 579 } 580 581 return NULL; 582} 583 584static dbus_bool_t 585send_rejected (DBusAuth *auth) 586{ 587 DBusString command; 588 int i; 589 590 if (!_dbus_string_init (&command, _DBUS_INT_MAX)) 591 return FALSE; 592 593 if (!_dbus_string_append (&command, 594 "REJECTED")) 595 goto nomem; 596 597 i = 0; 598 while (all_mechanisms[i].mechanism != NULL) 599 { 600 if (!_dbus_string_append (&command, 601 " ")) 602 goto nomem; 603 604 if (!_dbus_string_append (&command, 605 all_mechanisms[i].mechanism)) 606 goto nomem; 607 608 ++i; 609 } 610 611 if (!_dbus_string_append (&command, "\r\n")) 612 goto nomem; 613 614 if (!_dbus_string_copy (&command, 0, &auth->outgoing, 615 _dbus_string_get_length (&auth->outgoing))) 616 goto nomem; 617 618 return TRUE; 619 620 nomem: 621 _dbus_string_free (&command); 622 return FALSE; 623} 624 625static dbus_bool_t 626process_auth (DBusAuth *auth, 627 const DBusString *command, 628 const DBusString *args) 629{ 630 if (auth->mech) 631 { 632 /* We are already using a mechanism, client is on crack */ 633 if (!_dbus_string_append (&auth->outgoing, 634 "ERROR \"Sent AUTH while another AUTH in progress\"\r\n")) 635 return FALSE; 636 637 return TRUE; 638 } 639 else if (_dbus_string_get_length (args) == 0) 640 { 641 /* No args to the auth, send mechanisms */ 642 if (!send_rejected (auth)) 643 return FALSE; 644 645 return TRUE; 646 } 647 else 648 { 649 int i; 650 DBusString mech; 651 DBusString base64_response; 652 DBusString decoded_response; 653 654 _dbus_string_find_blank (args, 0, &i); 655 656 if (!_dbus_string_init (&mech, _DBUS_INT_MAX)) 657 return FALSE; 658 659 if (!_dbus_string_init (&base64_response, _DBUS_INT_MAX)) 660 { 661 _dbus_string_free (&mech); 662 return FALSE; 663 } 664 665 if (!_dbus_string_init (&decoded_response, _DBUS_INT_MAX)) 666 { 667 _dbus_string_free (&mech); 668 _dbus_string_free (&base64_response); 669 return FALSE; 670 } 671 672 if (!_dbus_string_copy_len (args, 0, i, &mech, 0)) 673 goto failed; 674 675 if (!_dbus_string_copy (args, i, &base64_response, 0)) 676 goto failed; 677 678 if (!_dbus_string_base64_decode (&base64_response, 0, 679 &decoded_response, 0)) 680 goto failed; 681 682 auth->mech = find_mech (&mech); 683 if (auth->mech != NULL) 684 { 685 _dbus_verbose ("Trying mechanism %s with initial response of %d bytes\n", 686 auth->mech->mechanism, 687 _dbus_string_get_length (&decoded_response)); 688 689 if (!(* auth->mech->server_data_func) (auth, 690 &decoded_response)) 691 goto failed; 692 } 693 else 694 { 695 /* Unsupported mechanism */ 696 if (!send_rejected (auth)) 697 return FALSE; 698 } 699 700 _dbus_string_free (&mech); 701 _dbus_string_free (&base64_response); 702 _dbus_string_free (&decoded_response); 703 704 return TRUE; 705 706 failed: 707 auth->mech = NULL; 708 _dbus_string_free (&mech); 709 _dbus_string_free (&base64_response); 710 _dbus_string_free (&decoded_response); 711 return FALSE; 712 } 713} 714 715static dbus_bool_t 716process_cancel (DBusAuth *auth, 717 const DBusString *command, 718 const DBusString *args) 719{ 720 shutdown_mech (auth); 721 722 return TRUE; 723} 724 725static dbus_bool_t 726process_begin (DBusAuth *auth, 727 const DBusString *command, 728 const DBusString *args) 729{ 730 if (auth->authenticated_pending_begin) 731 auth->authenticated = TRUE; 732 else 733 { 734 auth->need_disconnect = TRUE; /* client trying to send data before auth, 735 * kick it 736 */ 737 shutdown_mech (auth); 738 } 739 740 return TRUE; 741} 742 743static dbus_bool_t 744process_data_server (DBusAuth *auth, 745 const DBusString *command, 746 const DBusString *args) 747{ 748 if (auth->mech != NULL) 749 { 750 DBusString decoded; 751 752 if (!_dbus_string_init (&decoded, _DBUS_INT_MAX)) 753 return FALSE; 754 755 if (!_dbus_string_base64_decode (args, 0, &decoded, 0)) 756 { 757 _dbus_string_free (&decoded); 758 return FALSE; 759 } 760 761 if (!(* auth->mech->server_data_func) (auth, &decoded)) 762 { 763 _dbus_string_free (&decoded); 764 return FALSE; 765 } 766 767 _dbus_string_free (&decoded); 768 } 769 else 770 { 771 if (!_dbus_string_append (&auth->outgoing, 772 "ERROR \"Not currently in an auth conversation\"\r\n")) 773 return FALSE; 774 } 775 776 return TRUE; 777} 778 779static dbus_bool_t 780process_error_server (DBusAuth *auth, 781 const DBusString *command, 782 const DBusString *args) 783{ 784 785 return TRUE; 786} 787 788/* return FALSE if no memory, TRUE if all OK */ 789static dbus_bool_t 790get_word (const DBusString *str, 791 int *start, 792 DBusString *word) 793{ 794 int i; 795 796 _dbus_string_skip_blank (str, *start, start); 797 _dbus_string_find_blank (str, *start, &i); 798 799 if (i > *start) 800 { 801 if (!_dbus_string_copy_len (str, *start, i, word, 0)) 802 return FALSE; 803 804 *start = i; 805 } 806 807 return TRUE; 808} 809 810static dbus_bool_t 811record_mechanisms (DBusAuth *auth, 812 const DBusString *command, 813 const DBusString *args) 814{ 815 int next; 816 int len; 817 818 if (auth->already_got_mechanisms) 819 return TRUE; 820 821 len = _dbus_string_get_length (args); 822 823 next = 0; 824 while (next < len) 825 { 826 DBusString m; 827 const DBusAuthMechanismHandler *mech; 828 829 if (!_dbus_string_init (&m, _DBUS_INT_MAX)) 830 goto nomem; 831 832 if (!get_word (args, &next, &m)) 833 goto nomem; 834 835 mech = find_mech (&m); 836 837 if (mech != NULL) 838 { 839 /* FIXME right now we try mechanisms in the order 840 * the server lists them; should we do them in 841 * some more deterministic order? 842 * 843 * Probably in all_mechanisms order, our order of 844 * preference. Of course when the server is us, 845 * it lists things in that order anyhow. 846 */ 847 848 _dbus_verbose ("Adding mechanism %s to list we will try\n", 849 mech->mechanism); 850 851 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try, 852 (void*) mech)) 853 goto nomem; 854 } 855 else 856 { 857 const char *s; 858 859 _dbus_string_get_const_data (&m, &s); 860 _dbus_verbose ("Server offered mechanism \"%s\" that we don't know how to use\n", 861 s); 862 } 863 864 _dbus_string_free (&m); 865 } 866 867 auth->already_got_mechanisms = TRUE; 868 869 return TRUE; 870 871 nomem: 872 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try); 873 874 return FALSE; 875} 876 877static dbus_bool_t 878client_try_next_mechanism (DBusAuth *auth) 879{ 880 const DBusAuthMechanismHandler *mech; 881 DBusString auth_command; 882 883 if (DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL) 884 return FALSE; 885 886 mech = DBUS_AUTH_CLIENT (auth)->mechs_to_try->data; 887 888 if (!_dbus_string_init (&auth_command, _DBUS_INT_MAX)) 889 return FALSE; 890 891 if (!_dbus_string_append (&auth_command, 892 "AUTH ")) 893 { 894 _dbus_string_free (&auth_command); 895 return FALSE; 896 } 897 898 if (!_dbus_string_append (&auth_command, 899 mech->mechanism)) 900 { 901 _dbus_string_free (&auth_command); 902 return FALSE; 903 } 904 905 if (mech->client_initial_response_func != NULL) 906 { 907 if (!_dbus_string_append (&auth_command, " ")) 908 { 909 _dbus_string_free (&auth_command); 910 return FALSE; 911 } 912 913 if (!(* mech->client_initial_response_func) (auth, &auth_command)) 914 { 915 _dbus_string_free (&auth_command); 916 return FALSE; 917 } 918 } 919 920 if (!_dbus_string_append (&auth_command, 921 "\r\n")) 922 { 923 _dbus_string_free (&auth_command); 924 return FALSE; 925 } 926 927 if (!_dbus_string_copy (&auth_command, 0, 928 &auth->outgoing, 929 _dbus_string_get_length (&auth->outgoing))) 930 { 931 _dbus_string_free (&auth_command); 932 return FALSE; 933 } 934 935 auth->mech = mech; 936 _dbus_list_pop_first (& DBUS_AUTH_CLIENT (auth)->mechs_to_try); 937 938 _dbus_verbose ("Trying mechanism %s\n", 939 auth->mech->mechanism); 940 941 return TRUE; 942} 943 944static dbus_bool_t 945process_rejected (DBusAuth *auth, 946 const DBusString *command, 947 const DBusString *args) 948{ 949 shutdown_mech (auth); 950 951 if (!auth->already_got_mechanisms) 952 { 953 if (!record_mechanisms (auth, command, args)) 954 return FALSE; 955 } 956 957 if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL) 958 { 959 client_try_next_mechanism (auth); 960 } 961 else 962 { 963 /* Give up */ 964 auth->need_disconnect = TRUE; 965 } 966 967 return TRUE; 968} 969 970static dbus_bool_t 971process_ok (DBusAuth *auth, 972 const DBusString *command, 973 const DBusString *args) 974{ 975 if (!_dbus_string_append (&auth->outgoing, 976 "BEGIN\r\n")) 977 return FALSE; 978 979 auth->authenticated_pending_output = TRUE; 980 981 return TRUE; 982} 983 984 985static dbus_bool_t 986process_data_client (DBusAuth *auth, 987 const DBusString *command, 988 const DBusString *args) 989{ 990 if (auth->mech != NULL) 991 { 992 DBusString decoded; 993 994 if (!_dbus_string_init (&decoded, _DBUS_INT_MAX)) 995 return FALSE; 996 997 if (!_dbus_string_base64_decode (args, 0, &decoded, 0)) 998 { 999 _dbus_string_free (&decoded); 1000 return FALSE; 1001 } 1002 1003 if (!(* auth->mech->client_data_func) (auth, &decoded)) 1004 { 1005 _dbus_string_free (&decoded); 1006 return FALSE; 1007 } 1008 1009 _dbus_string_free (&decoded); 1010 } 1011 else 1012 { 1013 if (!_dbus_string_append (&auth->outgoing, 1014 "ERROR \"Got DATA when not in an auth exchange\"\r\n")) 1015 return FALSE; 1016 } 1017 1018 return TRUE; 1019} 1020 1021static dbus_bool_t 1022process_error_client (DBusAuth *auth, 1023 const DBusString *command, 1024 const DBusString *args) 1025{ 1026 return TRUE; 1027} 1028 1029static dbus_bool_t 1030process_unknown (DBusAuth *auth, 1031 const DBusString *command, 1032 const DBusString *args) 1033{ 1034 if (!_dbus_string_append (&auth->outgoing, 1035 "ERROR \"Unknown command\"\r\n")) 1036 return FALSE; 1037 1038 return TRUE; 1039} 1040 1041/* returns whether to call it again right away */ 1042static dbus_bool_t 1043process_command (DBusAuth *auth) 1044{ 1045 DBusString command; 1046 DBusString args; 1047 int eol; 1048 int i, j; 1049 dbus_bool_t retval; 1050 1051 retval = FALSE; 1052 1053 eol = 0; 1054 if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol)) 1055 return FALSE; 1056 1057 if (!_dbus_string_init (&command, _DBUS_INT_MAX)) 1058 { 1059 auth->needed_memory = TRUE; 1060 return FALSE; 1061 } 1062 1063 if (!_dbus_string_init (&args, _DBUS_INT_MAX)) 1064 { 1065 auth->needed_memory = TRUE; 1066 return FALSE; 1067 } 1068 1069 if (eol > _DBUS_ONE_MEGABYTE) 1070 { 1071 /* This is a giant line, someone is trying to hose us. */ 1072 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command too long\"\r\n")) 1073 goto out; 1074 else 1075 goto next_command; 1076 } 1077 1078 if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0)) 1079 goto out; 1080 1081 if (!_dbus_string_validate_ascii (&command, 0, 1082 _dbus_string_get_length (&command))) 1083 { 1084 _dbus_verbose ("Command contained non-ASCII chars or embedded nul\n"); 1085 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command contained non-ASCII\"\r\n")) 1086 goto out; 1087 else 1088 goto next_command; 1089 } 1090 1091 { 1092 const char *q; 1093 _dbus_string_get_const_data (&command, &q); 1094 _dbus_verbose ("got command \"%s\"\n", q); 1095 } 1096 1097 _dbus_string_find_blank (&command, 0, &i); 1098 _dbus_string_skip_blank (&command, i, &j); 1099 1100 if (j > i) 1101 _dbus_string_delete (&command, i, j - i); 1102 1103 if (!_dbus_string_move (&command, i, &args, 0)) 1104 goto out; 1105 1106 i = 0; 1107 while (auth->handlers[i].command != NULL) 1108 { 1109 if (_dbus_string_equal_c_str (&command, 1110 auth->handlers[i].command)) 1111 { 1112 _dbus_verbose ("Processing auth command %s\n", 1113 auth->handlers[i].command); 1114 1115 if (!(* auth->handlers[i].func) (auth, &command, &args)) 1116 goto out; 1117 1118 break; 1119 } 1120 ++i; 1121 } 1122 1123 if (auth->handlers[i].command == NULL) 1124 { 1125 if (!process_unknown (auth, &command, &args)) 1126 goto out; 1127 } 1128 1129 next_command: 1130 1131 /* We've succeeded in processing the whole command so drop it out 1132 * of the incoming buffer and return TRUE to try another command. 1133 */ 1134 1135 _dbus_string_delete (&auth->incoming, 0, eol); 1136 1137 /* kill the \r\n */ 1138 _dbus_string_delete (&auth->incoming, 0, 2); 1139 1140 retval = TRUE; 1141 1142 out: 1143 _dbus_string_free (&args); 1144 _dbus_string_free (&command); 1145 1146 if (!retval) 1147 auth->needed_memory = TRUE; 1148 else 1149 auth->needed_memory = FALSE; 1150 1151 return retval; 1152} 1153 1154 1155/** @} */ 1156 1157/** 1158 * @addtogroup DBusAuth 1159 * @{ 1160 */ 1161 1162/** 1163 * Creates a new auth conversation object for the server side. 1164 * See doc/dbus-sasl-profile.txt for full details on what 1165 * this object does. 1166 * 1167 * @returns the new object or #NULL if no memory 1168 */ 1169DBusAuth* 1170_dbus_auth_server_new (void) 1171{ 1172 DBusAuth *auth; 1173 1174 auth = _dbus_auth_new (sizeof (DBusAuthServer)); 1175 if (auth == NULL) 1176 return NULL; 1177 1178 auth->handlers = server_handlers; 1179 1180 return auth; 1181} 1182 1183/** 1184 * Creates a new auth conversation object for the client side. 1185 * See doc/dbus-sasl-profile.txt for full details on what 1186 * this object does. 1187 * 1188 * @returns the new object or #NULL if no memory 1189 */ 1190DBusAuth* 1191_dbus_auth_client_new (void) 1192{ 1193 DBusAuth *auth; 1194 1195 auth = _dbus_auth_new (sizeof (DBusAuthClient)); 1196 if (auth == NULL) 1197 return NULL; 1198 1199 auth->handlers = client_handlers; 1200 1201 /* Add a default mechanism to try */ 1202 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try, 1203 (void*) &all_mechanisms[0])) 1204 { 1205 _dbus_auth_unref (auth); 1206 return NULL; 1207 } 1208 1209 /* Now try the mechanism we just added */ 1210 if (!client_try_next_mechanism (auth)) 1211 { 1212 _dbus_auth_unref (auth); 1213 return NULL; 1214 } 1215 1216 return auth; 1217} 1218 1219/** 1220 * Increments the refcount of an auth object. 1221 * 1222 * @param auth the auth conversation 1223 */ 1224void 1225_dbus_auth_ref (DBusAuth *auth) 1226{ 1227 _dbus_assert (auth != NULL); 1228 1229 auth->refcount += 1; 1230} 1231 1232/** 1233 * Decrements the refcount of an auth object. 1234 * 1235 * @param auth the auth conversation 1236 */ 1237void 1238_dbus_auth_unref (DBusAuth *auth) 1239{ 1240 _dbus_assert (auth != NULL); 1241 _dbus_assert (auth->refcount > 0); 1242 1243 auth->refcount -= 1; 1244 if (auth->refcount == 0) 1245 { 1246 shutdown_mech (auth); 1247 1248 if (DBUS_AUTH_IS_CLIENT (auth)) 1249 { 1250 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try); 1251 } 1252 1253 _dbus_string_free (&auth->identity); 1254 _dbus_string_free (&auth->incoming); 1255 _dbus_string_free (&auth->outgoing); 1256 dbus_free (auth); 1257 } 1258} 1259 1260/** 1261 * @param auth the auth conversation object 1262 * @returns #TRUE if we're in a final state 1263 */ 1264#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->need_disconnect || (auth)->authenticated) 1265 1266/** 1267 * Analyzes buffered input and moves the auth conversation forward, 1268 * returning the new state of the auth conversation. 1269 * 1270 * @param auth the auth conversation 1271 * @returns the new state 1272 */ 1273DBusAuthState 1274_dbus_auth_do_work (DBusAuth *auth) 1275{ 1276 if (DBUS_AUTH_IN_END_STATE (auth)) 1277 return get_state (auth); 1278 1279 auth->needed_memory = FALSE; 1280 1281 /* Max amount we'll buffer up before deciding someone's on crack */ 1282#define MAX_BUFFER (16 * 1024) 1283 1284 do 1285 { 1286 if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER || 1287 _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER) 1288 { 1289 auth->need_disconnect = TRUE; 1290 _dbus_verbose ("Disconnecting due to excessive data buffered in auth phase\n"); 1291 break; 1292 } 1293 1294 if (auth->mech == NULL && 1295 auth->already_got_mechanisms && 1296 DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL) 1297 { 1298 auth->need_disconnect = TRUE; 1299 _dbus_verbose ("Disconnecting because we are out of mechanisms to try using\n"); 1300 break; 1301 } 1302 } 1303 while (process_command (auth)); 1304 1305 return get_state (auth); 1306} 1307 1308/** 1309 * Gets bytes that need to be sent to the peer we're conversing with. 1310 * After writing some bytes, _dbus_auth_bytes_sent() must be called 1311 * to notify the auth object that they were written. 1312 * 1313 * @param auth the auth conversation 1314 * @param str return location for a ref to the buffer to send 1315 * @returns #FALSE if nothing to send 1316 */ 1317dbus_bool_t 1318_dbus_auth_get_bytes_to_send (DBusAuth *auth, 1319 const DBusString **str) 1320{ 1321 _dbus_assert (auth != NULL); 1322 _dbus_assert (str != NULL); 1323 1324 *str = NULL; 1325 1326 if (DBUS_AUTH_IN_END_STATE (auth)) 1327 return FALSE; 1328 1329 if (_dbus_string_get_length (&auth->outgoing) == 0) 1330 return FALSE; 1331 1332 *str = &auth->outgoing; 1333 1334 return TRUE; 1335} 1336 1337/** 1338 * Notifies the auth conversation object that 1339 * the given number of bytes of the outgoing buffer 1340 * have been written out. 1341 * 1342 * @param auth the auth conversation 1343 * @param bytes_sent number of bytes written out 1344 */ 1345void 1346_dbus_auth_bytes_sent (DBusAuth *auth, 1347 int bytes_sent) 1348{ 1349 _dbus_string_delete (&auth->outgoing, 1350 0, bytes_sent); 1351 1352 if (auth->authenticated_pending_output && 1353 _dbus_string_get_length (&auth->outgoing) == 0) 1354 auth->authenticated = TRUE; 1355} 1356 1357/** 1358 * Stores bytes received from the peer we're conversing with. 1359 * 1360 * @param auth the auth conversation 1361 * @param str the received bytes. 1362 * @returns #FALSE if not enough memory to store the bytes. 1363 */ 1364dbus_bool_t 1365_dbus_auth_bytes_received (DBusAuth *auth, 1366 const DBusString *str) 1367{ 1368 _dbus_assert (auth != NULL); 1369 _dbus_assert (str != NULL); 1370 1371 if (DBUS_AUTH_IN_END_STATE (auth)) 1372 return FALSE; 1373 1374 auth->needed_memory = FALSE; 1375 1376 if (!_dbus_string_copy (str, 0, 1377 &auth->incoming, 1378 _dbus_string_get_length (&auth->incoming))) 1379 { 1380 auth->needed_memory = TRUE; 1381 return FALSE; 1382 } 1383 1384 _dbus_auth_do_work (auth); 1385 1386 return TRUE; 1387} 1388 1389/** 1390 * Returns leftover bytes that were not used as part of the auth 1391 * conversation. These bytes will be part of the message stream 1392 * instead. This function may not be called until authentication has 1393 * succeeded. 1394 * 1395 * @param auth the auth conversation 1396 * @param str string to append the unused bytes to 1397 * @returns #FALSE if not enough memory to return the bytes 1398 */ 1399dbus_bool_t 1400_dbus_auth_get_unused_bytes (DBusAuth *auth, 1401 DBusString *str) 1402{ 1403 if (!DBUS_AUTH_IN_END_STATE (auth)) 1404 return FALSE; 1405 1406 if (!_dbus_string_move (&auth->incoming, 1407 0, str, 1408 _dbus_string_get_length (str))) 1409 return FALSE; 1410 1411 return TRUE; 1412} 1413 1414/** 1415 * Called post-authentication, indicates whether we need to encode 1416 * the message stream with _dbus_auth_encode_data() prior to 1417 * sending it to the peer. 1418 * 1419 * @param auth the auth conversation 1420 * @returns #TRUE if we need to encode the stream 1421 */ 1422dbus_bool_t 1423_dbus_auth_needs_encoding (DBusAuth *auth) 1424{ 1425 if (!auth->authenticated) 1426 return FALSE; 1427 1428 if (auth->mech != NULL) 1429 { 1430 if (DBUS_AUTH_IS_CLIENT (auth)) 1431 return auth->mech->client_encode_func != NULL; 1432 else 1433 return auth->mech->server_encode_func != NULL; 1434 } 1435 else 1436 return FALSE; 1437} 1438 1439/** 1440 * Called post-authentication, encodes a block of bytes for sending to 1441 * the peer. If no encoding was negotiated, just copies the bytes 1442 * (you can avoid this by checking _dbus_auth_needs_encoding()). 1443 * 1444 * @param auth the auth conversation 1445 * @param plaintext the plain text data 1446 * @param encoded initialized string to where encoded data is appended 1447 * @returns #TRUE if we had enough memory and successfully encoded 1448 */ 1449dbus_bool_t 1450_dbus_auth_encode_data (DBusAuth *auth, 1451 const DBusString *plaintext, 1452 DBusString *encoded) 1453{ 1454 _dbus_assert (plaintext != encoded); 1455 1456 if (!auth->authenticated) 1457 return FALSE; 1458 1459 if (_dbus_auth_needs_encoding (auth)) 1460 { 1461 if (DBUS_AUTH_IS_CLIENT (auth)) 1462 return (* auth->mech->client_encode_func) (auth, plaintext, encoded); 1463 else 1464 return (* auth->mech->server_encode_func) (auth, plaintext, encoded); 1465 } 1466 else 1467 { 1468 return _dbus_string_copy (plaintext, 0, encoded, 1469 _dbus_string_get_length (encoded)); 1470 } 1471} 1472 1473/** 1474 * Called post-authentication, indicates whether we need to decode 1475 * the message stream with _dbus_auth_decode_data() after 1476 * receiving it from the peer. 1477 * 1478 * @param auth the auth conversation 1479 * @returns #TRUE if we need to encode the stream 1480 */ 1481dbus_bool_t 1482_dbus_auth_needs_decoding (DBusAuth *auth) 1483{ 1484 if (!auth->authenticated) 1485 return FALSE; 1486 1487 if (auth->mech != NULL) 1488 { 1489 if (DBUS_AUTH_IS_CLIENT (auth)) 1490 return auth->mech->client_decode_func != NULL; 1491 else 1492 return auth->mech->server_decode_func != NULL; 1493 } 1494 else 1495 return FALSE; 1496} 1497 1498 1499/** 1500 * Called post-authentication, decodes a block of bytes received from 1501 * the peer. If no encoding was negotiated, just copies the bytes (you 1502 * can avoid this by checking _dbus_auth_needs_decoding()). 1503 * 1504 * @todo We need to be able to distinguish "out of memory" error 1505 * from "the data is hosed" error. 1506 * 1507 * @param auth the auth conversation 1508 * @param encoded the encoded data 1509 * @param plaintext initialized string where decoded data is appended 1510 * @returns #TRUE if we had enough memory and successfully decoded 1511 */ 1512dbus_bool_t 1513_dbus_auth_decode_data (DBusAuth *auth, 1514 const DBusString *encoded, 1515 DBusString *plaintext) 1516{ 1517 _dbus_assert (plaintext != encoded); 1518 1519 if (!auth->authenticated) 1520 return FALSE; 1521 1522 if (_dbus_auth_needs_decoding (auth)) 1523 { 1524 if (DBUS_AUTH_IS_CLIENT (auth)) 1525 return (* auth->mech->client_decode_func) (auth, encoded, plaintext); 1526 else 1527 return (* auth->mech->server_decode_func) (auth, encoded, plaintext); 1528 } 1529 else 1530 { 1531 return _dbus_string_copy (encoded, 0, plaintext, 1532 _dbus_string_get_length (plaintext)); 1533 } 1534} 1535 1536/** 1537 * Sets credentials received via reliable means from the operating 1538 * system. 1539 * 1540 * @param auth the auth conversation 1541 * @param credentials the credentials received 1542 */ 1543void 1544_dbus_auth_set_credentials (DBusAuth *auth, 1545 const DBusCredentials *credentials) 1546{ 1547 auth->credentials = *credentials; 1548} 1549 1550/** 1551 * Gets the identity we authorized the client as. Apps may have 1552 * different policies as to what identities they allow. 1553 * 1554 * @param auth the auth conversation 1555 * @param credentials the credentials we've authorized 1556 */ 1557void 1558_dbus_auth_get_identity (DBusAuth *auth, 1559 DBusCredentials *credentials) 1560{ 1561 if (auth->authenticated) 1562 { 1563 *credentials = auth->authorized_identity; 1564 } 1565 else 1566 { 1567 credentials->pid = -1; 1568 credentials->uid = -1; 1569 credentials->gid = -1; 1570 } 1571} 1572 1573 1574/** @} */ 1575