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