X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dbus%2Fdbus-auth.c;h=3768cdb7066ec7d94d7d4edce08deaad33b807bc;hb=1200c464b6c9051340960e07f0d61a51dad71286;hp=6412185bf2fccdac4e2b1794853a726f131616b8;hpb=bf75b24d26fdc9deb856bac1918fa4853c594b03;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c index 6412185..3768cdb 100644 --- a/dbus/dbus-auth.c +++ b/dbus/dbus-auth.c @@ -1,4 +1,4 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dbus-auth.c Authentication * * Copyright (C) 2002, 2003, 2004 Red Hat Inc. @@ -17,9 +17,11 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ + +#include #include "dbus-auth.h" #include "dbus-string.h" #include "dbus-list.h" @@ -27,7 +29,7 @@ #include "dbus-keyring.h" #include "dbus-sha.h" #include "dbus-protocol.h" -#include "dbus-userdb.h" +#include "dbus-credentials.h" /** * @defgroup DBusAuth Authentication @@ -42,10 +44,6 @@ * challenge/response, but we don't currently allow that in our * protocol. * - * @todo DBusAuth really needs to be rewritten as an explicit state - * machine. Right now it's too hard to prove to yourself by inspection - * that it works. - * * @todo right now sometimes both ends will block waiting for input * from the other end, e.g. if there's an error during * DBUS_COOKIE_SHA1. @@ -126,7 +124,9 @@ typedef enum { DBUS_AUTH_COMMAND_REJECTED, DBUS_AUTH_COMMAND_OK, DBUS_AUTH_COMMAND_ERROR, - DBUS_AUTH_COMMAND_UNKNOWN + DBUS_AUTH_COMMAND_UNKNOWN, + DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD, + DBUS_AUTH_COMMAND_AGREE_UNIX_FD } DBusAuthCommand; /** @@ -166,13 +166,12 @@ struct DBusAuth * as. */ - DBusCredentials credentials; /**< Credentials read from socket, - * fields may be -1 - */ + DBusCredentials *credentials; /**< Credentials read from socket + */ - DBusCredentials authorized_identity; /**< Credentials that are authorized */ + DBusCredentials *authorized_identity; /**< Credentials that are authorized */ - DBusCredentials desired_identity; /**< Identity client has requested */ + DBusCredentials *desired_identity; /**< Identity client has requested */ DBusString context; /**< Cookie scope */ DBusKeyring *keyring; /**< Keyring for cookie mechanism. */ @@ -189,6 +188,9 @@ struct DBusAuth unsigned int already_got_mechanisms : 1; /**< Client already got mech list */ unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */ unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */ + + unsigned int unix_fd_possible : 1; /**< This side could do unix fd passing */ + unsigned int unix_fd_negotiated : 1; /**< Unix fd was successfully negotiated */ }; /** @@ -199,6 +201,8 @@ typedef struct DBusAuth base; /**< Parent class */ DBusList *mechs_to_try; /**< Mechanisms we got from the server that we're going to try using */ + + DBusString guid_from_server; /**< GUID received from server */ } DBusAuthClient; @@ -211,6 +215,8 @@ typedef struct int failures; /**< Number of times client has been rejected */ int max_failures; /**< Number of times we reject before disconnect */ + + DBusString guid; /**< Our globally unique ID in hex encoding */ } DBusAuthServer; @@ -226,6 +232,8 @@ static dbus_bool_t send_error (DBusAuth *auth, static dbus_bool_t send_ok (DBusAuth *auth); static dbus_bool_t send_begin (DBusAuth *auth); static dbus_bool_t send_cancel (DBusAuth *auth); +static dbus_bool_t send_negotiate_unix_fd (DBusAuth *auth); +static dbus_bool_t send_agree_unix_fd (DBusAuth *auth); /** * Client states @@ -264,6 +272,9 @@ static dbus_bool_t handle_client_state_waiting_for_ok (DBusAuth *aut static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth *auth, DBusAuthCommand command, const DBusString *args); +static dbus_bool_t handle_client_state_waiting_for_agree_unix_fd (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args); static const DBusAuthStateData client_state_need_send_auth = { "NeedSendAuth", NULL @@ -277,7 +288,10 @@ static const DBusAuthStateData client_state_waiting_for_ok = { static const DBusAuthStateData client_state_waiting_for_reject = { "WaitingForReject", handle_client_state_waiting_for_reject }; - +static const DBusAuthStateData client_state_waiting_for_agree_unix_fd = { + "WaitingForAgreeUnixFD", handle_client_state_waiting_for_agree_unix_fd +}; + /** * Common terminal states. Terminal states have handler == NULL. */ @@ -330,10 +344,6 @@ _dbus_auth_new (int size) return NULL; auth->refcount = 1; - - _dbus_credentials_clear (&auth->credentials); - _dbus_credentials_clear (&auth->authorized_identity); - _dbus_credentials_clear (&auth->desired_identity); auth->keyring = NULL; auth->cookie_id = -1; @@ -364,9 +374,31 @@ _dbus_auth_new (int size) /* default context if none is specified */ if (!_dbus_string_append (&auth->context, "org_freedesktop_general")) goto enomem_5; + + auth->credentials = _dbus_credentials_new (); + if (auth->credentials == NULL) + goto enomem_6; + + auth->authorized_identity = _dbus_credentials_new (); + if (auth->authorized_identity == NULL) + goto enomem_7; + + auth->desired_identity = _dbus_credentials_new (); + if (auth->desired_identity == NULL) + goto enomem_8; return auth; +#if 0 + enomem_9: + _dbus_credentials_unref (auth->desired_identity); +#endif + enomem_8: + _dbus_credentials_unref (auth->authorized_identity); + enomem_7: + _dbus_credentials_unref (auth->credentials); + enomem_6: + /* last alloc was an append to context, which is freed already below */ ; enomem_5: _dbus_string_free (&auth->challenge); enomem_4: @@ -389,8 +421,8 @@ shutdown_mech (DBusAuth *auth) auth->already_asked_for_initial_response = FALSE; _dbus_string_set_length (&auth->identity, 0); - _dbus_credentials_clear (&auth->authorized_identity); - _dbus_credentials_clear (&auth->desired_identity); + _dbus_credentials_clear (auth->authorized_identity); + _dbus_credentials_clear (auth->desired_identity); if (auth->mech != NULL) { @@ -406,6 +438,10 @@ shutdown_mech (DBusAuth *auth) } } +/* + * DBUS_COOKIE_SHA1 mechanism + */ + /* Returns TRUE but with an empty string hash if the * cookie_id isn't known. As with all this code * TRUE just means we had enough memory. @@ -512,7 +548,7 @@ sha1_handle_first_client_response (DBusAuth *auth, } } - if (!_dbus_credentials_from_username (data, &auth->desired_identity)) + if (!_dbus_credentials_add_from_user (auth->desired_identity, data)) { _dbus_verbose ("%s: Did not get a valid username from client\n", DBUS_AUTH_NAME (auth)); @@ -533,8 +569,8 @@ sha1_handle_first_client_response (DBusAuth *auth, * a different DBusAuth for every connection. */ if (auth->keyring && - !_dbus_keyring_is_for_user (auth->keyring, - data)) + !_dbus_keyring_is_for_credentials (auth->keyring, + auth->desired_identity)) { _dbus_keyring_unref (auth->keyring); auth->keyring = NULL; @@ -542,12 +578,10 @@ sha1_handle_first_client_response (DBusAuth *auth, if (auth->keyring == NULL) { - DBusError error; - dbus_error_init (&error); - auth->keyring = _dbus_keyring_new_homedir (data, - &auth->context, - &error); + auth->keyring = _dbus_keyring_new_for_credentials (auth->desired_identity, + &auth->context, + &error); if (auth->keyring == NULL) { @@ -707,14 +741,24 @@ sha1_handle_second_client_response (DBusAuth *auth, retval = TRUE; goto out_3; } - + + if (!_dbus_credentials_add_credentials (auth->authorized_identity, + auth->desired_identity)) + goto out_3; + + /* Copy process ID from the socket credentials if it's there + */ + if (!_dbus_credentials_add_credential (auth->authorized_identity, + DBUS_CREDENTIAL_UNIX_PROCESS_ID, + auth->credentials)) + goto out_3; + if (!send_ok (auth)) goto out_3; - _dbus_verbose ("%s: authenticated client with UID "DBUS_UID_FORMAT" using DBUS_COOKIE_SHA1\n", - DBUS_AUTH_NAME (auth), auth->desired_identity.uid); + _dbus_verbose ("%s: authenticated client using DBUS_COOKIE_SHA1\n", + DBUS_AUTH_NAME (auth)); - auth->authorized_identity = auth->desired_identity; retval = TRUE; out_3: @@ -750,15 +794,18 @@ static dbus_bool_t handle_client_initial_response_cookie_sha1_mech (DBusAuth *auth, DBusString *response) { - const DBusString *username; + DBusString username; dbus_bool_t retval; retval = FALSE; - if (!_dbus_username_from_current_process (&username)) + if (!_dbus_string_init (&username)) + return FALSE; + + if (!_dbus_append_user_from_current_process (&username)) goto out_0; - if (!_dbus_string_hex_encode (username, 0, + if (!_dbus_string_hex_encode (&username, 0, response, _dbus_string_get_length (response))) goto out_0; @@ -766,6 +813,8 @@ handle_client_initial_response_cookie_sha1_mech (DBusAuth *auth, retval = TRUE; out_0: + _dbus_string_free (&username); + return retval; } @@ -857,9 +906,9 @@ handle_client_data_cookie_sha1_mech (DBusAuth *auth, DBusError error; dbus_error_init (&error); - auth->keyring = _dbus_keyring_new_homedir (NULL, - &context, - &error); + auth->keyring = _dbus_keyring_new_for_credentials (NULL, + &context, + &error); if (auth->keyring == NULL) { @@ -963,11 +1012,15 @@ handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth) _dbus_string_set_length (&auth->challenge, 0); } +/* + * EXTERNAL mechanism + */ + static dbus_bool_t handle_server_data_external_mech (DBusAuth *auth, const DBusString *data) { - if (auth->credentials.uid == DBUS_UID_UNSET) + if (_dbus_credentials_are_anonymous (auth->credentials)) { _dbus_verbose ("%s: no credentials, mechanism EXTERNAL can't authenticate\n", DBUS_AUTH_NAME (auth)); @@ -1000,13 +1053,14 @@ handle_server_data_external_mech (DBusAuth *auth, _dbus_verbose ("%s: sending empty challenge asking client for auth identity\n", DBUS_AUTH_NAME (auth)); auth->already_asked_for_initial_response = TRUE; + goto_state (auth, &server_state_waiting_for_data); return TRUE; } else return FALSE; } - _dbus_credentials_clear (&auth->desired_identity); + _dbus_credentials_clear (auth->desired_identity); /* If auth->identity is still empty here, then client * responded with an empty string after we poked it for @@ -1015,12 +1069,16 @@ handle_server_data_external_mech (DBusAuth *auth, */ if (_dbus_string_get_length (&auth->identity) == 0) { - auth->desired_identity.uid = auth->credentials.uid; + if (!_dbus_credentials_add_credentials (auth->desired_identity, + auth->credentials)) + { + return FALSE; /* OOM */ + } } else { - if (!_dbus_parse_uid (&auth->identity, - &auth->desired_identity.uid)) + if (!_dbus_credentials_add_from_user (auth->desired_identity, + &auth->identity)) { _dbus_verbose ("%s: could not get credentials from uid string\n", DBUS_AUTH_NAME (auth)); @@ -1028,7 +1086,7 @@ handle_server_data_external_mech (DBusAuth *auth, } } - if (auth->desired_identity.uid == DBUS_UID_UNSET) + if (_dbus_credentials_are_anonymous (auth->desired_identity)) { _dbus_verbose ("%s: desired user %s is no good\n", DBUS_AUTH_NAME (auth), @@ -1036,32 +1094,40 @@ handle_server_data_external_mech (DBusAuth *auth, return send_rejected (auth); } - if (_dbus_credentials_match (&auth->desired_identity, - &auth->credentials)) + if (_dbus_credentials_are_superset (auth->credentials, + auth->desired_identity)) { - /* client has authenticated */ + /* client has authenticated */ + if (!_dbus_credentials_add_credentials (auth->authorized_identity, + auth->desired_identity)) + return FALSE; + + /* also copy process ID from the socket credentials + */ + if (!_dbus_credentials_add_credential (auth->authorized_identity, + DBUS_CREDENTIAL_UNIX_PROCESS_ID, + auth->credentials)) + return FALSE; + + /* also copy audit data from the socket credentials + */ + if (!_dbus_credentials_add_credential (auth->authorized_identity, + DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID, + auth->credentials)) + return FALSE; + if (!send_ok (auth)) return FALSE; - _dbus_verbose ("%s: authenticated client with UID "DBUS_UID_FORMAT - " matching socket credentials UID "DBUS_UID_FORMAT"\n", - DBUS_AUTH_NAME (auth), - auth->desired_identity.uid, - auth->credentials.uid); + _dbus_verbose ("%s: authenticated client based on socket credentials\n", + DBUS_AUTH_NAME (auth)); - auth->authorized_identity.pid = auth->credentials.pid; - auth->authorized_identity.uid = auth->desired_identity.uid; return TRUE; } else { - _dbus_verbose ("%s: credentials uid="DBUS_UID_FORMAT - " gid="DBUS_GID_FORMAT - " do not allow uid="DBUS_UID_FORMAT - " gid="DBUS_GID_FORMAT"\n", - DBUS_AUTH_NAME (auth), - auth->credentials.uid, auth->credentials.gid, - auth->desired_identity.uid, auth->desired_identity.gid); + _dbus_verbose ("%s: desired identity not found in socket credentials\n", + DBUS_AUTH_NAME (auth)); return send_rejected (auth); } } @@ -1085,9 +1151,8 @@ handle_client_initial_response_external_mech (DBusAuth *auth, if (!_dbus_string_init (&plaintext)) return FALSE; - - if (!_dbus_string_append_uint (&plaintext, - _dbus_getuid ())) + + if (!_dbus_append_user_from_current_process (&plaintext)) goto failed; if (!_dbus_string_hex_encode (&plaintext, 0, @@ -1118,13 +1183,115 @@ handle_client_shutdown_external_mech (DBusAuth *auth) } +/* + * ANONYMOUS mechanism + */ + +static dbus_bool_t +handle_server_data_anonymous_mech (DBusAuth *auth, + const DBusString *data) +{ + if (_dbus_string_get_length (data) > 0) + { + /* Client is allowed to send "trace" data, the only defined + * meaning is that if it contains '@' it is an email address, + * and otherwise it is anything else, and it's supposed to be + * UTF-8 + */ + if (!_dbus_string_validate_utf8 (data, 0, _dbus_string_get_length (data))) + { + _dbus_verbose ("%s: Received invalid UTF-8 trace data from ANONYMOUS client\n", + DBUS_AUTH_NAME (auth)); + return send_rejected (auth); + } + + _dbus_verbose ("%s: ANONYMOUS client sent trace string: '%s'\n", + DBUS_AUTH_NAME (auth), + _dbus_string_get_const_data (data)); + } + + /* We want to be anonymous (clear in case some other protocol got midway through I guess) */ + _dbus_credentials_clear (auth->desired_identity); + + /* Copy process ID from the socket credentials + */ + if (!_dbus_credentials_add_credential (auth->authorized_identity, + DBUS_CREDENTIAL_UNIX_PROCESS_ID, + auth->credentials)) + return FALSE; + + /* Anonymous is always allowed */ + if (!send_ok (auth)) + return FALSE; + + _dbus_verbose ("%s: authenticated client as anonymous\n", + DBUS_AUTH_NAME (auth)); + + return TRUE; +} + +static void +handle_server_shutdown_anonymous_mech (DBusAuth *auth) +{ + +} + +static dbus_bool_t +handle_client_initial_response_anonymous_mech (DBusAuth *auth, + DBusString *response) +{ + /* Our initial response is a "trace" string which must be valid UTF-8 + * and must be an email address if it contains '@'. + * We just send the dbus implementation info, like a user-agent or + * something, because... why not. There's nothing guaranteed here + * though, we could change it later. + */ + DBusString plaintext; + + if (!_dbus_string_init (&plaintext)) + return FALSE; + + if (!_dbus_string_append (&plaintext, + "libdbus " DBUS_VERSION_STRING)) + goto failed; + + if (!_dbus_string_hex_encode (&plaintext, 0, + response, + _dbus_string_get_length (response))) + goto failed; + + _dbus_string_free (&plaintext); + + return TRUE; + + failed: + _dbus_string_free (&plaintext); + return FALSE; +} + +static dbus_bool_t +handle_client_data_anonymous_mech (DBusAuth *auth, + const DBusString *data) +{ + + return TRUE; +} + +static void +handle_client_shutdown_anonymous_mech (DBusAuth *auth) +{ + +} + /* Put mechanisms here in order of preference. - * What I eventually want to have is: + * Right now we have: * - * - a mechanism that checks UNIX domain socket credentials - * - a simple magic cookie mechanism like X11 or ICE - * - mechanisms that chain to Cyrus SASL, so we can use anything it - * offers such as Kerberos, X509, whatever. + * - EXTERNAL checks socket credentials (or in the future, other info from the OS) + * - DBUS_COOKIE_SHA1 uses a cookie in the home directory, like xauth or ICE + * - ANONYMOUS checks nothing but doesn't auth the person as a user + * + * We might ideally add a mechanism to chain to Cyrus SASL so we can + * use its mechanisms as well. * */ static const DBusAuthMechanismHandler @@ -1145,6 +1312,14 @@ all_mechanisms[] = { handle_client_data_cookie_sha1_mech, NULL, NULL, handle_client_shutdown_cookie_sha1_mech }, + { "ANONYMOUS", + handle_server_data_anonymous_mech, + NULL, NULL, + handle_server_shutdown_anonymous_mech, + handle_client_initial_response_anonymous_mech, + handle_client_data_anonymous_mech, + NULL, NULL, + handle_client_shutdown_anonymous_mech }, { NULL, NULL } }; @@ -1327,25 +1502,80 @@ send_error (DBusAuth *auth, const char *message) static dbus_bool_t send_ok (DBusAuth *auth) { - if (_dbus_string_append (&auth->outgoing, "OK\r\n")) + int orig_len; + + orig_len = _dbus_string_get_length (&auth->outgoing); + + if (_dbus_string_append (&auth->outgoing, "OK ") && + _dbus_string_copy (& DBUS_AUTH_SERVER (auth)->guid, + 0, + &auth->outgoing, + _dbus_string_get_length (&auth->outgoing)) && + _dbus_string_append (&auth->outgoing, "\r\n")) { goto_state (auth, &server_state_waiting_for_begin); return TRUE; } else - return FALSE; + { + _dbus_string_set_length (&auth->outgoing, orig_len); + return FALSE; + } } static dbus_bool_t -send_begin (DBusAuth *auth) +send_begin (DBusAuth *auth) { - if (_dbus_string_append (&auth->outgoing, "BEGIN\r\n")) + + if (!_dbus_string_append (&auth->outgoing, + "BEGIN\r\n")) + return FALSE; + + goto_state (auth, &common_state_authenticated); + return TRUE; +} + +static dbus_bool_t +process_ok(DBusAuth *auth, + const DBusString *args_from_ok) { + + int end_of_hex; + + /* "args_from_ok" should be the GUID, whitespace already pulled off the front */ + _dbus_assert (_dbus_string_get_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server) == 0); + + /* We decode the hex string to binary, using guid_from_server as scratch... */ + + end_of_hex = 0; + if (!_dbus_string_hex_decode (args_from_ok, 0, &end_of_hex, + & DBUS_AUTH_CLIENT (auth)->guid_from_server, 0)) + return FALSE; + + /* now clear out the scratch */ + _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0); + + if (end_of_hex != _dbus_string_get_length (args_from_ok) || + end_of_hex == 0) { - goto_state (auth, &common_state_authenticated); + _dbus_verbose ("Bad GUID from server, parsed %d bytes and had %d bytes from server\n", + end_of_hex, _dbus_string_get_length (args_from_ok)); + goto_state (auth, &common_state_need_disconnect); return TRUE; } - else - return FALSE; + + if (!_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0)) { + _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0); + return FALSE; + } + + _dbus_verbose ("Got GUID '%s' from the server\n", + _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server)); + + if (auth->unix_fd_possible) + return send_negotiate_unix_fd(auth); + + _dbus_verbose("Not negotiating unix fd passing, since not possible\n"); + return send_begin (auth); } static dbus_bool_t @@ -1362,8 +1592,8 @@ send_cancel (DBusAuth *auth) static dbus_bool_t process_data (DBusAuth *auth, - const DBusString *args, - DBusAuthDataFunction data_func) + const DBusString *args, + DBusAuthDataFunction data_func) { int end; DBusString decoded; @@ -1405,6 +1635,33 @@ process_data (DBusAuth *auth, } static dbus_bool_t +send_negotiate_unix_fd (DBusAuth *auth) +{ + if (!_dbus_string_append (&auth->outgoing, + "NEGOTIATE_UNIX_FD\r\n")) + return FALSE; + + goto_state (auth, &client_state_waiting_for_agree_unix_fd); + return TRUE; +} + +static dbus_bool_t +send_agree_unix_fd (DBusAuth *auth) +{ + _dbus_assert(auth->unix_fd_possible); + + auth->unix_fd_negotiated = TRUE; + _dbus_verbose("Agreed to UNIX FD passing\n"); + + if (!_dbus_string_append (&auth->outgoing, + "AGREE_UNIX_FD\r\n")) + return FALSE; + + goto_state (auth, &server_state_waiting_for_begin); + return TRUE; +} + +static dbus_bool_t handle_auth (DBusAuth *auth, const DBusString *args) { if (_dbus_string_get_length (args) == 0) @@ -1495,9 +1752,13 @@ handle_server_state_waiting_for_auth (DBusAuth *auth, case DBUS_AUTH_COMMAND_ERROR: return send_rejected (auth); + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + return send_error (auth, "Need to authenticate first"); + case DBUS_AUTH_COMMAND_REJECTED: case DBUS_AUTH_COMMAND_OK: case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: default: return send_error (auth, "Unknown command"); } @@ -1524,9 +1785,13 @@ handle_server_state_waiting_for_data (DBusAuth *auth, goto_state (auth, &common_state_need_disconnect); return TRUE; + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + return send_error (auth, "Need to authenticate first"); + case DBUS_AUTH_COMMAND_REJECTED: case DBUS_AUTH_COMMAND_OK: case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: default: return send_error (auth, "Unknown command"); } @@ -1549,9 +1814,16 @@ handle_server_state_waiting_for_begin (DBusAuth *auth, goto_state (auth, &common_state_authenticated); return TRUE; + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + if (auth->unix_fd_possible) + return send_agree_unix_fd(auth); + else + return send_error(auth, "Unix FD passing not supported, not authenticated or otherwise not possible"); + case DBUS_AUTH_COMMAND_REJECTED: case DBUS_AUTH_COMMAND_OK: case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: default: return send_error (auth, "Unknown command"); @@ -1623,14 +1895,22 @@ record_mechanisms (DBusAuth *auth, * it lists things in that order anyhow. */ - _dbus_verbose ("%s: Adding mechanism %s to list we will try\n", - DBUS_AUTH_NAME (auth), mech->mechanism); + if (mech != &all_mechanisms[0]) + { + _dbus_verbose ("%s: Adding mechanism %s to list we will try\n", + DBUS_AUTH_NAME (auth), mech->mechanism); - if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try, - (void*) mech)) + if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try, + (void*) mech)) + { + _dbus_string_free (&m); + goto nomem; + } + } + else { - _dbus_string_free (&m); - goto nomem; + _dbus_verbose ("%s: Already tried mechanism %s; not adding to list we will try\n", + DBUS_AUTH_NAME (auth), mech->mechanism); } } else @@ -1708,7 +1988,7 @@ handle_client_state_waiting_for_data (DBusAuth *auth, return process_rejected (auth, args); case DBUS_AUTH_COMMAND_OK: - return send_begin (auth); + return process_ok(auth, args); case DBUS_AUTH_COMMAND_ERROR: return send_cancel (auth); @@ -1717,6 +1997,8 @@ handle_client_state_waiting_for_data (DBusAuth *auth, case DBUS_AUTH_COMMAND_CANCEL: case DBUS_AUTH_COMMAND_BEGIN: case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: default: return send_error (auth, "Unknown command"); } @@ -1733,7 +2015,7 @@ handle_client_state_waiting_for_ok (DBusAuth *auth, return process_rejected (auth, args); case DBUS_AUTH_COMMAND_OK: - return send_begin (auth); + return process_ok(auth, args); case DBUS_AUTH_COMMAND_DATA: case DBUS_AUTH_COMMAND_ERROR: @@ -1743,6 +2025,8 @@ handle_client_state_waiting_for_ok (DBusAuth *auth, case DBUS_AUTH_COMMAND_CANCEL: case DBUS_AUTH_COMMAND_BEGIN: case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: default: return send_error (auth, "Unknown command"); } @@ -1765,12 +2049,46 @@ handle_client_state_waiting_for_reject (DBusAuth *auth, case DBUS_AUTH_COMMAND_OK: case DBUS_AUTH_COMMAND_ERROR: case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: default: goto_state (auth, &common_state_need_disconnect); return TRUE; } } +static dbus_bool_t +handle_client_state_waiting_for_agree_unix_fd(DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args) +{ + switch (command) + { + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: + _dbus_assert(auth->unix_fd_possible); + auth->unix_fd_negotiated = TRUE; + _dbus_verbose("Successfully negotiated UNIX FD passing\n"); + return send_begin (auth); + + case DBUS_AUTH_COMMAND_ERROR: + _dbus_assert(auth->unix_fd_possible); + auth->unix_fd_negotiated = FALSE; + _dbus_verbose("Failed to negotiate UNIX FD passing\n"); + return send_begin (auth); + + case DBUS_AUTH_COMMAND_OK: + case DBUS_AUTH_COMMAND_DATA: + case DBUS_AUTH_COMMAND_REJECTED: + case DBUS_AUTH_COMMAND_AUTH: + case DBUS_AUTH_COMMAND_CANCEL: + case DBUS_AUTH_COMMAND_BEGIN: + case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + default: + return send_error (auth, "Unknown command"); + } +} + /** * Mapping from command name to enum */ @@ -1779,14 +2097,16 @@ typedef struct { DBusAuthCommand command; /**< Corresponding enum */ } DBusAuthCommandName; -static DBusAuthCommandName auth_command_names[] = { - { "AUTH", DBUS_AUTH_COMMAND_AUTH }, - { "CANCEL", DBUS_AUTH_COMMAND_CANCEL }, - { "DATA", DBUS_AUTH_COMMAND_DATA }, - { "BEGIN", DBUS_AUTH_COMMAND_BEGIN }, - { "REJECTED", DBUS_AUTH_COMMAND_REJECTED }, - { "OK", DBUS_AUTH_COMMAND_OK }, - { "ERROR", DBUS_AUTH_COMMAND_ERROR } +static const DBusAuthCommandName auth_command_names[] = { + { "AUTH", DBUS_AUTH_COMMAND_AUTH }, + { "CANCEL", DBUS_AUTH_COMMAND_CANCEL }, + { "DATA", DBUS_AUTH_COMMAND_DATA }, + { "BEGIN", DBUS_AUTH_COMMAND_BEGIN }, + { "REJECTED", DBUS_AUTH_COMMAND_REJECTED }, + { "OK", DBUS_AUTH_COMMAND_OK }, + { "ERROR", DBUS_AUTH_COMMAND_ERROR }, + { "NEGOTIATE_UNIX_FD", DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD }, + { "AGREE_UNIX_FD", DBUS_AUTH_COMMAND_AGREE_UNIX_FD } }; static DBusAuthCommand @@ -1805,7 +2125,8 @@ lookup_command_from_name (DBusString *command) } static void -goto_state (DBusAuth *auth, const DBusAuthStateData *state) +goto_state (DBusAuth *auth, + const DBusAuthStateData *state) { _dbus_verbose ("%s: going from state %s to state %s\n", DBUS_AUTH_NAME (auth), @@ -1874,7 +2195,7 @@ process_command (DBusAuth *auth) if (!_dbus_string_move (&line, i, &args, 0)) goto out; - /* FIXME we should probably validate that only the allowed + /* FIXME 1.0 we should probably validate that only the allowed * chars are in the command name */ @@ -1917,26 +2238,41 @@ process_command (DBusAuth *auth) /** * Creates a new auth conversation object for the server side. - * See doc/dbus-sasl-profile.txt for full details on what - * this object does. + * See http://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol + * for full details on what this object does. * * @returns the new object or #NULL if no memory */ DBusAuth* -_dbus_auth_server_new (void) +_dbus_auth_server_new (const DBusString *guid) { DBusAuth *auth; DBusAuthServer *server_auth; + DBusString guid_copy; - auth = _dbus_auth_new (sizeof (DBusAuthServer)); - if (auth == NULL) + if (!_dbus_string_init (&guid_copy)) return NULL; + if (!_dbus_string_copy (guid, 0, &guid_copy, 0)) + { + _dbus_string_free (&guid_copy); + return NULL; + } + + auth = _dbus_auth_new (sizeof (DBusAuthServer)); + if (auth == NULL) + { + _dbus_string_free (&guid_copy); + return NULL; + } + auth->side = auth_side_server; auth->state = &server_state_waiting_for_auth; server_auth = DBUS_AUTH_SERVER (auth); + server_auth->guid = guid_copy; + /* perhaps this should be per-mechanism with a lower * max */ @@ -1948,8 +2284,8 @@ _dbus_auth_server_new (void) /** * Creates a new auth conversation object for the client side. - * See doc/dbus-sasl-profile.txt for full details on what - * this object does. + * See http://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol + * for full details on what this object does. * * @returns the new object or #NULL if no memory */ @@ -1957,10 +2293,19 @@ DBusAuth* _dbus_auth_client_new (void) { DBusAuth *auth; + DBusString guid_str; + + if (!_dbus_string_init (&guid_str)) + return NULL; auth = _dbus_auth_new (sizeof (DBusAuthClient)); if (auth == NULL) - return NULL; + { + _dbus_string_free (&guid_str); + return NULL; + } + + DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str; auth->side = auth_side_client; auth->state = &client_state_need_send_auth; @@ -1977,6 +2322,47 @@ _dbus_auth_client_new (void) } /** + * Creates a new auth conversation object for the client side of kdbus. + * In fact it only initialize structures and sets authenticated state + * because of different authentication-like mechanism in kdbus - policies + * TODO Probably to be checked and modified when kdbus will be documented + * + * @returns the new object or #NULL if no memory + */ +DBusAuth* +_dbus_auth_client_new_kdbus (void) +{ + DBusAuth *auth; + DBusString guid_str; + + if (!_dbus_string_init (&guid_str)) + return NULL; + + auth = _dbus_auth_new (sizeof (DBusAuthClient)); + if (auth == NULL) + { + _dbus_string_free (&guid_str); + return NULL; + } + + DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str; + + auth->side = auth_side_client; + auth->state = &common_state_authenticated; + auth->unix_fd_negotiated = TRUE; + + /* Start the auth conversation by sending AUTH for our default + * mechanism */ +/* if (!send_auth (auth, &all_mechanisms[0])) + { + _dbus_auth_unref (auth); + return NULL; + }*/ + + return auth; +} + +/** * Increments the refcount of an auth object. * * @param auth the auth conversation @@ -2010,8 +2396,15 @@ _dbus_auth_unref (DBusAuth *auth) if (DBUS_AUTH_IS_CLIENT (auth)) { + _dbus_string_free (& DBUS_AUTH_CLIENT (auth)->guid_from_server); _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try); } + else + { + _dbus_assert (DBUS_AUTH_IS_SERVER (auth)); + + _dbus_string_free (& DBUS_AUTH_SERVER (auth)->guid); + } if (auth->keyring) _dbus_keyring_unref (auth->keyring); @@ -2023,6 +2416,10 @@ _dbus_auth_unref (DBusAuth *auth) _dbus_string_free (&auth->outgoing); dbus_free_string_array (auth->allowed_mechs); + + _dbus_credentials_unref (auth->credentials); + _dbus_credentials_unref (auth->authorized_identity); + _dbus_credentials_unref (auth->desired_identity); dbus_free (auth); } @@ -2315,7 +2712,7 @@ _dbus_auth_needs_decoding (DBusAuth *auth) * the peer. If no encoding was negotiated, just copies the bytes (you * can avoid this by checking _dbus_auth_needs_decoding()). * - * @todo We need to be able to distinguish "out of memory" error + * @todo 1.0? We need to be able to distinguish "out of memory" error * from "the data is hosed" error. * * @param auth the auth conversation @@ -2353,29 +2750,59 @@ _dbus_auth_decode_data (DBusAuth *auth, * * @param auth the auth conversation * @param credentials the credentials received + * @returns #FALSE on OOM */ -void +dbus_bool_t _dbus_auth_set_credentials (DBusAuth *auth, - const DBusCredentials *credentials) + DBusCredentials *credentials) { - auth->credentials = *credentials; + _dbus_credentials_clear (auth->credentials); + return _dbus_credentials_add_credentials (auth->credentials, + credentials); } /** * Gets the identity we authorized the client as. Apps may have * different policies as to what identities they allow. * + * Returned credentials are not a copy and should not be modified + * * @param auth the auth conversation - * @param credentials the credentials we've authorized + * @returns the credentials we've authorized BY REFERENCE do not modify */ -void -_dbus_auth_get_identity (DBusAuth *auth, - DBusCredentials *credentials) +DBusCredentials* +_dbus_auth_get_identity (DBusAuth *auth) +{ + if (auth->state == &common_state_authenticated) + { + return auth->authorized_identity; + } + else + { + /* FIXME instead of this, keep an empty credential around that + * doesn't require allocation or something + */ + /* return empty credentials */ + _dbus_assert (_dbus_credentials_are_empty (auth->authorized_identity)); + return auth->authorized_identity; + } +} + +/** + * Gets the GUID from the server if we've authenticated; gets + * #NULL otherwise. + * @param auth the auth object + * @returns the GUID in ASCII hex format + */ +const char* +_dbus_auth_get_guid_from_server (DBusAuth *auth) { + _dbus_assert (DBUS_AUTH_IS_CLIENT (auth)); + if (auth->state == &common_state_authenticated) - *credentials = auth->authorized_identity; + return _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server); else - _dbus_credentials_clear (credentials); + return NULL; } /** @@ -2394,6 +2821,31 @@ _dbus_auth_set_context (DBusAuth *auth, &auth->context, 0, _dbus_string_get_length (context)); } +/** + * Sets whether unix fd passing is potentially on the transport and + * hence shall be negotiated. + * + * @param auth the auth conversation + * @param b TRUE when unix fd passing shall be negotiated, otherwise FALSE + */ +void +_dbus_auth_set_unix_fd_possible(DBusAuth *auth, dbus_bool_t b) +{ + auth->unix_fd_possible = b; +} + +/** + * Queries whether unix fd passing was successfully negotiated. + * + * @param auth the auth conversion + * @returns #TRUE when unix fd passing was negotiated. + */ +dbus_bool_t +_dbus_auth_get_unix_fd_negotiated(DBusAuth *auth) +{ + return auth->unix_fd_negotiated; +} + /** @} */ /* tests in dbus-auth-util.c */