Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

dbus-auth.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */ 00002 /* dbus-auth.c Authentication 00003 * 00004 * Copyright (C) 2002, 2003, 2004 Red Hat Inc. 00005 * 00006 * Licensed under the Academic Free License version 2.1 00007 * 00008 * This program is free software; you can redistribute it and/or modify 00009 * it under the terms of the GNU General Public License as published by 00010 * the Free Software Foundation; either version 2 of the License, or 00011 * (at your option) any later version. 00012 * 00013 * This program is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 * GNU General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU General Public License 00019 * along with this program; if not, write to the Free Software 00020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00021 * 00022 */ 00023 #include "dbus-auth.h" 00024 #include "dbus-string.h" 00025 #include "dbus-list.h" 00026 #include "dbus-internals.h" 00027 #include "dbus-keyring.h" 00028 #include "dbus-sha.h" 00029 #include "dbus-userdb.h" 00030 #include "dbus-protocol.h" 00031 00072 typedef dbus_bool_t (* DBusInitialResponseFunction) (DBusAuth *auth, 00073 DBusString *response); 00074 00079 typedef dbus_bool_t (* DBusAuthDataFunction) (DBusAuth *auth, 00080 const DBusString *data); 00081 00085 typedef dbus_bool_t (* DBusAuthEncodeFunction) (DBusAuth *auth, 00086 const DBusString *data, 00087 DBusString *encoded); 00088 00092 typedef dbus_bool_t (* DBusAuthDecodeFunction) (DBusAuth *auth, 00093 const DBusString *data, 00094 DBusString *decoded); 00095 00099 typedef void (* DBusAuthShutdownFunction) (DBusAuth *auth); 00100 00104 typedef struct 00105 { 00106 const char *mechanism; 00107 DBusAuthDataFunction server_data_func; 00108 DBusAuthEncodeFunction server_encode_func; 00109 DBusAuthDecodeFunction server_decode_func; 00110 DBusAuthShutdownFunction server_shutdown_func; 00111 DBusInitialResponseFunction client_initial_response_func; 00112 DBusAuthDataFunction client_data_func; 00113 DBusAuthEncodeFunction client_encode_func; 00114 DBusAuthDecodeFunction client_decode_func; 00115 DBusAuthShutdownFunction client_shutdown_func; 00116 } DBusAuthMechanismHandler; 00117 00121 typedef enum { 00122 DBUS_AUTH_COMMAND_AUTH, 00123 DBUS_AUTH_COMMAND_CANCEL, 00124 DBUS_AUTH_COMMAND_DATA, 00125 DBUS_AUTH_COMMAND_BEGIN, 00126 DBUS_AUTH_COMMAND_REJECTED, 00127 DBUS_AUTH_COMMAND_OK, 00128 DBUS_AUTH_COMMAND_ERROR, 00129 DBUS_AUTH_COMMAND_UNKNOWN 00130 } DBusAuthCommand; 00131 00137 typedef dbus_bool_t (* DBusAuthStateFunction) (DBusAuth *auth, 00138 DBusAuthCommand command, 00139 const DBusString *args); 00140 00144 typedef struct 00145 { 00146 const char *name; 00147 DBusAuthStateFunction handler; 00148 } DBusAuthStateData; 00149 00153 struct DBusAuth 00154 { 00155 int refcount; 00156 const char *side; 00158 DBusString incoming; 00159 DBusString outgoing; 00161 const DBusAuthStateData *state; 00163 const DBusAuthMechanismHandler *mech; 00165 DBusString identity; 00169 DBusCredentials credentials; 00173 DBusCredentials authorized_identity; 00175 DBusCredentials desired_identity; 00177 DBusString context; 00178 DBusKeyring *keyring; 00179 int cookie_id; 00180 DBusString challenge; 00182 char **allowed_mechs; 00186 unsigned int needed_memory : 1; 00189 unsigned int already_got_mechanisms : 1; 00190 unsigned int already_asked_for_initial_response : 1; 00191 unsigned int buffer_outstanding : 1; 00192 }; 00193 00197 typedef struct 00198 { 00199 DBusAuth base; 00201 DBusList *mechs_to_try; 00203 } DBusAuthClient; 00204 00208 typedef struct 00209 { 00210 DBusAuth base; 00212 int failures; 00213 int max_failures; 00215 } DBusAuthServer; 00216 00217 static void goto_state (DBusAuth *auth, 00218 const DBusAuthStateData *new_state); 00219 static dbus_bool_t send_auth (DBusAuth *auth, 00220 const DBusAuthMechanismHandler *mech); 00221 static dbus_bool_t send_data (DBusAuth *auth, 00222 DBusString *data); 00223 static dbus_bool_t send_rejected (DBusAuth *auth); 00224 static dbus_bool_t send_error (DBusAuth *auth, 00225 const char *message); 00226 static dbus_bool_t send_ok (DBusAuth *auth); 00227 static dbus_bool_t send_begin (DBusAuth *auth); 00228 static dbus_bool_t send_cancel (DBusAuth *auth); 00229 00234 static dbus_bool_t handle_server_state_waiting_for_auth (DBusAuth *auth, 00235 DBusAuthCommand command, 00236 const DBusString *args); 00237 static dbus_bool_t handle_server_state_waiting_for_data (DBusAuth *auth, 00238 DBusAuthCommand command, 00239 const DBusString *args); 00240 static dbus_bool_t handle_server_state_waiting_for_begin (DBusAuth *auth, 00241 DBusAuthCommand command, 00242 const DBusString *args); 00243 00244 static const DBusAuthStateData server_state_waiting_for_auth = { 00245 "WaitingForAuth", handle_server_state_waiting_for_auth 00246 }; 00247 static const DBusAuthStateData server_state_waiting_for_data = { 00248 "WaitingForData", handle_server_state_waiting_for_data 00249 }; 00250 static const DBusAuthStateData server_state_waiting_for_begin = { 00251 "WaitingForBegin", handle_server_state_waiting_for_begin 00252 }; 00253 00258 static dbus_bool_t handle_client_state_waiting_for_data (DBusAuth *auth, 00259 DBusAuthCommand command, 00260 const DBusString *args); 00261 static dbus_bool_t handle_client_state_waiting_for_ok (DBusAuth *auth, 00262 DBusAuthCommand command, 00263 const DBusString *args); 00264 static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth *auth, 00265 DBusAuthCommand command, 00266 const DBusString *args); 00267 00268 static const DBusAuthStateData client_state_need_send_auth = { 00269 "NeedSendAuth", NULL 00270 }; 00271 static const DBusAuthStateData client_state_waiting_for_data = { 00272 "WaitingForData", handle_client_state_waiting_for_data 00273 }; 00274 static const DBusAuthStateData client_state_waiting_for_ok = { 00275 "WaitingForOK", handle_client_state_waiting_for_ok 00276 }; 00277 static const DBusAuthStateData client_state_waiting_for_reject = { 00278 "WaitingForReject", handle_client_state_waiting_for_reject 00279 }; 00280 00285 static const DBusAuthStateData common_state_authenticated = { 00286 "Authenticated", NULL 00287 }; 00288 00289 static const DBusAuthStateData common_state_need_disconnect = { 00290 "NeedDisconnect", NULL 00291 }; 00292 00293 static const char auth_side_client[] = "client"; 00294 static const char auth_side_server[] = "server"; 00299 #define DBUS_AUTH_IS_SERVER(auth) ((auth)->side == auth_side_server) 00300 00304 #define DBUS_AUTH_IS_CLIENT(auth) ((auth)->side == auth_side_client) 00305 00309 #define DBUS_AUTH_CLIENT(auth) ((DBusAuthClient*)(auth)) 00310 00314 #define DBUS_AUTH_SERVER(auth) ((DBusAuthServer*)(auth)) 00315 00321 #define DBUS_AUTH_NAME(auth) ((auth)->side) 00322 00323 static DBusAuth* 00324 _dbus_auth_new (int size) 00325 { 00326 DBusAuth *auth; 00327 00328 auth = dbus_malloc0 (size); 00329 if (auth == NULL) 00330 return NULL; 00331 00332 auth->refcount = 1; 00333 00334 _dbus_credentials_clear (&auth->credentials); 00335 _dbus_credentials_clear (&auth->authorized_identity); 00336 _dbus_credentials_clear (&auth->desired_identity); 00337 00338 auth->keyring = NULL; 00339 auth->cookie_id = -1; 00340 00341 /* note that we don't use the max string length feature, 00342 * because you can't use that feature if you're going to 00343 * try to recover from out-of-memory (it creates 00344 * what looks like unrecoverable inability to alloc 00345 * more space in the string). But we do handle 00346 * overlong buffers in _dbus_auth_do_work(). 00347 */ 00348 00349 if (!_dbus_string_init (&auth->incoming)) 00350 goto enomem_0; 00351 00352 if (!_dbus_string_init (&auth->outgoing)) 00353 goto enomem_1; 00354 00355 if (!_dbus_string_init (&auth->identity)) 00356 goto enomem_2; 00357 00358 if (!_dbus_string_init (&auth->context)) 00359 goto enomem_3; 00360 00361 if (!_dbus_string_init (&auth->challenge)) 00362 goto enomem_4; 00363 00364 /* default context if none is specified */ 00365 if (!_dbus_string_append (&auth->context, "org_freedesktop_general")) 00366 goto enomem_5; 00367 00368 return auth; 00369 00370 enomem_5: 00371 _dbus_string_free (&auth->challenge); 00372 enomem_4: 00373 _dbus_string_free (&auth->context); 00374 enomem_3: 00375 _dbus_string_free (&auth->identity); 00376 enomem_2: 00377 _dbus_string_free (&auth->outgoing); 00378 enomem_1: 00379 _dbus_string_free (&auth->incoming); 00380 enomem_0: 00381 dbus_free (auth); 00382 return NULL; 00383 } 00384 00385 static void 00386 shutdown_mech (DBusAuth *auth) 00387 { 00388 /* Cancel any auth */ 00389 auth->already_asked_for_initial_response = FALSE; 00390 _dbus_string_set_length (&auth->identity, 0); 00391 00392 _dbus_credentials_clear (&auth->authorized_identity); 00393 _dbus_credentials_clear (&auth->desired_identity); 00394 00395 if (auth->mech != NULL) 00396 { 00397 _dbus_verbose ("%s: Shutting down mechanism %s\n", 00398 DBUS_AUTH_NAME (auth), auth->mech->mechanism); 00399 00400 if (DBUS_AUTH_IS_CLIENT (auth)) 00401 (* auth->mech->client_shutdown_func) (auth); 00402 else 00403 (* auth->mech->server_shutdown_func) (auth); 00404 00405 auth->mech = NULL; 00406 } 00407 } 00408 00409 /* Returns TRUE but with an empty string hash if the 00410 * cookie_id isn't known. As with all this code 00411 * TRUE just means we had enough memory. 00412 */ 00413 static dbus_bool_t 00414 sha1_compute_hash (DBusAuth *auth, 00415 int cookie_id, 00416 const DBusString *server_challenge, 00417 const DBusString *client_challenge, 00418 DBusString *hash) 00419 { 00420 DBusString cookie; 00421 DBusString to_hash; 00422 dbus_bool_t retval; 00423 00424 _dbus_assert (auth->keyring != NULL); 00425 00426 retval = FALSE; 00427 00428 if (!_dbus_string_init (&cookie)) 00429 return FALSE; 00430 00431 if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id, 00432 &cookie)) 00433 goto out_0; 00434 00435 if (_dbus_string_get_length (&cookie) == 0) 00436 { 00437 retval = TRUE; 00438 goto out_0; 00439 } 00440 00441 if (!_dbus_string_init (&to_hash)) 00442 goto out_0; 00443 00444 if (!_dbus_string_copy (server_challenge, 0, 00445 &to_hash, _dbus_string_get_length (&to_hash))) 00446 goto out_1; 00447 00448 if (!_dbus_string_append (&to_hash, ":")) 00449 goto out_1; 00450 00451 if (!_dbus_string_copy (client_challenge, 0, 00452 &to_hash, _dbus_string_get_length (&to_hash))) 00453 goto out_1; 00454 00455 if (!_dbus_string_append (&to_hash, ":")) 00456 goto out_1; 00457 00458 if (!_dbus_string_copy (&cookie, 0, 00459 &to_hash, _dbus_string_get_length (&to_hash))) 00460 goto out_1; 00461 00462 if (!_dbus_sha_compute (&to_hash, hash)) 00463 goto out_1; 00464 00465 retval = TRUE; 00466 00467 out_1: 00468 _dbus_string_zero (&to_hash); 00469 _dbus_string_free (&to_hash); 00470 out_0: 00471 _dbus_string_zero (&cookie); 00472 _dbus_string_free (&cookie); 00473 return retval; 00474 } 00475 00480 #define N_CHALLENGE_BYTES (128/8) 00481 00482 static dbus_bool_t 00483 sha1_handle_first_client_response (DBusAuth *auth, 00484 const DBusString *data) 00485 { 00486 /* We haven't sent a challenge yet, we're expecting a desired 00487 * username from the client. 00488 */ 00489 DBusString tmp; 00490 DBusString tmp2; 00491 dbus_bool_t retval; 00492 DBusError error; 00493 00494 retval = FALSE; 00495 00496 _dbus_string_set_length (&auth->challenge, 0); 00497 00498 if (_dbus_string_get_length (data) > 0) 00499 { 00500 if (_dbus_string_get_length (&auth->identity) > 0) 00501 { 00502 /* Tried to send two auth identities, wtf */ 00503 _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n", 00504 DBUS_AUTH_NAME (auth)); 00505 return send_rejected (auth); 00506 } 00507 else 00508 { 00509 /* this is our auth identity */ 00510 if (!_dbus_string_copy (data, 0, &auth->identity, 0)) 00511 return FALSE; 00512 } 00513 } 00514 00515 if (!_dbus_credentials_from_username (data, &auth->desired_identity)) 00516 { 00517 _dbus_verbose ("%s: Did not get a valid username from client\n", 00518 DBUS_AUTH_NAME (auth)); 00519 return send_rejected (auth); 00520 } 00521 00522 if (!_dbus_string_init (&tmp)) 00523 return FALSE; 00524 00525 if (!_dbus_string_init (&tmp2)) 00526 { 00527 _dbus_string_free (&tmp); 00528 return FALSE; 00529 } 00530 00531 /* we cache the keyring for speed, so here we drop it if it's the 00532 * wrong one. FIXME caching the keyring here is useless since we use 00533 * a different DBusAuth for every connection. 00534 */ 00535 if (auth->keyring && 00536 !_dbus_keyring_is_for_user (auth->keyring, 00537 data)) 00538 { 00539 _dbus_keyring_unref (auth->keyring); 00540 auth->keyring = NULL; 00541 } 00542 00543 if (auth->keyring == NULL) 00544 { 00545 DBusError error; 00546 00547 dbus_error_init (&error); 00548 auth->keyring = _dbus_keyring_new_homedir (data, 00549 &auth->context, 00550 &error); 00551 00552 if (auth->keyring == NULL) 00553 { 00554 if (dbus_error_has_name (&error, 00555 DBUS_ERROR_NO_MEMORY)) 00556 { 00557 dbus_error_free (&error); 00558 goto out; 00559 } 00560 else 00561 { 00562 _DBUS_ASSERT_ERROR_IS_SET (&error); 00563 _dbus_verbose ("%s: Error loading keyring: %s\n", 00564 DBUS_AUTH_NAME (auth), error.message); 00565 if (send_rejected (auth)) 00566 retval = TRUE; /* retval is only about mem */ 00567 dbus_error_free (&error); 00568 goto out; 00569 } 00570 } 00571 else 00572 { 00573 _dbus_assert (!dbus_error_is_set (&error)); 00574 } 00575 } 00576 00577 _dbus_assert (auth->keyring != NULL); 00578 00579 dbus_error_init (&error); 00580 auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error); 00581 if (auth->cookie_id < 0) 00582 { 00583 _DBUS_ASSERT_ERROR_IS_SET (&error); 00584 _dbus_verbose ("%s: Could not get a cookie ID to send to client: %s\n", 00585 DBUS_AUTH_NAME (auth), error.message); 00586 if (send_rejected (auth)) 00587 retval = TRUE; 00588 dbus_error_free (&error); 00589 goto out; 00590 } 00591 else 00592 { 00593 _dbus_assert (!dbus_error_is_set (&error)); 00594 } 00595 00596 if (!_dbus_string_copy (&auth->context, 0, 00597 &tmp2, _dbus_string_get_length (&tmp2))) 00598 goto out; 00599 00600 if (!_dbus_string_append (&tmp2, " ")) 00601 goto out; 00602 00603 if (!_dbus_string_append_int (&tmp2, auth->cookie_id)) 00604 goto out; 00605 00606 if (!_dbus_string_append (&tmp2, " ")) 00607 goto out; 00608 00609 if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES)) 00610 goto out; 00611 00612 _dbus_string_set_length (&auth->challenge, 0); 00613 if (!_dbus_string_hex_encode (&tmp, 0, &auth->challenge, 0)) 00614 goto out; 00615 00616 if (!_dbus_string_hex_encode (&tmp, 0, &tmp2, 00617 _dbus_string_get_length (&tmp2))) 00618 goto out; 00619 00620 if (!send_data (auth, &tmp2)) 00621 goto out; 00622 00623 goto_state (auth, &server_state_waiting_for_data); 00624 retval = TRUE; 00625 00626 out: 00627 _dbus_string_zero (&tmp); 00628 _dbus_string_free (&tmp); 00629 _dbus_string_zero (&tmp2); 00630 _dbus_string_free (&tmp2); 00631 00632 return retval; 00633 } 00634 00635 static dbus_bool_t 00636 sha1_handle_second_client_response (DBusAuth *auth, 00637 const DBusString *data) 00638 { 00639 /* We are expecting a response which is the hex-encoded client 00640 * challenge, space, then SHA-1 hash of the concatenation of our 00641 * challenge, ":", client challenge, ":", secret key, all 00642 * hex-encoded. 00643 */ 00644 int i; 00645 DBusString client_challenge; 00646 DBusString client_hash; 00647 dbus_bool_t retval; 00648 DBusString correct_hash; 00649 00650 retval = FALSE; 00651 00652 if (!_dbus_string_find_blank (data, 0, &i)) 00653 { 00654 _dbus_verbose ("%s: no space separator in client response\n", 00655 DBUS_AUTH_NAME (auth)); 00656 return send_rejected (auth); 00657 } 00658 00659 if (!_dbus_string_init (&client_challenge)) 00660 goto out_0; 00661 00662 if (!_dbus_string_init (&client_hash)) 00663 goto out_1; 00664 00665 if (!_dbus_string_copy_len (data, 0, i, &client_challenge, 00666 0)) 00667 goto out_2; 00668 00669 _dbus_string_skip_blank (data, i, &i); 00670 00671 if (!_dbus_string_copy_len (data, i, 00672 _dbus_string_get_length (data) - i, 00673 &client_hash, 00674 0)) 00675 goto out_2; 00676 00677 if (_dbus_string_get_length (&client_challenge) == 0 || 00678 _dbus_string_get_length (&client_hash) == 0) 00679 { 00680 _dbus_verbose ("%s: zero-length client challenge or hash\n", 00681 DBUS_AUTH_NAME (auth)); 00682 if (send_rejected (auth)) 00683 retval = TRUE; 00684 goto out_2; 00685 } 00686 00687 if (!_dbus_string_init (&correct_hash)) 00688 goto out_2; 00689 00690 if (!sha1_compute_hash (auth, auth->cookie_id, 00691 &auth->challenge, 00692 &client_challenge, 00693 &correct_hash)) 00694 goto out_3; 00695 00696 /* if cookie_id was invalid, then we get an empty hash */ 00697 if (_dbus_string_get_length (&correct_hash) == 0) 00698 { 00699 if (send_rejected (auth)) 00700 retval = TRUE; 00701 goto out_3; 00702 } 00703 00704 if (!_dbus_string_equal (&client_hash, &correct_hash)) 00705 { 00706 if (send_rejected (auth)) 00707 retval = TRUE; 00708 goto out_3; 00709 } 00710 00711 if (!send_ok (auth)) 00712 goto out_3; 00713 00714 _dbus_verbose ("%s: authenticated client with UID "DBUS_UID_FORMAT" using DBUS_COOKIE_SHA1\n", 00715 DBUS_AUTH_NAME (auth), auth->desired_identity.uid); 00716 00717 auth->authorized_identity = auth->desired_identity; 00718 retval = TRUE; 00719 00720 out_3: 00721 _dbus_string_zero (&correct_hash); 00722 _dbus_string_free (&correct_hash); 00723 out_2: 00724 _dbus_string_zero (&client_hash); 00725 _dbus_string_free (&client_hash); 00726 out_1: 00727 _dbus_string_free (&client_challenge); 00728 out_0: 00729 return retval; 00730 } 00731 00732 static dbus_bool_t 00733 handle_server_data_cookie_sha1_mech (DBusAuth *auth, 00734 const DBusString *data) 00735 { 00736 if (auth->cookie_id < 0) 00737 return sha1_handle_first_client_response (auth, data); 00738 else 00739 return sha1_handle_second_client_response (auth, data); 00740 } 00741 00742 static void 00743 handle_server_shutdown_cookie_sha1_mech (DBusAuth *auth) 00744 { 00745 auth->cookie_id = -1; 00746 _dbus_string_set_length (&auth->challenge, 0); 00747 } 00748 00749 static dbus_bool_t 00750 handle_client_initial_response_cookie_sha1_mech (DBusAuth *auth, 00751 DBusString *response) 00752 { 00753 const DBusString *username; 00754 dbus_bool_t retval; 00755 00756 retval = FALSE; 00757 00758 if (!_dbus_username_from_current_process (&username)) 00759 goto out_0; 00760 00761 if (!_dbus_string_hex_encode (username, 0, 00762 response, 00763 _dbus_string_get_length (response))) 00764 goto out_0; 00765 00766 retval = TRUE; 00767 00768 out_0: 00769 return retval; 00770 } 00771 00772 static dbus_bool_t 00773 handle_client_data_cookie_sha1_mech (DBusAuth *auth, 00774 const DBusString *data) 00775 { 00776 /* The data we get from the server should be the cookie context 00777 * name, the cookie ID, and the server challenge, separated by 00778 * spaces. We send back our challenge string and the correct hash. 00779 */ 00780 dbus_bool_t retval; 00781 DBusString context; 00782 DBusString cookie_id_str; 00783 DBusString server_challenge; 00784 DBusString client_challenge; 00785 DBusString correct_hash; 00786 DBusString tmp; 00787 int i, j; 00788 long val; 00789 00790 retval = FALSE; 00791 00792 if (!_dbus_string_find_blank (data, 0, &i)) 00793 { 00794 if (send_error (auth, 00795 "Server did not send context/ID/challenge properly")) 00796 retval = TRUE; 00797 goto out_0; 00798 } 00799 00800 if (!_dbus_string_init (&context)) 00801 goto out_0; 00802 00803 if (!_dbus_string_copy_len (data, 0, i, 00804 &context, 0)) 00805 goto out_1; 00806 00807 _dbus_string_skip_blank (data, i, &i); 00808 if (!_dbus_string_find_blank (data, i, &j)) 00809 { 00810 if (send_error (auth, 00811 "Server did not send context/ID/challenge properly")) 00812 retval = TRUE; 00813 goto out_1; 00814 } 00815 00816 if (!_dbus_string_init (&cookie_id_str)) 00817 goto out_1; 00818 00819 if (!_dbus_string_copy_len (data, i, j - i, 00820 &cookie_id_str, 0)) 00821 goto out_2; 00822 00823 if (!_dbus_string_init (&server_challenge)) 00824 goto out_2; 00825 00826 i = j; 00827 _dbus_string_skip_blank (data, i, &i); 00828 j = _dbus_string_get_length (data); 00829 00830 if (!_dbus_string_copy_len (data, i, j - i, 00831 &server_challenge, 0)) 00832 goto out_3; 00833 00834 if (!_dbus_keyring_validate_context (&context)) 00835 { 00836 if (send_error (auth, "Server sent invalid cookie context")) 00837 retval = TRUE; 00838 goto out_3; 00839 } 00840 00841 if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL)) 00842 { 00843 if (send_error (auth, "Could not parse cookie ID as an integer")) 00844 retval = TRUE; 00845 goto out_3; 00846 } 00847 00848 if (_dbus_string_get_length (&server_challenge) == 0) 00849 { 00850 if (send_error (auth, "Empty server challenge string")) 00851 retval = TRUE; 00852 goto out_3; 00853 } 00854 00855 if (auth->keyring == NULL) 00856 { 00857 DBusError error; 00858 00859 dbus_error_init (&error); 00860 auth->keyring = _dbus_keyring_new_homedir (NULL, 00861 &context, 00862 &error); 00863 00864 if (auth->keyring == NULL) 00865 { 00866 if (dbus_error_has_name (&error, 00867 DBUS_ERROR_NO_MEMORY)) 00868 { 00869 dbus_error_free (&error); 00870 goto out_3; 00871 } 00872 else 00873 { 00874 _DBUS_ASSERT_ERROR_IS_SET (&error); 00875 00876 _dbus_verbose ("%s: Error loading keyring: %s\n", 00877 DBUS_AUTH_NAME (auth), error.message); 00878 00879 if (send_error (auth, "Could not load cookie file")) 00880 retval = TRUE; /* retval is only about mem */ 00881 00882 dbus_error_free (&error); 00883 goto out_3; 00884 } 00885 } 00886 else 00887 { 00888 _dbus_assert (!dbus_error_is_set (&error)); 00889 } 00890 } 00891 00892 _dbus_assert (auth->keyring != NULL); 00893 00894 if (!_dbus_string_init (&tmp)) 00895 goto out_3; 00896 00897 if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES)) 00898 goto out_4; 00899 00900 if (!_dbus_string_init (&client_challenge)) 00901 goto out_4; 00902 00903 if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0)) 00904 goto out_5; 00905 00906 if (!_dbus_string_init (&correct_hash)) 00907 goto out_5; 00908 00909 if (!sha1_compute_hash (auth, val, 00910 &server_challenge, 00911 &client_challenge, 00912 &correct_hash)) 00913 goto out_6; 00914 00915 if (_dbus_string_get_length (&correct_hash) == 0) 00916 { 00917 /* couldn't find the cookie ID or something */ 00918 if (send_error (auth, "Don't have the requested cookie ID")) 00919 retval = TRUE; 00920 goto out_6; 00921 } 00922 00923 _dbus_string_set_length (&tmp, 0); 00924 00925 if (!_dbus_string_copy (&client_challenge, 0, &tmp, 00926 _dbus_string_get_length (&tmp))) 00927 goto out_6; 00928 00929 if (!_dbus_string_append (&tmp, " ")) 00930 goto out_6; 00931 00932 if (!_dbus_string_copy (&correct_hash, 0, &tmp, 00933 _dbus_string_get_length (&tmp))) 00934 goto out_6; 00935 00936 if (!send_data (auth, &tmp)) 00937 goto out_6; 00938 00939 retval = TRUE; 00940 00941 out_6: 00942 _dbus_string_zero (&correct_hash); 00943 _dbus_string_free (&correct_hash); 00944 out_5: 00945 _dbus_string_free (&client_challenge); 00946 out_4: 00947 _dbus_string_zero (&tmp); 00948 _dbus_string_free (&tmp); 00949 out_3: 00950 _dbus_string_free (&server_challenge); 00951 out_2: 00952 _dbus_string_free (&cookie_id_str); 00953 out_1: 00954 _dbus_string_free (&context); 00955 out_0: 00956 return retval; 00957 } 00958 00959 static void 00960 handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth) 00961 { 00962 auth->cookie_id = -1; 00963 _dbus_string_set_length (&auth->challenge, 0); 00964 } 00965 00966 static dbus_bool_t 00967 handle_server_data_external_mech (DBusAuth *auth, 00968 const DBusString *data) 00969 { 00970 if (auth->credentials.uid == DBUS_UID_UNSET) 00971 { 00972 _dbus_verbose ("%s: no credentials, mechanism EXTERNAL can't authenticate\n", 00973 DBUS_AUTH_NAME (auth)); 00974 return send_rejected (auth); 00975 } 00976 00977 if (_dbus_string_get_length (data) > 0) 00978 { 00979 if (_dbus_string_get_length (&auth->identity) > 0) 00980 { 00981 /* Tried to send two auth identities, wtf */ 00982 _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n", 00983 DBUS_AUTH_NAME (auth)); 00984 return send_rejected (auth); 00985 } 00986 else 00987 { 00988 /* this is our auth identity */ 00989 if (!_dbus_string_copy (data, 0, &auth->identity, 0)) 00990 return FALSE; 00991 } 00992 } 00993 00994 /* Poke client for an auth identity, if none given */ 00995 if (_dbus_string_get_length (&auth->identity) == 0 && 00996 !auth->already_asked_for_initial_response) 00997 { 00998 if (send_data (auth, NULL)) 00999 { 01000 _dbus_verbose ("%s: sending empty challenge asking client for auth identity\n", 01001 DBUS_AUTH_NAME (auth)); 01002 auth->already_asked_for_initial_response = TRUE; 01003 return TRUE; 01004 } 01005 else 01006 return FALSE; 01007 } 01008 01009 _dbus_credentials_clear (&auth->desired_identity); 01010 01011 /* If auth->identity is still empty here, then client 01012 * responded with an empty string after we poked it for 01013 * an initial response. This means to try to auth the 01014 * identity provided in the credentials. 01015 */ 01016 if (_dbus_string_get_length (&auth->identity) == 0) 01017 { 01018 auth->desired_identity.uid = auth->credentials.uid; 01019 } 01020 else 01021 { 01022 if (!_dbus_uid_from_string (&auth->identity, 01023 &auth->desired_identity.uid)) 01024 { 01025 _dbus_verbose ("%s: could not get credentials from uid string\n", 01026 DBUS_AUTH_NAME (auth)); 01027 return send_rejected (auth); 01028 } 01029 } 01030 01031 if (auth->desired_identity.uid == DBUS_UID_UNSET) 01032 { 01033 _dbus_verbose ("%s: desired user %s is no good\n", 01034 DBUS_AUTH_NAME (auth), 01035 _dbus_string_get_const_data (&auth->identity)); 01036 return send_rejected (auth); 01037 } 01038 01039 if (_dbus_credentials_match (&auth->desired_identity, 01040 &auth->credentials)) 01041 { 01042 /* client has authenticated */ 01043 if (!send_ok (auth)) 01044 return FALSE; 01045 01046 _dbus_verbose ("%s: authenticated client with UID "DBUS_UID_FORMAT 01047 " matching socket credentials UID "DBUS_UID_FORMAT"\n", 01048 DBUS_AUTH_NAME (auth), 01049 auth->desired_identity.uid, 01050 auth->credentials.uid); 01051 01052 auth->authorized_identity.pid = auth->credentials.pid; 01053 auth->authorized_identity.uid = auth->desired_identity.uid; 01054 return TRUE; 01055 } 01056 else 01057 { 01058 _dbus_verbose ("%s: credentials uid="DBUS_UID_FORMAT 01059 " gid="DBUS_GID_FORMAT 01060 " do not allow uid="DBUS_UID_FORMAT 01061 " gid="DBUS_GID_FORMAT"\n", 01062 DBUS_AUTH_NAME (auth), 01063 auth->credentials.uid, auth->credentials.gid, 01064 auth->desired_identity.uid, auth->desired_identity.gid); 01065 return send_rejected (auth); 01066 } 01067 } 01068 01069 static void 01070 handle_server_shutdown_external_mech (DBusAuth *auth) 01071 { 01072 01073 } 01074 01075 static dbus_bool_t 01076 handle_client_initial_response_external_mech (DBusAuth *auth, 01077 DBusString *response) 01078 { 01079 /* We always append our UID as an initial response, so the server 01080 * doesn't have to send back an empty challenge to check whether we 01081 * want to specify an identity. i.e. this avoids a round trip that 01082 * the spec for the EXTERNAL mechanism otherwise requires. 01083 */ 01084 DBusString plaintext; 01085 01086 if (!_dbus_string_init (&plaintext)) 01087 return FALSE; 01088 01089 if (!_dbus_string_append_uint (&plaintext, 01090 _dbus_getuid ())) 01091 goto failed; 01092 01093 if (!_dbus_string_hex_encode (&plaintext, 0, 01094 response, 01095 _dbus_string_get_length (response))) 01096 goto failed; 01097 01098 _dbus_string_free (&plaintext); 01099 01100 return TRUE; 01101 01102 failed: 01103 _dbus_string_free (&plaintext); 01104 return FALSE; 01105 } 01106 01107 static dbus_bool_t 01108 handle_client_data_external_mech (DBusAuth *auth, 01109 const DBusString *data) 01110 { 01111 01112 return TRUE; 01113 } 01114 01115 static void 01116 handle_client_shutdown_external_mech (DBusAuth *auth) 01117 { 01118 01119 } 01120 01121 /* Put mechanisms here in order of preference. 01122 * What I eventually want to have is: 01123 * 01124 * - a mechanism that checks UNIX domain socket credentials 01125 * - a simple magic cookie mechanism like X11 or ICE 01126 * - mechanisms that chain to Cyrus SASL, so we can use anything it 01127 * offers such as Kerberos, X509, whatever. 01128 * 01129 */ 01130 static const DBusAuthMechanismHandler 01131 all_mechanisms[] = { 01132 { "EXTERNAL", 01133 handle_server_data_external_mech, 01134 NULL, NULL, 01135 handle_server_shutdown_external_mech, 01136 handle_client_initial_response_external_mech, 01137 handle_client_data_external_mech, 01138 NULL, NULL, 01139 handle_client_shutdown_external_mech }, 01140 { "DBUS_COOKIE_SHA1", 01141 handle_server_data_cookie_sha1_mech, 01142 NULL, NULL, 01143 handle_server_shutdown_cookie_sha1_mech, 01144 handle_client_initial_response_cookie_sha1_mech, 01145 handle_client_data_cookie_sha1_mech, 01146 NULL, NULL, 01147 handle_client_shutdown_cookie_sha1_mech }, 01148 { NULL, NULL } 01149 }; 01150 01151 static const DBusAuthMechanismHandler* 01152 find_mech (const DBusString *name, 01153 char **allowed_mechs) 01154 { 01155 int i; 01156 01157 if (allowed_mechs != NULL && 01158 !_dbus_string_array_contains ((const char**) allowed_mechs, 01159 _dbus_string_get_const_data (name))) 01160 return NULL; 01161 01162 i = 0; 01163 while (all_mechanisms[i].mechanism != NULL) 01164 { 01165 if (_dbus_string_equal_c_str (name, 01166 all_mechanisms[i].mechanism)) 01167 01168 return &all_mechanisms[i]; 01169 01170 ++i; 01171 } 01172 01173 return NULL; 01174 } 01175 01176 static dbus_bool_t 01177 send_auth (DBusAuth *auth, const DBusAuthMechanismHandler *mech) 01178 { 01179 DBusString auth_command; 01180 01181 if (!_dbus_string_init (&auth_command)) 01182 return FALSE; 01183 01184 if (!_dbus_string_append (&auth_command, 01185 "AUTH ")) 01186 { 01187 _dbus_string_free (&auth_command); 01188 return FALSE; 01189 } 01190 01191 if (!_dbus_string_append (&auth_command, 01192 mech->mechanism)) 01193 { 01194 _dbus_string_free (&auth_command); 01195 return FALSE; 01196 } 01197 01198 if (mech->client_initial_response_func != NULL) 01199 { 01200 if (!_dbus_string_append (&auth_command, " ")) 01201 { 01202 _dbus_string_free (&auth_command); 01203 return FALSE; 01204 } 01205 01206 if (!(* mech->client_initial_response_func) (auth, &auth_command)) 01207 { 01208 _dbus_string_free (&auth_command); 01209 return FALSE; 01210 } 01211 } 01212 01213 if (!_dbus_string_append (&auth_command, 01214 "\r\n")) 01215 { 01216 _dbus_string_free (&auth_command); 01217 return FALSE; 01218 } 01219 01220 if (!_dbus_string_copy (&auth_command, 0, 01221 &auth->outgoing, 01222 _dbus_string_get_length (&auth->outgoing))) 01223 { 01224 _dbus_string_free (&auth_command); 01225 return FALSE; 01226 } 01227 01228 _dbus_string_free (&auth_command); 01229 shutdown_mech (auth); 01230 auth->mech = mech; 01231 goto_state (auth, &client_state_waiting_for_data); 01232 01233 return TRUE; 01234 } 01235 01236 static dbus_bool_t 01237 send_data (DBusAuth *auth, DBusString *data) 01238 { 01239 int old_len; 01240 01241 if (data == NULL || _dbus_string_get_length (data) == 0) 01242 return _dbus_string_append (&auth->outgoing, "DATA\r\n"); 01243 else 01244 { 01245 old_len = _dbus_string_get_length (&auth->outgoing); 01246 if (!_dbus_string_append (&auth->outgoing, "DATA ")) 01247 goto out; 01248 01249 if (!_dbus_string_hex_encode (data, 0, &auth->outgoing, 01250 _dbus_string_get_length (&auth->outgoing))) 01251 goto out; 01252 01253 if (!_dbus_string_append (&auth->outgoing, "\r\n")) 01254 goto out; 01255 01256 return TRUE; 01257 01258 out: 01259 _dbus_string_set_length (&auth->outgoing, old_len); 01260 01261 return FALSE; 01262 } 01263 } 01264 01265 static dbus_bool_t 01266 send_rejected (DBusAuth *auth) 01267 { 01268 DBusString command; 01269 DBusAuthServer *server_auth; 01270 int i; 01271 01272 if (!_dbus_string_init (&command)) 01273 return FALSE; 01274 01275 if (!_dbus_string_append (&command, 01276 "REJECTED")) 01277 goto nomem; 01278 01279 i = 0; 01280 while (all_mechanisms[i].mechanism != NULL) 01281 { 01282 if (!_dbus_string_append (&command, 01283 " ")) 01284 goto nomem; 01285 01286 if (!_dbus_string_append (&command, 01287 all_mechanisms[i].mechanism)) 01288 goto nomem; 01289 01290 ++i; 01291 } 01292 01293 if (!_dbus_string_append (&command, "\r\n")) 01294 goto nomem; 01295 01296 if (!_dbus_string_copy (&command, 0, &auth->outgoing, 01297 _dbus_string_get_length (&auth->outgoing))) 01298 goto nomem; 01299 01300 shutdown_mech (auth); 01301 01302 _dbus_assert (DBUS_AUTH_IS_SERVER (auth)); 01303 server_auth = DBUS_AUTH_SERVER (auth); 01304 server_auth->failures += 1; 01305 01306 if (server_auth->failures >= server_auth->max_failures) 01307 goto_state (auth, &common_state_need_disconnect); 01308 else 01309 goto_state (auth, &server_state_waiting_for_auth); 01310 01311 _dbus_string_free (&command); 01312 01313 return TRUE; 01314 01315 nomem: 01316 _dbus_string_free (&command); 01317 return FALSE; 01318 } 01319 01320 static dbus_bool_t 01321 send_error (DBusAuth *auth, const char *message) 01322 { 01323 return _dbus_string_append_printf (&auth->outgoing, 01324 "ERROR \"%s\"\r\n", message); 01325 } 01326 01327 static dbus_bool_t 01328 send_ok (DBusAuth *auth) 01329 { 01330 if (_dbus_string_append (&auth->outgoing, "OK\r\n")) 01331 { 01332 goto_state (auth, &server_state_waiting_for_begin); 01333 return TRUE; 01334 } 01335 else 01336 return FALSE; 01337 } 01338 01339 static dbus_bool_t 01340 send_begin (DBusAuth *auth) 01341 { 01342 if (_dbus_string_append (&auth->outgoing, "BEGIN\r\n")) 01343 { 01344 goto_state (auth, &common_state_authenticated); 01345 return TRUE; 01346 } 01347 else 01348 return FALSE; 01349 } 01350 01351 static dbus_bool_t 01352 send_cancel (DBusAuth *auth) 01353 { 01354 if (_dbus_string_append (&auth->outgoing, "CANCEL\r\n")) 01355 { 01356 goto_state (auth, &client_state_waiting_for_reject); 01357 return TRUE; 01358 } 01359 else 01360 return FALSE; 01361 } 01362 01363 static dbus_bool_t 01364 process_data (DBusAuth *auth, 01365 const DBusString *args, 01366 DBusAuthDataFunction data_func) 01367 { 01368 int end; 01369 DBusString decoded; 01370 01371 if (!_dbus_string_init (&decoded)) 01372 return FALSE; 01373 01374 if (!_dbus_string_hex_decode (args, 0, &end, &decoded, 0)) 01375 { 01376 _dbus_string_free (&decoded); 01377 return FALSE; 01378 } 01379 01380 if (_dbus_string_get_length (args) != end) 01381 { 01382 _dbus_string_free (&decoded); 01383 if (!send_error (auth, "Invalid hex encoding")) 01384 return FALSE; 01385 01386 return TRUE; 01387 } 01388 01389 #ifdef DBUS_ENABLE_VERBOSE_MODE 01390 if (_dbus_string_validate_ascii (&decoded, 0, 01391 _dbus_string_get_length (&decoded))) 01392 _dbus_verbose ("%s: data: '%s'\n", 01393 DBUS_AUTH_NAME (auth), 01394 _dbus_string_get_const_data (&decoded)); 01395 #endif 01396 01397 if (!(* data_func) (auth, &decoded)) 01398 { 01399 _dbus_string_free (&decoded); 01400 return FALSE; 01401 } 01402 01403 _dbus_string_free (&decoded); 01404 return TRUE; 01405 } 01406 01407 static dbus_bool_t 01408 handle_auth (DBusAuth *auth, const DBusString *args) 01409 { 01410 if (_dbus_string_get_length (args) == 0) 01411 { 01412 /* No args to the auth, send mechanisms */ 01413 if (!send_rejected (auth)) 01414 return FALSE; 01415 01416 return TRUE; 01417 } 01418 else 01419 { 01420 int i; 01421 DBusString mech; 01422 DBusString hex_response; 01423 01424 _dbus_string_find_blank (args, 0, &i); 01425 01426 if (!_dbus_string_init (&mech)) 01427 return FALSE; 01428 01429 if (!_dbus_string_init (&hex_response)) 01430 { 01431 _dbus_string_free (&mech); 01432 return FALSE; 01433 } 01434 01435 if (!_dbus_string_copy_len (args, 0, i, &mech, 0)) 01436 goto failed; 01437 01438 _dbus_string_skip_blank (args, i, &i); 01439 if (!_dbus_string_copy (args, i, &hex_response, 0)) 01440 goto failed; 01441 01442 auth->mech = find_mech (&mech, auth->allowed_mechs); 01443 if (auth->mech != NULL) 01444 { 01445 _dbus_verbose ("%s: Trying mechanism %s\n", 01446 DBUS_AUTH_NAME (auth), 01447 auth->mech->mechanism); 01448 01449 if (!process_data (auth, &hex_response, 01450 auth->mech->server_data_func)) 01451 goto failed; 01452 } 01453 else 01454 { 01455 /* Unsupported mechanism */ 01456 _dbus_verbose ("%s: Unsupported mechanism %s\n", 01457 DBUS_AUTH_NAME (auth), 01458 _dbus_string_get_const_data (&mech)); 01459 01460 if (!send_rejected (auth)) 01461 goto failed; 01462 } 01463 01464 _dbus_string_free (&mech); 01465 _dbus_string_free (&hex_response); 01466 01467 return TRUE; 01468 01469 failed: 01470 auth->mech = NULL; 01471 _dbus_string_free (&mech); 01472 _dbus_string_free (&hex_response); 01473 return FALSE; 01474 } 01475 } 01476 01477 static dbus_bool_t 01478 handle_server_state_waiting_for_auth (DBusAuth *auth, 01479 DBusAuthCommand command, 01480 const DBusString *args) 01481 { 01482 switch (command) 01483 { 01484 case DBUS_AUTH_COMMAND_AUTH: 01485 return handle_auth (auth, args); 01486 01487 case DBUS_AUTH_COMMAND_CANCEL: 01488 case DBUS_AUTH_COMMAND_DATA: 01489 return send_error (auth, "Not currently in an auth conversation"); 01490 01491 case DBUS_AUTH_COMMAND_BEGIN: 01492 goto_state (auth, &common_state_need_disconnect); 01493 return TRUE; 01494 01495 case DBUS_AUTH_COMMAND_ERROR: 01496 return send_rejected (auth); 01497 01498 case DBUS_AUTH_COMMAND_REJECTED: 01499 case DBUS_AUTH_COMMAND_OK: 01500 case DBUS_AUTH_COMMAND_UNKNOWN: 01501 default: 01502 return send_error (auth, "Unknown command"); 01503 } 01504 } 01505 01506 static dbus_bool_t 01507 handle_server_state_waiting_for_data (DBusAuth *auth, 01508 DBusAuthCommand command, 01509 const DBusString *args) 01510 { 01511 switch (command) 01512 { 01513 case DBUS_AUTH_COMMAND_AUTH: 01514 return send_error (auth, "Sent AUTH while another AUTH in progress"); 01515 01516 case DBUS_AUTH_COMMAND_CANCEL: 01517 case DBUS_AUTH_COMMAND_ERROR: 01518 return send_rejected (auth); 01519 01520 case DBUS_AUTH_COMMAND_DATA: 01521 return process_data (auth, args, auth->mech->server_data_func); 01522 01523 case DBUS_AUTH_COMMAND_BEGIN: 01524 goto_state (auth, &common_state_need_disconnect); 01525 return TRUE; 01526 01527 case DBUS_AUTH_COMMAND_REJECTED: 01528 case DBUS_AUTH_COMMAND_OK: 01529 case DBUS_AUTH_COMMAND_UNKNOWN: 01530 default: 01531 return send_error (auth, "Unknown command"); 01532 } 01533 } 01534 01535 static dbus_bool_t 01536 handle_server_state_waiting_for_begin (DBusAuth *auth, 01537 DBusAuthCommand command, 01538 const DBusString *args) 01539 { 01540 switch (command) 01541 { 01542 case DBUS_AUTH_COMMAND_AUTH: 01543 return send_error (auth, "Sent AUTH while expecting BEGIN"); 01544 01545 case DBUS_AUTH_COMMAND_DATA: 01546 return send_error (auth, "Sent DATA while expecting BEGIN"); 01547 01548 case DBUS_AUTH_COMMAND_BEGIN: 01549 goto_state (auth, &common_state_authenticated); 01550 return TRUE; 01551 01552 case DBUS_AUTH_COMMAND_REJECTED: 01553 case DBUS_AUTH_COMMAND_OK: 01554 case DBUS_AUTH_COMMAND_UNKNOWN: 01555 default: 01556 return send_error (auth, "Unknown command"); 01557 01558 case DBUS_AUTH_COMMAND_CANCEL: 01559 case DBUS_AUTH_COMMAND_ERROR: 01560 return send_rejected (auth); 01561 } 01562 } 01563 01564 /* return FALSE if no memory, TRUE if all OK */ 01565 static dbus_bool_t 01566 get_word (const DBusString *str, 01567 int *start, 01568 DBusString *word) 01569 { 01570 int i; 01571 01572 _dbus_string_skip_blank (str, *start, start); 01573 _dbus_string_find_blank (str, *start, &i); 01574 01575 if (i > *start) 01576 { 01577 if (!_dbus_string_copy_len (str, *start, i - *start, word, 0)) 01578 return FALSE; 01579 01580 *start = i; 01581 } 01582 01583 return TRUE; 01584 } 01585 01586 static dbus_bool_t 01587 record_mechanisms (DBusAuth *auth, 01588 const DBusString *args) 01589 { 01590 int next; 01591 int len; 01592 01593 if (auth->already_got_mechanisms) 01594 return TRUE; 01595 01596 len = _dbus_string_get_length (args); 01597 01598 next = 0; 01599 while (next < len) 01600 { 01601 DBusString m; 01602 const DBusAuthMechanismHandler *mech; 01603 01604 if (!_dbus_string_init (&m)) 01605 goto nomem; 01606 01607 if (!get_word (args, &next, &m)) 01608 { 01609 _dbus_string_free (&m); 01610 goto nomem; 01611 } 01612 01613 mech = find_mech (&m, auth->allowed_mechs); 01614 01615 if (mech != NULL) 01616 { 01617 /* FIXME right now we try mechanisms in the order 01618 * the server lists them; should we do them in 01619 * some more deterministic order? 01620 * 01621 * Probably in all_mechanisms order, our order of 01622 * preference. Of course when the server is us, 01623 * it lists things in that order anyhow. 01624 */ 01625 01626 _dbus_verbose ("%s: Adding mechanism %s to list we will try\n", 01627 DBUS_AUTH_NAME (auth), mech->mechanism); 01628 01629 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try, 01630 (void*) mech)) 01631 { 01632 _dbus_string_free (&m); 01633 goto nomem; 01634 } 01635 } 01636 else 01637 { 01638 _dbus_verbose ("%s: Server offered mechanism \"%s\" that we don't know how to use\n", 01639 DBUS_AUTH_NAME (auth), 01640 _dbus_string_get_const_data (&m)); 01641 } 01642 01643 _dbus_string_free (&m); 01644 } 01645 01646 auth->already_got_mechanisms = TRUE; 01647 01648 return TRUE; 01649 01650 nomem: 01651 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try); 01652 01653 return FALSE; 01654 } 01655 01656 static dbus_bool_t 01657 process_rejected (DBusAuth *auth, const DBusString *args) 01658 { 01659 const DBusAuthMechanismHandler *mech; 01660 DBusAuthClient *client; 01661 01662 client = DBUS_AUTH_CLIENT (auth); 01663 01664 if (!auth->already_got_mechanisms) 01665 { 01666 if (!record_mechanisms (auth, args)) 01667 return FALSE; 01668 } 01669 01670 if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL) 01671 { 01672 mech = client->mechs_to_try->data; 01673 01674 if (!send_auth (auth, mech)) 01675 return FALSE; 01676 01677 _dbus_list_pop_first (&client->mechs_to_try); 01678 01679 _dbus_verbose ("%s: Trying mechanism %s\n", 01680 DBUS_AUTH_NAME (auth), 01681 mech->mechanism); 01682 } 01683 else 01684 { 01685 /* Give up */ 01686 _dbus_verbose ("%s: Disconnecting because we are out of mechanisms to try using\n", 01687 DBUS_AUTH_NAME (auth)); 01688 goto_state (auth, &common_state_need_disconnect); 01689 } 01690 01691 return TRUE; 01692 } 01693 01694 01695 static dbus_bool_t 01696 handle_client_state_waiting_for_data (DBusAuth *auth, 01697 DBusAuthCommand command, 01698 const DBusString *args) 01699 { 01700 _dbus_assert (auth->mech != NULL); 01701 01702 switch (command) 01703 { 01704 case DBUS_AUTH_COMMAND_DATA: 01705 return process_data (auth, args, auth->mech->client_data_func); 01706 01707 case DBUS_AUTH_COMMAND_REJECTED: 01708 return process_rejected (auth, args); 01709 01710 case DBUS_AUTH_COMMAND_OK: 01711 return send_begin (auth); 01712 01713 case DBUS_AUTH_COMMAND_ERROR: 01714 return send_cancel (auth); 01715 01716 case DBUS_AUTH_COMMAND_AUTH: 01717 case DBUS_AUTH_COMMAND_CANCEL: 01718 case DBUS_AUTH_COMMAND_BEGIN: 01719 case DBUS_AUTH_COMMAND_UNKNOWN: 01720 default: 01721 return send_error (auth, "Unknown command"); 01722 } 01723 } 01724 01725 static dbus_bool_t 01726 handle_client_state_waiting_for_ok (DBusAuth *auth, 01727 DBusAuthCommand command, 01728 const DBusString *args) 01729 { 01730 switch (command) 01731 { 01732 case DBUS_AUTH_COMMAND_REJECTED: 01733 return process_rejected (auth, args); 01734 01735 case DBUS_AUTH_COMMAND_OK: 01736 return send_begin (auth); 01737 01738 case DBUS_AUTH_COMMAND_DATA: 01739 case DBUS_AUTH_COMMAND_ERROR: 01740 return send_cancel (auth); 01741 01742 case DBUS_AUTH_COMMAND_AUTH: 01743 case DBUS_AUTH_COMMAND_CANCEL: 01744 case DBUS_AUTH_COMMAND_BEGIN: 01745 case DBUS_AUTH_COMMAND_UNKNOWN: 01746 default: 01747 return send_error (auth, "Unknown command"); 01748 } 01749 } 01750 01751 static dbus_bool_t 01752 handle_client_state_waiting_for_reject (DBusAuth *auth, 01753 DBusAuthCommand command, 01754 const DBusString *args) 01755 { 01756 switch (command) 01757 { 01758 case DBUS_AUTH_COMMAND_REJECTED: 01759 return process_rejected (auth, args); 01760 01761 case DBUS_AUTH_COMMAND_AUTH: 01762 case DBUS_AUTH_COMMAND_CANCEL: 01763 case DBUS_AUTH_COMMAND_DATA: 01764 case DBUS_AUTH_COMMAND_BEGIN: 01765 case DBUS_AUTH_COMMAND_OK: 01766 case DBUS_AUTH_COMMAND_ERROR: 01767 case DBUS_AUTH_COMMAND_UNKNOWN: 01768 default: 01769 goto_state (auth, &common_state_need_disconnect); 01770 return TRUE; 01771 } 01772 } 01773 01777 typedef struct { 01778 const char *name; 01779 DBusAuthCommand command; 01780 } DBusAuthCommandName; 01781 01782 static DBusAuthCommandName auth_command_names[] = { 01783 { "AUTH", DBUS_AUTH_COMMAND_AUTH }, 01784 { "CANCEL", DBUS_AUTH_COMMAND_CANCEL }, 01785 { "DATA", DBUS_AUTH_COMMAND_DATA }, 01786 { "BEGIN", DBUS_AUTH_COMMAND_BEGIN }, 01787 { "REJECTED", DBUS_AUTH_COMMAND_REJECTED }, 01788 { "OK", DBUS_AUTH_COMMAND_OK }, 01789 { "ERROR", DBUS_AUTH_COMMAND_ERROR } 01790 }; 01791 01792 static DBusAuthCommand 01793 lookup_command_from_name (DBusString *command) 01794 { 01795 int i; 01796 01797 for (i = 0; i < _DBUS_N_ELEMENTS (auth_command_names); i++) 01798 { 01799 if (_dbus_string_equal_c_str (command, 01800 auth_command_names[i].name)) 01801 return auth_command_names[i].command; 01802 } 01803 01804 return DBUS_AUTH_COMMAND_UNKNOWN; 01805 } 01806 01807 static void 01808 goto_state (DBusAuth *auth, const DBusAuthStateData *state) 01809 { 01810 _dbus_verbose ("%s: going from state %s to state %s\n", 01811 DBUS_AUTH_NAME (auth), 01812 auth->state->name, 01813 state->name); 01814 01815 auth->state = state; 01816 } 01817 01818 /* returns whether to call it again right away */ 01819 static dbus_bool_t 01820 process_command (DBusAuth *auth) 01821 { 01822 DBusAuthCommand command; 01823 DBusString line; 01824 DBusString args; 01825 int eol; 01826 int i, j; 01827 dbus_bool_t retval; 01828 01829 /* _dbus_verbose ("%s: trying process_command()\n"); */ 01830 01831 retval = FALSE; 01832 01833 eol = 0; 01834 if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol)) 01835 return FALSE; 01836 01837 if (!_dbus_string_init (&line)) 01838 { 01839 auth->needed_memory = TRUE; 01840 return FALSE; 01841 } 01842 01843 if (!_dbus_string_init (&args)) 01844 { 01845 _dbus_string_free (&line); 01846 auth->needed_memory = TRUE; 01847 return FALSE; 01848 } 01849 01850 if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &line, 0)) 01851 goto out; 01852 01853 if (!_dbus_string_validate_ascii (&line, 0, 01854 _dbus_string_get_length (&line))) 01855 { 01856 _dbus_verbose ("%s: Command contained non-ASCII chars or embedded nul\n", 01857 DBUS_AUTH_NAME (auth)); 01858 if (!send_error (auth, "Command contained non-ASCII")) 01859 goto out; 01860 else 01861 goto next_command; 01862 } 01863 01864 _dbus_verbose ("%s: got command \"%s\"\n", 01865 DBUS_AUTH_NAME (auth), 01866 _dbus_string_get_const_data (&line)); 01867 01868 _dbus_string_find_blank (&line, 0, &i); 01869 _dbus_string_skip_blank (&line, i, &j); 01870 01871 if (j > i) 01872 _dbus_string_delete (&line, i, j - i); 01873 01874 if (!_dbus_string_move (&line, i, &args, 0)) 01875 goto out; 01876 01877 command = lookup_command_from_name (&line); 01878 if (!(* auth->state->handler) (auth, command, &args)) 01879 goto out; 01880 01881 next_command: 01882 01883 /* We've succeeded in processing the whole command so drop it out 01884 * of the incoming buffer and return TRUE to try another command. 01885 */ 01886 01887 _dbus_string_delete (&auth->incoming, 0, eol); 01888 01889 /* kill the \r\n */ 01890 _dbus_string_delete (&auth->incoming, 0, 2); 01891 01892 retval = TRUE; 01893 01894 out: 01895 _dbus_string_free (&args); 01896 _dbus_string_free (&line); 01897 01898 if (!retval) 01899 auth->needed_memory = TRUE; 01900 else 01901 auth->needed_memory = FALSE; 01902 01903 return retval; 01904 } 01905 01906 01921 DBusAuth* 01922 _dbus_auth_server_new (void) 01923 { 01924 DBusAuth *auth; 01925 DBusAuthServer *server_auth; 01926 01927 auth = _dbus_auth_new (sizeof (DBusAuthServer)); 01928 if (auth == NULL) 01929 return NULL; 01930 01931 auth->side = auth_side_server; 01932 auth->state = &server_state_waiting_for_auth; 01933 01934 server_auth = DBUS_AUTH_SERVER (auth); 01935 01936 /* perhaps this should be per-mechanism with a lower 01937 * max 01938 */ 01939 server_auth->failures = 0; 01940 server_auth->max_failures = 6; 01941 01942 return auth; 01943 } 01944 01952 DBusAuth* 01953 _dbus_auth_client_new (void) 01954 { 01955 DBusAuth *auth; 01956 01957 auth = _dbus_auth_new (sizeof (DBusAuthClient)); 01958 if (auth == NULL) 01959 return NULL; 01960 01961 auth->side = auth_side_client; 01962 auth->state = &client_state_need_send_auth; 01963 01964 /* Start the auth conversation by sending AUTH for our default 01965 * mechanism */ 01966 if (!send_auth (auth, &all_mechanisms[0])) 01967 { 01968 _dbus_auth_unref (auth); 01969 return NULL; 01970 } 01971 01972 return auth; 01973 } 01974 01981 DBusAuth * 01982 _dbus_auth_ref (DBusAuth *auth) 01983 { 01984 _dbus_assert (auth != NULL); 01985 01986 auth->refcount += 1; 01987 01988 return auth; 01989 } 01990 01996 void 01997 _dbus_auth_unref (DBusAuth *auth) 01998 { 01999 _dbus_assert (auth != NULL); 02000 _dbus_assert (auth->refcount > 0); 02001 02002 auth->refcount -= 1; 02003 if (auth->refcount == 0) 02004 { 02005 shutdown_mech (auth); 02006 02007 if (DBUS_AUTH_IS_CLIENT (auth)) 02008 { 02009 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try); 02010 } 02011 02012 if (auth->keyring) 02013 _dbus_keyring_unref (auth->keyring); 02014 02015 _dbus_string_free (&auth->context); 02016 _dbus_string_free (&auth->challenge); 02017 _dbus_string_free (&auth->identity); 02018 _dbus_string_free (&auth->incoming); 02019 _dbus_string_free (&auth->outgoing); 02020 02021 dbus_free_string_array (auth->allowed_mechs); 02022 02023 dbus_free (auth); 02024 } 02025 } 02026 02035 dbus_bool_t 02036 _dbus_auth_set_mechanisms (DBusAuth *auth, 02037 const char **mechanisms) 02038 { 02039 char **copy; 02040 02041 if (mechanisms != NULL) 02042 { 02043 copy = _dbus_dup_string_array (mechanisms); 02044 if (copy == NULL) 02045 return FALSE; 02046 } 02047 else 02048 copy = NULL; 02049 02050 dbus_free_string_array (auth->allowed_mechs); 02051 02052 auth->allowed_mechs = copy; 02053 02054 return TRUE; 02055 } 02056 02061 #define DBUS_AUTH_IN_END_STATE(auth) ((auth)->state->handler == NULL) 02062 02070 DBusAuthState 02071 _dbus_auth_do_work (DBusAuth *auth) 02072 { 02073 auth->needed_memory = FALSE; 02074 02075 /* Max amount we'll buffer up before deciding someone's on crack */ 02076 #define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE) 02077 02078 do 02079 { 02080 if (DBUS_AUTH_IN_END_STATE (auth)) 02081 break; 02082 02083 if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER || 02084 _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER) 02085 { 02086 goto_state (auth, &common_state_need_disconnect); 02087 _dbus_verbose ("%s: Disconnecting due to excessive data buffered in auth phase\n", 02088 DBUS_AUTH_NAME (auth)); 02089 break; 02090 } 02091 } 02092 while (process_command (auth)); 02093 02094 if (auth->needed_memory) 02095 return DBUS_AUTH_STATE_WAITING_FOR_MEMORY; 02096 else if (_dbus_string_get_length (&auth->outgoing) > 0) 02097 return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND; 02098 else if (auth->state == &common_state_need_disconnect) 02099 return DBUS_AUTH_STATE_NEED_DISCONNECT; 02100 else if (auth->state == &common_state_authenticated) 02101 return DBUS_AUTH_STATE_AUTHENTICATED; 02102 else return DBUS_AUTH_STATE_WAITING_FOR_INPUT; 02103 } 02104 02114 dbus_bool_t 02115 _dbus_auth_get_bytes_to_send (DBusAuth *auth, 02116 const DBusString **str) 02117 { 02118 _dbus_assert (auth != NULL); 02119 _dbus_assert (str != NULL); 02120 02121 *str = NULL; 02122 02123 if (_dbus_string_get_length (&auth->outgoing) == 0) 02124 return FALSE; 02125 02126 *str = &auth->outgoing; 02127 02128 return TRUE; 02129 } 02130 02139 void 02140 _dbus_auth_bytes_sent (DBusAuth *auth, 02141 int bytes_sent) 02142 { 02143 _dbus_verbose ("%s: Sent %d bytes of: %s\n", 02144 DBUS_AUTH_NAME (auth), 02145 bytes_sent, 02146 _dbus_string_get_const_data (&auth->outgoing)); 02147 02148 _dbus_string_delete (&auth->outgoing, 02149 0, bytes_sent); 02150 } 02151 02159 void 02160 _dbus_auth_get_buffer (DBusAuth *auth, 02161 DBusString **buffer) 02162 { 02163 _dbus_assert (auth != NULL); 02164 _dbus_assert (!auth->buffer_outstanding); 02165 02166 *buffer = &auth->incoming; 02167 02168 auth->buffer_outstanding = TRUE; 02169 } 02170 02178 void 02179 _dbus_auth_return_buffer (DBusAuth *auth, 02180 DBusString *buffer, 02181 int bytes_read) 02182 { 02183 _dbus_assert (buffer == &auth->incoming); 02184 _dbus_assert (auth->buffer_outstanding); 02185 02186 auth->buffer_outstanding = FALSE; 02187 } 02188 02198 void 02199 _dbus_auth_get_unused_bytes (DBusAuth *auth, 02200 const DBusString **str) 02201 { 02202 if (!DBUS_AUTH_IN_END_STATE (auth)) 02203 return; 02204 02205 *str = &auth->incoming; 02206 } 02207 02208 02215 void 02216 _dbus_auth_delete_unused_bytes (DBusAuth *auth) 02217 { 02218 if (!DBUS_AUTH_IN_END_STATE (auth)) 02219 return; 02220 02221 _dbus_string_set_length (&auth->incoming, 0); 02222 } 02223 02232 dbus_bool_t 02233 _dbus_auth_needs_encoding (DBusAuth *auth) 02234 { 02235 if (auth->state != &common_state_authenticated) 02236 return FALSE; 02237 02238 if (auth->mech != NULL) 02239 { 02240 if (DBUS_AUTH_IS_CLIENT (auth)) 02241 return auth->mech->client_encode_func != NULL; 02242 else 02243 return auth->mech->server_encode_func != NULL; 02244 } 02245 else 02246 return FALSE; 02247 } 02248 02259 dbus_bool_t 02260 _dbus_auth_encode_data (DBusAuth *auth, 02261 const DBusString *plaintext, 02262 DBusString *encoded) 02263 { 02264 _dbus_assert (plaintext != encoded); 02265 02266 if (auth->state != &common_state_authenticated) 02267 return FALSE; 02268 02269 if (_dbus_auth_needs_encoding (auth)) 02270 { 02271 if (DBUS_AUTH_IS_CLIENT (auth)) 02272 return (* auth->mech->client_encode_func) (auth, plaintext, encoded); 02273 else 02274 return (* auth->mech->server_encode_func) (auth, plaintext, encoded); 02275 } 02276 else 02277 { 02278 return _dbus_string_copy (plaintext, 0, encoded, 02279 _dbus_string_get_length (encoded)); 02280 } 02281 } 02282 02291 dbus_bool_t 02292 _dbus_auth_needs_decoding (DBusAuth *auth) 02293 { 02294 if (auth->state != &common_state_authenticated) 02295 return FALSE; 02296 02297 if (auth->mech != NULL) 02298 { 02299 if (DBUS_AUTH_IS_CLIENT (auth)) 02300 return auth->mech->client_decode_func != NULL; 02301 else 02302 return auth->mech->server_decode_func != NULL; 02303 } 02304 else 02305 return FALSE; 02306 } 02307 02308 02322 dbus_bool_t 02323 _dbus_auth_decode_data (DBusAuth *auth, 02324 const DBusString *encoded, 02325 DBusString *plaintext) 02326 { 02327 _dbus_assert (plaintext != encoded); 02328 02329 if (auth->state != &common_state_authenticated) 02330 return FALSE; 02331 02332 if (_dbus_auth_needs_decoding (auth)) 02333 { 02334 if (DBUS_AUTH_IS_CLIENT (auth)) 02335 return (* auth->mech->client_decode_func) (auth, encoded, plaintext); 02336 else 02337 return (* auth->mech->server_decode_func) (auth, encoded, plaintext); 02338 } 02339 else 02340 { 02341 return _dbus_string_copy (encoded, 0, plaintext, 02342 _dbus_string_get_length (plaintext)); 02343 } 02344 } 02345 02353 void 02354 _dbus_auth_set_credentials (DBusAuth *auth, 02355 const DBusCredentials *credentials) 02356 { 02357 auth->credentials = *credentials; 02358 } 02359 02367 void 02368 _dbus_auth_get_identity (DBusAuth *auth, 02369 DBusCredentials *credentials) 02370 { 02371 if (auth->state == &common_state_authenticated) 02372 *credentials = auth->authorized_identity; 02373 else 02374 _dbus_credentials_clear (credentials); 02375 } 02376 02385 dbus_bool_t 02386 _dbus_auth_set_context (DBusAuth *auth, 02387 const DBusString *context) 02388 { 02389 return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context), 02390 &auth->context, 0, _dbus_string_get_length (context)); 02391 } 02392 02395 #ifdef DBUS_BUILD_TESTS 02396 #include "dbus-test.h" 02397 #include "dbus-auth-script.h" 02398 #include <stdio.h> 02399 02400 static dbus_bool_t 02401 process_test_subdir (const DBusString *test_base_dir, 02402 const char *subdir) 02403 { 02404 DBusString test_directory; 02405 DBusString filename; 02406 DBusDirIter *dir; 02407 dbus_bool_t retval; 02408 DBusError error; 02409 02410 retval = FALSE; 02411 dir = NULL; 02412 02413 if (!_dbus_string_init (&test_directory)) 02414 _dbus_assert_not_reached ("didn't allocate test_directory\n"); 02415 02416 _dbus_string_init_const (&filename, subdir); 02417 02418 if (!_dbus_string_copy (test_base_dir, 0, 02419 &test_directory, 0)) 02420 _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory"); 02421 02422 if (!_dbus_concat_dir_and_file (&test_directory, &filename)) 02423 _dbus_assert_not_reached ("couldn't allocate full path"); 02424 02425 _dbus_string_free (&filename); 02426 if (!_dbus_string_init (&filename)) 02427 _dbus_assert_not_reached ("didn't allocate filename string\n"); 02428 02429 dbus_error_init (&error); 02430 dir = _dbus_directory_open (&test_directory, &error); 02431 if (dir == NULL) 02432 { 02433 _dbus_warn ("Could not open %s: %s\n", 02434 _dbus_string_get_const_data (&test_directory), 02435 error.message); 02436 dbus_error_free (&error); 02437 goto failed; 02438 } 02439 02440 printf ("Testing %s:\n", subdir); 02441 02442 next: 02443 while (_dbus_directory_get_next_file (dir, &filename, &error)) 02444 { 02445 DBusString full_path; 02446 02447 if (!_dbus_string_init (&full_path)) 02448 _dbus_assert_not_reached ("couldn't init string"); 02449 02450 if (!_dbus_string_copy (&test_directory, 0, &full_path, 0)) 02451 _dbus_assert_not_reached ("couldn't copy dir to full_path"); 02452 02453 if (!_dbus_concat_dir_and_file (&full_path, &filename)) 02454 _dbus_assert_not_reached ("couldn't concat file to dir"); 02455 02456 if (!_dbus_string_ends_with_c_str (&filename, ".auth-script")) 02457 { 02458 _dbus_verbose ("Skipping non-.auth-script file %s\n", 02459 _dbus_string_get_const_data (&filename)); 02460 _dbus_string_free (&full_path); 02461 goto next; 02462 } 02463 02464 printf (" %s\n", _dbus_string_get_const_data (&filename)); 02465 02466 if (!_dbus_auth_script_run (&full_path)) 02467 { 02468 _dbus_string_free (&full_path); 02469 goto failed; 02470 } 02471 else 02472 _dbus_string_free (&full_path); 02473 } 02474 02475 if (dbus_error_is_set (&error)) 02476 { 02477 _dbus_warn ("Could not get next file in %s: %s\n", 02478 _dbus_string_get_const_data (&test_directory), error.message); 02479 dbus_error_free (&error); 02480 goto failed; 02481 } 02482 02483 retval = TRUE; 02484 02485 failed: 02486 02487 if (dir) 02488 _dbus_directory_close (dir); 02489 _dbus_string_free (&test_directory); 02490 _dbus_string_free (&filename); 02491 02492 return retval; 02493 } 02494 02495 static dbus_bool_t 02496 process_test_dirs (const char *test_data_dir) 02497 { 02498 DBusString test_directory; 02499 dbus_bool_t retval; 02500 02501 retval = FALSE; 02502 02503 _dbus_string_init_const (&test_directory, test_data_dir); 02504 02505 if (!process_test_subdir (&test_directory, "auth")) 02506 goto failed; 02507 02508 retval = TRUE; 02509 02510 failed: 02511 02512 _dbus_string_free (&test_directory); 02513 02514 return retval; 02515 } 02516 02517 dbus_bool_t 02518 _dbus_auth_test (const char *test_data_dir) 02519 { 02520 02521 if (test_data_dir == NULL) 02522 return TRUE; 02523 02524 if (!process_test_dirs (test_data_dir)) 02525 return FALSE; 02526 02527 return TRUE; 02528 } 02529 02530 #endif /* DBUS_BUILD_TESTS */

Generated on Mon Aug 16 17:40:07 2004 for D-BUS by doxygen 1.3.8