1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-auth.c Authentication
4 * Copyright (C) 2002, 2003 Red Hat Inc.
6 * Licensed under the Academic Free License version 1.2
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "dbus-auth.h"
24 #include "dbus-string.h"
25 #include "dbus-list.h"
26 #include "dbus-internals.h"
27 #include "dbus-keyring.h"
29 #include "dbus-userdb.h"
31 /* See doc/dbus-sasl-profile.txt */
34 * @defgroup DBusAuth Authentication
35 * @ingroup DBusInternals
36 * @brief DBusAuth object
38 * DBusAuth manages the authentication negotiation when a connection
39 * is first established, and also manage any encryption used over a
42 * The file doc/dbus-sasl-profile.txt documents the network protocol
43 * used for authentication.
45 * @todo some SASL profiles require sending the empty string as a
46 * challenge/response, but we don't currently allow that in our
49 * @todo DBusAuth really needs to be rewritten as an explicit state
50 * machine. Right now it's too hard to prove to yourself by inspection
53 * @todo right now sometimes both ends will block waiting for input
54 * from the other end, e.g. if there's an error during
57 * @todo the cookie keyring needs to be cached globally not just
58 * per-auth (which raises threadsafety issues too)
60 * @todo grep FIXME in dbus-auth.c
64 * @defgroup DBusAuthInternals Authentication implementation details
65 * @ingroup DBusInternals
66 * @brief DBusAuth implementation details
68 * Private details of authentication code.
74 * Processes a command. Returns whether we had enough memory to
75 * complete the operation.
77 typedef dbus_bool_t (* DBusProcessAuthCommandFunction) (DBusAuth *auth,
78 const DBusString *command,
79 const DBusString *args);
84 DBusProcessAuthCommandFunction func;
85 } DBusAuthCommandHandler;
88 * This function appends an initial client response to the given string
90 typedef dbus_bool_t (* DBusInitialResponseFunction) (DBusAuth *auth,
91 DBusString *response);
94 * This function processes a block of data received from the peer.
95 * i.e. handles a DATA command.
97 typedef dbus_bool_t (* DBusAuthDataFunction) (DBusAuth *auth,
98 const DBusString *data);
101 * This function encodes a block of data from the peer.
103 typedef dbus_bool_t (* DBusAuthEncodeFunction) (DBusAuth *auth,
104 const DBusString *data,
105 DBusString *encoded);
108 * This function decodes a block of data from the peer.
110 typedef dbus_bool_t (* DBusAuthDecodeFunction) (DBusAuth *auth,
111 const DBusString *data,
112 DBusString *decoded);
115 * This function is called when the mechanism is abandoned.
117 typedef void (* DBusAuthShutdownFunction) (DBusAuth *auth);
121 const char *mechanism;
122 DBusAuthDataFunction server_data_func;
123 DBusAuthEncodeFunction server_encode_func;
124 DBusAuthDecodeFunction server_decode_func;
125 DBusAuthShutdownFunction server_shutdown_func;
126 DBusInitialResponseFunction client_initial_response_func;
127 DBusAuthDataFunction client_data_func;
128 DBusAuthEncodeFunction client_encode_func;
129 DBusAuthDecodeFunction client_decode_func;
130 DBusAuthShutdownFunction client_shutdown_func;
131 } DBusAuthMechanismHandler;
134 * Internal members of DBusAuth.
138 int refcount; /**< reference count */
140 DBusString incoming; /**< Incoming data buffer */
141 DBusString outgoing; /**< Outgoing data buffer */
143 const DBusAuthCommandHandler *handlers; /**< Handlers for commands */
145 const DBusAuthMechanismHandler *mech; /**< Current auth mechanism */
147 DBusString identity; /**< Current identity we're authorizing
151 DBusCredentials credentials; /**< Credentials read from socket,
155 DBusCredentials authorized_identity; /**< Credentials that are authorized */
157 DBusCredentials desired_identity; /**< Identity client has requested */
159 DBusString context; /**< Cookie scope */
160 DBusKeyring *keyring; /**< Keyring for cookie mechanism. */
161 int cookie_id; /**< ID of cookie to use */
162 DBusString challenge; /**< Challenge sent to client */
164 char **allowed_mechs; /**< Mechanisms we're allowed to use,
165 * or #NULL if we can use any
168 unsigned int needed_memory : 1; /**< We needed memory to continue since last
169 * successful getting something done
171 unsigned int need_disconnect : 1; /**< We've given up, time to disconnect */
172 unsigned int authenticated : 1; /**< We are authenticated */
173 unsigned int authenticated_pending_output : 1; /**< Authenticated once we clear outgoing buffer */
174 unsigned int authenticated_pending_begin : 1; /**< Authenticated once we get BEGIN */
175 unsigned int already_got_mechanisms : 1; /**< Client already got mech list */
176 unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */
177 unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */
184 DBusList *mechs_to_try; /**< Mechanisms we got from the server that we're going to try using */
192 int failures; /**< Number of times client has been rejected */
193 int max_failures; /**< Number of times we reject before disconnect */
197 static dbus_bool_t process_auth (DBusAuth *auth,
198 const DBusString *command,
199 const DBusString *args);
200 static dbus_bool_t process_cancel (DBusAuth *auth,
201 const DBusString *command,
202 const DBusString *args);
203 static dbus_bool_t process_begin (DBusAuth *auth,
204 const DBusString *command,
205 const DBusString *args);
206 static dbus_bool_t process_data_server (DBusAuth *auth,
207 const DBusString *command,
208 const DBusString *args);
209 static dbus_bool_t process_error_server (DBusAuth *auth,
210 const DBusString *command,
211 const DBusString *args);
212 static dbus_bool_t process_rejected (DBusAuth *auth,
213 const DBusString *command,
214 const DBusString *args);
215 static dbus_bool_t process_ok (DBusAuth *auth,
216 const DBusString *command,
217 const DBusString *args);
218 static dbus_bool_t process_data_client (DBusAuth *auth,
219 const DBusString *command,
220 const DBusString *args);
221 static dbus_bool_t process_error_client (DBusAuth *auth,
222 const DBusString *command,
223 const DBusString *args);
226 static dbus_bool_t client_try_next_mechanism (DBusAuth *auth);
227 static dbus_bool_t send_rejected (DBusAuth *auth);
229 static DBusAuthCommandHandler
230 server_handlers[] = {
231 { "AUTH", process_auth },
232 { "CANCEL", process_cancel },
233 { "BEGIN", process_begin },
234 { "DATA", process_data_server },
235 { "ERROR", process_error_server },
239 static DBusAuthCommandHandler
240 client_handlers[] = {
241 { "REJECTED", process_rejected },
242 { "OK", process_ok },
243 { "DATA", process_data_client },
244 { "ERROR", process_error_client },
249 * @param auth the auth conversation
250 * @returns #TRUE if the conversation is the server side
252 #define DBUS_AUTH_IS_SERVER(auth) ((auth)->handlers == server_handlers)
254 * @param auth the auth conversation
255 * @returns #TRUE if the conversation is the client side
257 #define DBUS_AUTH_IS_CLIENT(auth) ((auth)->handlers == client_handlers)
259 * @param auth the auth conversation
260 * @returns auth cast to DBusAuthClient
262 #define DBUS_AUTH_CLIENT(auth) ((DBusAuthClient*)(auth))
264 * @param auth the auth conversation
265 * @returns auth cast to DBusAuthServer
267 #define DBUS_AUTH_SERVER(auth) ((DBusAuthServer*)(auth))
270 _dbus_auth_new (int size)
274 auth = dbus_malloc0 (size);
280 _dbus_credentials_clear (&auth->credentials);
281 _dbus_credentials_clear (&auth->authorized_identity);
282 _dbus_credentials_clear (&auth->desired_identity);
284 auth->keyring = NULL;
285 auth->cookie_id = -1;
287 /* note that we don't use the max string length feature,
288 * because you can't use that feature if you're going to
289 * try to recover from out-of-memory (it creates
290 * what looks like unrecoverable inability to alloc
291 * more space in the string). But we do handle
292 * overlong buffers in _dbus_auth_do_work().
295 if (!_dbus_string_init (&auth->incoming))
298 if (!_dbus_string_init (&auth->outgoing))
301 if (!_dbus_string_init (&auth->identity))
304 if (!_dbus_string_init (&auth->context))
307 if (!_dbus_string_init (&auth->challenge))
310 /* default context if none is specified */
311 if (!_dbus_string_append (&auth->context, "org_freedesktop_general"))
317 _dbus_string_free (&auth->challenge);
319 _dbus_string_free (&auth->context);
321 _dbus_string_free (&auth->identity);
323 _dbus_string_free (&auth->outgoing);
325 _dbus_string_free (&auth->incoming);
332 shutdown_mech (DBusAuth *auth)
334 /* Cancel any auth */
335 auth->authenticated_pending_begin = FALSE;
336 auth->authenticated = FALSE;
337 auth->already_asked_for_initial_response = FALSE;
338 _dbus_string_set_length (&auth->identity, 0);
340 _dbus_credentials_clear (&auth->authorized_identity);
341 _dbus_credentials_clear (&auth->desired_identity);
343 if (auth->mech != NULL)
345 _dbus_verbose ("Shutting down mechanism %s\n",
346 auth->mech->mechanism);
348 if (DBUS_AUTH_IS_CLIENT (auth))
349 (* auth->mech->client_shutdown_func) (auth);
351 (* auth->mech->server_shutdown_func) (auth);
357 /* Returns TRUE but with an empty string hash if the
358 * cookie_id isn't known. As with all this code
359 * TRUE just means we had enough memory.
362 sha1_compute_hash (DBusAuth *auth,
364 const DBusString *server_challenge,
365 const DBusString *client_challenge,
372 _dbus_assert (auth->keyring != NULL);
376 if (!_dbus_string_init (&cookie))
379 if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id,
383 if (_dbus_string_get_length (&cookie) == 0)
389 if (!_dbus_string_init (&to_hash))
392 if (!_dbus_string_copy (server_challenge, 0,
393 &to_hash, _dbus_string_get_length (&to_hash)))
396 if (!_dbus_string_append (&to_hash, ":"))
399 if (!_dbus_string_copy (client_challenge, 0,
400 &to_hash, _dbus_string_get_length (&to_hash)))
403 if (!_dbus_string_append (&to_hash, ":"))
406 if (!_dbus_string_copy (&cookie, 0,
407 &to_hash, _dbus_string_get_length (&to_hash)))
410 if (!_dbus_sha_compute (&to_hash, hash))
416 _dbus_string_zero (&to_hash);
417 _dbus_string_free (&to_hash);
419 _dbus_string_zero (&cookie);
420 _dbus_string_free (&cookie);
424 /** http://www.ietf.org/rfc/rfc2831.txt suggests at least 64 bits of
425 * entropy, we use 128. This is the number of bytes in the random
428 #define N_CHALLENGE_BYTES (128/8)
431 sha1_handle_first_client_response (DBusAuth *auth,
432 const DBusString *data)
434 /* We haven't sent a challenge yet, we're expecting a desired
435 * username from the client.
445 _dbus_string_set_length (&auth->challenge, 0);
447 if (_dbus_string_get_length (data) > 0)
449 if (_dbus_string_get_length (&auth->identity) > 0)
451 /* Tried to send two auth identities, wtf */
452 _dbus_verbose ("client tried to send auth identity, but we already have one\n");
453 return send_rejected (auth);
457 /* this is our auth identity */
458 if (!_dbus_string_copy (data, 0, &auth->identity, 0))
463 if (!_dbus_credentials_from_username (data, &auth->desired_identity))
465 _dbus_verbose ("Did not get a valid username from client\n");
466 return send_rejected (auth);
469 if (!_dbus_string_init (&tmp))
472 if (!_dbus_string_init (&tmp2))
474 _dbus_string_free (&tmp);
478 old_len = _dbus_string_get_length (&auth->outgoing);
480 /* we cache the keyring for speed, so here we drop it if it's the
481 * wrong one. FIXME caching the keyring here is useless since we use
482 * a different DBusAuth for every connection.
485 !_dbus_keyring_is_for_user (auth->keyring,
488 _dbus_keyring_unref (auth->keyring);
489 auth->keyring = NULL;
492 if (auth->keyring == NULL)
496 dbus_error_init (&error);
497 auth->keyring = _dbus_keyring_new_homedir (data,
501 if (auth->keyring == NULL)
503 if (dbus_error_has_name (&error,
504 DBUS_ERROR_NO_MEMORY))
506 dbus_error_free (&error);
511 _DBUS_ASSERT_ERROR_IS_SET (&error);
512 _dbus_verbose ("Error loading keyring: %s\n",
514 if (send_rejected (auth))
515 retval = TRUE; /* retval is only about mem */
516 dbus_error_free (&error);
522 _dbus_assert (!dbus_error_is_set (&error));
526 _dbus_assert (auth->keyring != NULL);
528 dbus_error_init (&error);
529 auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error);
530 if (auth->cookie_id < 0)
532 _DBUS_ASSERT_ERROR_IS_SET (&error);
533 _dbus_verbose ("Could not get a cookie ID to send to client: %s\n",
535 if (send_rejected (auth))
537 dbus_error_free (&error);
542 _dbus_assert (!dbus_error_is_set (&error));
545 if (!_dbus_string_copy (&auth->context, 0,
546 &tmp2, _dbus_string_get_length (&tmp2)))
549 if (!_dbus_string_append (&tmp2, " "))
552 if (!_dbus_string_append_int (&tmp2, auth->cookie_id))
555 if (!_dbus_string_append (&tmp2, " "))
558 if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
561 _dbus_string_set_length (&auth->challenge, 0);
562 if (!_dbus_string_hex_encode (&tmp, 0, &auth->challenge, 0))
565 if (!_dbus_string_hex_encode (&tmp, 0, &tmp2,
566 _dbus_string_get_length (&tmp2)))
569 if (!_dbus_string_append (&auth->outgoing,
573 if (!_dbus_string_base64_encode (&tmp2, 0, &auth->outgoing,
574 _dbus_string_get_length (&auth->outgoing)))
577 if (!_dbus_string_append (&auth->outgoing,
584 _dbus_string_zero (&tmp);
585 _dbus_string_free (&tmp);
586 _dbus_string_zero (&tmp2);
587 _dbus_string_free (&tmp2);
589 _dbus_string_set_length (&auth->outgoing, old_len);
594 sha1_handle_second_client_response (DBusAuth *auth,
595 const DBusString *data)
597 /* We are expecting a response which is the hex-encoded client
598 * challenge, space, then SHA-1 hash of the concatenation of our
599 * challenge, ":", client challenge, ":", secret key, all
603 DBusString client_challenge;
604 DBusString client_hash;
606 DBusString correct_hash;
610 if (!_dbus_string_find_blank (data, 0, &i))
612 _dbus_verbose ("no space separator in client response\n");
613 return send_rejected (auth);
616 if (!_dbus_string_init (&client_challenge))
619 if (!_dbus_string_init (&client_hash))
622 if (!_dbus_string_copy_len (data, 0, i, &client_challenge,
626 _dbus_string_skip_blank (data, i, &i);
628 if (!_dbus_string_copy_len (data, i,
629 _dbus_string_get_length (data) - i,
634 if (_dbus_string_get_length (&client_challenge) == 0 ||
635 _dbus_string_get_length (&client_hash) == 0)
637 _dbus_verbose ("zero-length client challenge or hash\n");
638 if (send_rejected (auth))
643 if (!_dbus_string_init (&correct_hash))
646 if (!sha1_compute_hash (auth, auth->cookie_id,
652 /* if cookie_id was invalid, then we get an empty hash */
653 if (_dbus_string_get_length (&correct_hash) == 0)
655 if (send_rejected (auth))
660 if (!_dbus_string_equal (&client_hash, &correct_hash))
662 if (send_rejected (auth))
667 if (!_dbus_string_append (&auth->outgoing,
671 _dbus_verbose ("authenticated client with UID "DBUS_UID_FORMAT" using DBUS_COOKIE_SHA1\n",
672 auth->desired_identity.uid);
674 auth->authorized_identity = auth->desired_identity;
675 auth->authenticated_pending_begin = TRUE;
679 _dbus_string_zero (&correct_hash);
680 _dbus_string_free (&correct_hash);
682 _dbus_string_zero (&client_hash);
683 _dbus_string_free (&client_hash);
685 _dbus_string_free (&client_challenge);
691 handle_server_data_cookie_sha1_mech (DBusAuth *auth,
692 const DBusString *data)
694 if (auth->cookie_id < 0)
695 return sha1_handle_first_client_response (auth, data);
697 return sha1_handle_second_client_response (auth, data);
701 handle_server_shutdown_cookie_sha1_mech (DBusAuth *auth)
703 auth->cookie_id = -1;
704 _dbus_string_set_length (&auth->challenge, 0);
708 handle_client_initial_response_cookie_sha1_mech (DBusAuth *auth,
709 DBusString *response)
711 const DBusString *username;
716 if (!_dbus_username_from_current_process (&username))
719 if (!_dbus_string_base64_encode (username, 0,
721 _dbus_string_get_length (response)))
730 /* FIXME if we send the server an error, right now both sides
731 * just hang. Server has to reject on getting an error, or
732 * client has to cancel. Should be in the spec.
735 handle_client_data_cookie_sha1_mech (DBusAuth *auth,
736 const DBusString *data)
738 /* The data we get from the server should be the cookie context
739 * name, the cookie ID, and the server challenge, separated by
740 * spaces. We send back our challenge string and the correct hash.
744 DBusString cookie_id_str;
745 DBusString server_challenge;
746 DBusString client_challenge;
747 DBusString correct_hash;
755 if (!_dbus_string_find_blank (data, 0, &i))
757 if (_dbus_string_append (&auth->outgoing,
758 "ERROR \"Server did not send context/ID/challenge properly\"\r\n"))
763 if (!_dbus_string_init (&context))
766 if (!_dbus_string_copy_len (data, 0, i,
770 _dbus_string_skip_blank (data, i, &i);
771 if (!_dbus_string_find_blank (data, i, &j))
773 if (_dbus_string_append (&auth->outgoing,
774 "ERROR \"Server did not send context/ID/challenge properly\"\r\n"))
779 if (!_dbus_string_init (&cookie_id_str))
782 if (!_dbus_string_copy_len (data, i, j - i,
786 if (!_dbus_string_init (&server_challenge))
790 _dbus_string_skip_blank (data, i, &i);
791 j = _dbus_string_get_length (data);
793 if (!_dbus_string_copy_len (data, i, j - i,
794 &server_challenge, 0))
797 if (!_dbus_keyring_validate_context (&context))
799 if (_dbus_string_append (&auth->outgoing,
800 "ERROR \"Server sent invalid cookie context\"\r\n"))
805 if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL))
807 if (_dbus_string_append (&auth->outgoing,
808 "ERROR \"Could not parse cookie ID as an integer\"\r\n"))
813 if (_dbus_string_get_length (&server_challenge) == 0)
815 if (_dbus_string_append (&auth->outgoing,
816 "ERROR \"Empty server challenge string\"\r\n"))
821 if (auth->keyring == NULL)
825 dbus_error_init (&error);
826 auth->keyring = _dbus_keyring_new_homedir (NULL,
830 if (auth->keyring == NULL)
832 if (dbus_error_has_name (&error,
833 DBUS_ERROR_NO_MEMORY))
835 dbus_error_free (&error);
840 _DBUS_ASSERT_ERROR_IS_SET (&error);
842 _dbus_verbose ("Error loading keyring: %s\n",
845 if (_dbus_string_append (&auth->outgoing,
846 "ERROR \"Could not load cookie file\"\r\n"))
847 retval = TRUE; /* retval is only about mem */
849 dbus_error_free (&error);
855 _dbus_assert (!dbus_error_is_set (&error));
859 _dbus_assert (auth->keyring != NULL);
861 if (!_dbus_string_init (&tmp))
864 if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
867 if (!_dbus_string_init (&client_challenge))
870 if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0))
873 if (!_dbus_string_init (&correct_hash))
876 if (!sha1_compute_hash (auth, val,
882 if (_dbus_string_get_length (&correct_hash) == 0)
884 /* couldn't find the cookie ID or something */
885 if (_dbus_string_append (&auth->outgoing,
886 "ERROR \"Don't have the requested cookie ID\"\r\n"))
891 _dbus_string_set_length (&tmp, 0);
893 if (!_dbus_string_copy (&client_challenge, 0, &tmp,
894 _dbus_string_get_length (&tmp)))
897 if (!_dbus_string_append (&tmp, " "))
900 if (!_dbus_string_copy (&correct_hash, 0, &tmp,
901 _dbus_string_get_length (&tmp)))
904 old_len = _dbus_string_get_length (&auth->outgoing);
905 if (!_dbus_string_append (&auth->outgoing, "DATA "))
908 if (!_dbus_string_base64_encode (&tmp, 0,
910 _dbus_string_get_length (&auth->outgoing)))
912 _dbus_string_set_length (&auth->outgoing, old_len);
916 if (!_dbus_string_append (&auth->outgoing, "\r\n"))
918 _dbus_string_set_length (&auth->outgoing, old_len);
925 _dbus_string_zero (&correct_hash);
926 _dbus_string_free (&correct_hash);
928 _dbus_string_free (&client_challenge);
930 _dbus_string_zero (&tmp);
931 _dbus_string_free (&tmp);
933 _dbus_string_free (&server_challenge);
935 _dbus_string_free (&cookie_id_str);
937 _dbus_string_free (&context);
943 handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth)
945 auth->cookie_id = -1;
946 _dbus_string_set_length (&auth->challenge, 0);
950 handle_server_data_external_mech (DBusAuth *auth,
951 const DBusString *data)
953 if (auth->credentials.uid == DBUS_UID_UNSET)
955 _dbus_verbose ("no credentials, mechanism EXTERNAL can't authenticate\n");
956 return send_rejected (auth);
959 if (_dbus_string_get_length (data) > 0)
961 if (_dbus_string_get_length (&auth->identity) > 0)
963 /* Tried to send two auth identities, wtf */
964 _dbus_verbose ("client tried to send auth identity, but we already have one\n");
965 return send_rejected (auth);
969 /* this is our auth identity */
970 if (!_dbus_string_copy (data, 0, &auth->identity, 0))
975 /* Poke client for an auth identity, if none given */
976 if (_dbus_string_get_length (&auth->identity) == 0 &&
977 !auth->already_asked_for_initial_response)
979 if (_dbus_string_append (&auth->outgoing,
982 _dbus_verbose ("sending empty challenge asking client for auth identity\n");
983 auth->already_asked_for_initial_response = TRUE;
990 _dbus_credentials_clear (&auth->desired_identity);
992 /* If auth->identity is still empty here, then client
993 * responded with an empty string after we poked it for
994 * an initial response. This means to try to auth the
995 * identity provided in the credentials.
997 if (_dbus_string_get_length (&auth->identity) == 0)
999 auth->desired_identity.uid = auth->credentials.uid;
1003 if (!_dbus_uid_from_string (&auth->identity,
1004 &auth->desired_identity.uid))
1006 _dbus_verbose ("could not get credentials from uid string\n");
1007 return send_rejected (auth);
1011 if (auth->desired_identity.uid == DBUS_UID_UNSET)
1013 _dbus_verbose ("desired user %s is no good\n",
1014 _dbus_string_get_const_data (&auth->identity));
1015 return send_rejected (auth);
1018 if (_dbus_credentials_match (&auth->desired_identity,
1019 &auth->credentials))
1021 /* client has authenticated */
1022 if (!_dbus_string_append (&auth->outgoing,
1026 _dbus_verbose ("authenticated client with UID "DBUS_UID_FORMAT
1027 " matching socket credentials UID "DBUS_UID_FORMAT"\n",
1028 auth->desired_identity.uid,
1029 auth->credentials.uid);
1031 auth->authorized_identity.uid = auth->desired_identity.uid;
1033 auth->authenticated_pending_begin = TRUE;
1039 _dbus_verbose ("credentials uid="DBUS_UID_FORMAT
1040 " gid="DBUS_GID_FORMAT
1041 " do not allow uid="DBUS_UID_FORMAT
1042 " gid="DBUS_GID_FORMAT"\n",
1043 auth->credentials.uid, auth->credentials.gid,
1044 auth->desired_identity.uid, auth->desired_identity.gid);
1045 return send_rejected (auth);
1050 handle_server_shutdown_external_mech (DBusAuth *auth)
1056 handle_client_initial_response_external_mech (DBusAuth *auth,
1057 DBusString *response)
1059 /* We always append our UID as an initial response, so the server
1060 * doesn't have to send back an empty challenge to check whether we
1061 * want to specify an identity. i.e. this avoids a round trip that
1062 * the spec for the EXTERNAL mechanism otherwise requires.
1064 DBusString plaintext;
1066 if (!_dbus_string_init (&plaintext))
1069 if (!_dbus_string_append_uint (&plaintext,
1073 if (!_dbus_string_base64_encode (&plaintext, 0,
1075 _dbus_string_get_length (response)))
1078 _dbus_string_free (&plaintext);
1083 _dbus_string_free (&plaintext);
1088 handle_client_data_external_mech (DBusAuth *auth,
1089 const DBusString *data)
1096 handle_client_shutdown_external_mech (DBusAuth *auth)
1101 /* Put mechanisms here in order of preference.
1102 * What I eventually want to have is:
1104 * - a mechanism that checks UNIX domain socket credentials
1105 * - a simple magic cookie mechanism like X11 or ICE
1106 * - mechanisms that chain to Cyrus SASL, so we can use anything it
1107 * offers such as Kerberos, X509, whatever.
1110 static const DBusAuthMechanismHandler
1111 all_mechanisms[] = {
1113 handle_server_data_external_mech,
1115 handle_server_shutdown_external_mech,
1116 handle_client_initial_response_external_mech,
1117 handle_client_data_external_mech,
1119 handle_client_shutdown_external_mech },
1120 { "DBUS_COOKIE_SHA1",
1121 handle_server_data_cookie_sha1_mech,
1123 handle_server_shutdown_cookie_sha1_mech,
1124 handle_client_initial_response_cookie_sha1_mech,
1125 handle_client_data_cookie_sha1_mech,
1127 handle_client_shutdown_cookie_sha1_mech },
1131 static const DBusAuthMechanismHandler*
1132 find_mech (const DBusString *name,
1133 char **allowed_mechs)
1137 if (allowed_mechs != NULL &&
1138 !_dbus_string_array_contains ((const char**) allowed_mechs,
1139 _dbus_string_get_const_data (name)))
1143 while (all_mechanisms[i].mechanism != NULL)
1145 if (_dbus_string_equal_c_str (name,
1146 all_mechanisms[i].mechanism))
1148 return &all_mechanisms[i];
1157 send_rejected (DBusAuth *auth)
1160 DBusAuthServer *server_auth;
1163 if (!_dbus_string_init (&command))
1166 if (!_dbus_string_append (&command,
1171 while (all_mechanisms[i].mechanism != NULL)
1173 if (!_dbus_string_append (&command,
1177 if (!_dbus_string_append (&command,
1178 all_mechanisms[i].mechanism))
1184 if (!_dbus_string_append (&command, "\r\n"))
1187 if (!_dbus_string_copy (&command, 0, &auth->outgoing,
1188 _dbus_string_get_length (&auth->outgoing)))
1191 shutdown_mech (auth);
1193 _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
1194 server_auth = DBUS_AUTH_SERVER (auth);
1195 server_auth->failures += 1;
1197 _dbus_string_free (&command);
1202 _dbus_string_free (&command);
1207 process_auth (DBusAuth *auth,
1208 const DBusString *command,
1209 const DBusString *args)
1213 /* We are already using a mechanism, client is on crack */
1214 if (!_dbus_string_append (&auth->outgoing,
1215 "ERROR \"Sent AUTH while another AUTH in progress\"\r\n"))
1220 else if (_dbus_string_get_length (args) == 0)
1222 /* No args to the auth, send mechanisms */
1223 if (!send_rejected (auth))
1232 DBusString base64_response;
1233 DBusString decoded_response;
1235 _dbus_string_find_blank (args, 0, &i);
1237 if (!_dbus_string_init (&mech))
1240 if (!_dbus_string_init (&base64_response))
1242 _dbus_string_free (&mech);
1246 if (!_dbus_string_init (&decoded_response))
1248 _dbus_string_free (&mech);
1249 _dbus_string_free (&base64_response);
1253 if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
1256 if (!_dbus_string_copy (args, i, &base64_response, 0))
1259 if (!_dbus_string_base64_decode (&base64_response, 0,
1260 &decoded_response, 0))
1263 auth->mech = find_mech (&mech, auth->allowed_mechs);
1264 if (auth->mech != NULL)
1266 _dbus_verbose ("Trying mechanism %s with initial response of %d bytes\n",
1267 auth->mech->mechanism,
1268 _dbus_string_get_length (&decoded_response));
1270 if (!(* auth->mech->server_data_func) (auth,
1276 /* Unsupported mechanism */
1277 if (!send_rejected (auth))
1281 _dbus_string_free (&mech);
1282 _dbus_string_free (&base64_response);
1283 _dbus_string_free (&decoded_response);
1289 _dbus_string_free (&mech);
1290 _dbus_string_free (&base64_response);
1291 _dbus_string_free (&decoded_response);
1297 process_cancel (DBusAuth *auth,
1298 const DBusString *command,
1299 const DBusString *args)
1301 shutdown_mech (auth);
1307 process_begin (DBusAuth *auth,
1308 const DBusString *command,
1309 const DBusString *args)
1311 if (auth->authenticated_pending_begin)
1312 auth->authenticated = TRUE;
1315 auth->need_disconnect = TRUE; /* client trying to send data before auth,
1318 shutdown_mech (auth);
1325 process_data_server (DBusAuth *auth,
1326 const DBusString *command,
1327 const DBusString *args)
1329 if (auth->mech != NULL)
1333 if (!_dbus_string_init (&decoded))
1336 if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
1338 _dbus_string_free (&decoded);
1342 #ifdef DBUS_ENABLE_VERBOSE_MODE
1343 if (_dbus_string_validate_ascii (&decoded, 0,
1344 _dbus_string_get_length (&decoded)))
1345 _dbus_verbose ("data: '%s'\n", _dbus_string_get_const_data (&decoded));
1348 if (!(* auth->mech->server_data_func) (auth, &decoded))
1350 _dbus_string_free (&decoded);
1354 _dbus_string_free (&decoded);
1358 if (!_dbus_string_append (&auth->outgoing,
1359 "ERROR \"Not currently in an auth conversation\"\r\n"))
1367 process_error_server (DBusAuth *auth,
1368 const DBusString *command,
1369 const DBusString *args)
1375 /* return FALSE if no memory, TRUE if all OK */
1377 get_word (const DBusString *str,
1383 _dbus_string_skip_blank (str, *start, start);
1384 _dbus_string_find_blank (str, *start, &i);
1388 if (!_dbus_string_copy_len (str, *start, i - *start, word, 0))
1398 record_mechanisms (DBusAuth *auth,
1399 const DBusString *command,
1400 const DBusString *args)
1405 if (auth->already_got_mechanisms)
1408 len = _dbus_string_get_length (args);
1414 const DBusAuthMechanismHandler *mech;
1416 if (!_dbus_string_init (&m))
1419 if (!get_word (args, &next, &m))
1422 mech = find_mech (&m, auth->allowed_mechs);
1426 /* FIXME right now we try mechanisms in the order
1427 * the server lists them; should we do them in
1428 * some more deterministic order?
1430 * Probably in all_mechanisms order, our order of
1431 * preference. Of course when the server is us,
1432 * it lists things in that order anyhow.
1435 _dbus_verbose ("Adding mechanism %s to list we will try\n",
1438 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1444 _dbus_verbose ("Server offered mechanism \"%s\" that we don't know how to use\n",
1445 _dbus_string_get_const_data (&m));
1448 _dbus_string_free (&m);
1451 auth->already_got_mechanisms = TRUE;
1456 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1462 client_try_next_mechanism (DBusAuth *auth)
1464 const DBusAuthMechanismHandler *mech;
1465 DBusString auth_command;
1466 DBusAuthClient *client;
1468 client = DBUS_AUTH_CLIENT (auth);
1470 /* Pop any mechs not in the list of allowed mechanisms */
1472 while (client->mechs_to_try != NULL)
1474 mech = client->mechs_to_try->data;
1476 if (auth->allowed_mechs != NULL &&
1477 !_dbus_string_array_contains ((const char**) auth->allowed_mechs,
1480 /* don't try this one after all */
1481 _dbus_verbose ("Mechanism %s isn't in the list of allowed mechanisms\n",
1484 _dbus_list_pop_first (& client->mechs_to_try);
1487 break; /* we'll try this one */
1493 if (!_dbus_string_init (&auth_command))
1496 if (!_dbus_string_append (&auth_command,
1499 _dbus_string_free (&auth_command);
1503 if (!_dbus_string_append (&auth_command,
1506 _dbus_string_free (&auth_command);
1510 if (mech->client_initial_response_func != NULL)
1512 if (!_dbus_string_append (&auth_command, " "))
1514 _dbus_string_free (&auth_command);
1518 if (!(* mech->client_initial_response_func) (auth, &auth_command))
1520 _dbus_string_free (&auth_command);
1525 if (!_dbus_string_append (&auth_command,
1528 _dbus_string_free (&auth_command);
1532 if (!_dbus_string_copy (&auth_command, 0,
1534 _dbus_string_get_length (&auth->outgoing)))
1536 _dbus_string_free (&auth_command);
1541 _dbus_list_pop_first (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1543 _dbus_verbose ("Trying mechanism %s\n",
1544 auth->mech->mechanism);
1546 _dbus_string_free (&auth_command);
1552 process_rejected (DBusAuth *auth,
1553 const DBusString *command,
1554 const DBusString *args)
1556 shutdown_mech (auth);
1558 if (!auth->already_got_mechanisms)
1560 if (!record_mechanisms (auth, command, args))
1564 if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
1566 client_try_next_mechanism (auth);
1571 auth->need_disconnect = TRUE;
1578 process_ok (DBusAuth *auth,
1579 const DBusString *command,
1580 const DBusString *args)
1582 if (!_dbus_string_append (&auth->outgoing,
1586 auth->authenticated_pending_output = TRUE;
1592 process_data_client (DBusAuth *auth,
1593 const DBusString *command,
1594 const DBusString *args)
1596 if (auth->mech != NULL)
1600 if (!_dbus_string_init (&decoded))
1603 if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
1605 _dbus_string_free (&decoded);
1609 #ifdef DBUS_ENABLE_VERBOSE_MODE
1610 if (_dbus_string_validate_ascii (&decoded, 0,
1611 _dbus_string_get_length (&decoded)))
1613 _dbus_verbose ("data: '%s'\n",
1614 _dbus_string_get_const_data (&decoded));
1618 if (!(* auth->mech->client_data_func) (auth, &decoded))
1620 _dbus_string_free (&decoded);
1624 _dbus_string_free (&decoded);
1628 if (!_dbus_string_append (&auth->outgoing,
1629 "ERROR \"Got DATA when not in an auth exchange\"\r\n"))
1637 process_error_client (DBusAuth *auth,
1638 const DBusString *command,
1639 const DBusString *args)
1645 process_unknown (DBusAuth *auth,
1646 const DBusString *command,
1647 const DBusString *args)
1649 if (!_dbus_string_append (&auth->outgoing,
1650 "ERROR \"Unknown command\"\r\n"))
1656 /* returns whether to call it again right away */
1658 process_command (DBusAuth *auth)
1666 /* _dbus_verbose (" trying process_command()\n"); */
1671 if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
1674 if (!_dbus_string_init (&command))
1676 auth->needed_memory = TRUE;
1680 if (!_dbus_string_init (&args))
1682 _dbus_string_free (&command);
1683 auth->needed_memory = TRUE;
1687 if (eol > _DBUS_ONE_MEGABYTE)
1689 /* This is a giant line, someone is trying to hose us. */
1690 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command too long\"\r\n"))
1696 if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0))
1699 if (!_dbus_string_validate_ascii (&command, 0,
1700 _dbus_string_get_length (&command)))
1702 _dbus_verbose ("Command contained non-ASCII chars or embedded nul\n");
1703 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command contained non-ASCII\"\r\n"))
1709 _dbus_verbose ("got command \"%s\"\n", _dbus_string_get_const_data (&command));
1711 _dbus_string_find_blank (&command, 0, &i);
1712 _dbus_string_skip_blank (&command, i, &j);
1715 _dbus_string_delete (&command, i, j - i);
1717 if (!_dbus_string_move (&command, i, &args, 0))
1721 while (auth->handlers[i].command != NULL)
1723 if (_dbus_string_equal_c_str (&command,
1724 auth->handlers[i].command))
1726 _dbus_verbose ("Processing auth command %s\n",
1727 auth->handlers[i].command);
1729 if (!(* auth->handlers[i].func) (auth, &command, &args))
1737 if (auth->handlers[i].command == NULL)
1739 if (!process_unknown (auth, &command, &args))
1745 /* We've succeeded in processing the whole command so drop it out
1746 * of the incoming buffer and return TRUE to try another command.
1749 _dbus_string_delete (&auth->incoming, 0, eol);
1752 _dbus_string_delete (&auth->incoming, 0, 2);
1757 _dbus_string_free (&args);
1758 _dbus_string_free (&command);
1761 auth->needed_memory = TRUE;
1763 auth->needed_memory = FALSE;
1772 * @addtogroup DBusAuth
1777 * Creates a new auth conversation object for the server side.
1778 * See doc/dbus-sasl-profile.txt for full details on what
1781 * @returns the new object or #NULL if no memory
1784 _dbus_auth_server_new (void)
1787 DBusAuthServer *server_auth;
1789 auth = _dbus_auth_new (sizeof (DBusAuthServer));
1793 auth->handlers = server_handlers;
1795 server_auth = DBUS_AUTH_SERVER (auth);
1797 /* perhaps this should be per-mechanism with a lower
1800 server_auth->failures = 0;
1801 server_auth->max_failures = 6;
1807 * Creates a new auth conversation object for the client side.
1808 * See doc/dbus-sasl-profile.txt for full details on what
1811 * @returns the new object or #NULL if no memory
1814 _dbus_auth_client_new (void)
1818 auth = _dbus_auth_new (sizeof (DBusAuthClient));
1822 auth->handlers = client_handlers;
1824 /* Add a default mechanism to try */
1825 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1826 (void*) &all_mechanisms[0]))
1828 _dbus_auth_unref (auth);
1832 /* Now try the mechanism we just added */
1833 if (!client_try_next_mechanism (auth))
1835 _dbus_auth_unref (auth);
1843 * Increments the refcount of an auth object.
1845 * @param auth the auth conversation
1848 _dbus_auth_ref (DBusAuth *auth)
1850 _dbus_assert (auth != NULL);
1852 auth->refcount += 1;
1856 * Decrements the refcount of an auth object.
1858 * @param auth the auth conversation
1861 _dbus_auth_unref (DBusAuth *auth)
1863 _dbus_assert (auth != NULL);
1864 _dbus_assert (auth->refcount > 0);
1866 auth->refcount -= 1;
1867 if (auth->refcount == 0)
1869 shutdown_mech (auth);
1871 if (DBUS_AUTH_IS_CLIENT (auth))
1873 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1877 _dbus_keyring_unref (auth->keyring);
1879 _dbus_string_free (&auth->context);
1880 _dbus_string_free (&auth->challenge);
1881 _dbus_string_free (&auth->identity);
1882 _dbus_string_free (&auth->incoming);
1883 _dbus_string_free (&auth->outgoing);
1885 dbus_free_string_array (auth->allowed_mechs);
1892 * Sets an array of authentication mechanism names
1893 * that we are willing to use.
1895 * @param auth the auth conversation
1896 * @param mechanisms #NULL-terminated array of mechanism names
1897 * @returns #FALSE if no memory
1900 _dbus_auth_set_mechanisms (DBusAuth *auth,
1901 const char **mechanisms)
1905 if (mechanisms != NULL)
1907 copy = _dbus_dup_string_array (mechanisms);
1914 dbus_free_string_array (auth->allowed_mechs);
1916 auth->allowed_mechs = copy;
1922 * @param auth the auth conversation object
1923 * @returns #TRUE if we're in a final state
1925 #define DBUS_AUTH_IN_END_STATE(auth) ((auth)->need_disconnect || (auth)->authenticated)
1928 * Analyzes buffered input and moves the auth conversation forward,
1929 * returning the new state of the auth conversation.
1931 * @param auth the auth conversation
1932 * @returns the new state
1935 _dbus_auth_do_work (DBusAuth *auth)
1937 auth->needed_memory = FALSE;
1939 /* Max amount we'll buffer up before deciding someone's on crack */
1940 #define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE)
1944 if (DBUS_AUTH_IN_END_STATE (auth))
1947 if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
1948 _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
1950 auth->need_disconnect = TRUE;
1951 _dbus_verbose ("Disconnecting due to excessive data buffered in auth phase\n");
1955 if (auth->mech == NULL &&
1956 auth->already_got_mechanisms &&
1957 DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
1959 auth->need_disconnect = TRUE;
1960 _dbus_verbose ("Disconnecting because we are out of mechanisms to try using\n");
1964 while (process_command (auth));
1966 if (DBUS_AUTH_IS_SERVER (auth) &&
1967 DBUS_AUTH_SERVER (auth)->failures >=
1968 DBUS_AUTH_SERVER (auth)->max_failures)
1969 auth->need_disconnect = TRUE;
1971 if (auth->need_disconnect)
1972 return DBUS_AUTH_STATE_NEED_DISCONNECT;
1973 else if (auth->authenticated)
1975 if (_dbus_string_get_length (&auth->incoming) > 0)
1976 return DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES;
1978 return DBUS_AUTH_STATE_AUTHENTICATED;
1980 else if (auth->needed_memory)
1981 return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
1982 else if (_dbus_string_get_length (&auth->outgoing) > 0)
1983 return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
1985 return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
1989 * Gets bytes that need to be sent to the peer we're conversing with.
1990 * After writing some bytes, _dbus_auth_bytes_sent() must be called
1991 * to notify the auth object that they were written.
1993 * @param auth the auth conversation
1994 * @param str return location for a ref to the buffer to send
1995 * @returns #FALSE if nothing to send
1998 _dbus_auth_get_bytes_to_send (DBusAuth *auth,
1999 const DBusString **str)
2001 _dbus_assert (auth != NULL);
2002 _dbus_assert (str != NULL);
2006 if (DBUS_AUTH_IN_END_STATE (auth))
2009 if (_dbus_string_get_length (&auth->outgoing) == 0)
2012 *str = &auth->outgoing;
2018 * Notifies the auth conversation object that
2019 * the given number of bytes of the outgoing buffer
2020 * have been written out.
2022 * @param auth the auth conversation
2023 * @param bytes_sent number of bytes written out
2026 _dbus_auth_bytes_sent (DBusAuth *auth,
2029 _dbus_verbose ("Sent %d bytes of: %s\n", bytes_sent,
2030 _dbus_string_get_const_data (&auth->outgoing));
2032 _dbus_string_delete (&auth->outgoing,
2035 if (auth->authenticated_pending_output &&
2036 _dbus_string_get_length (&auth->outgoing) == 0)
2037 auth->authenticated = TRUE;
2041 * Get a buffer to be used for reading bytes from the peer we're conversing
2042 * with. Bytes should be appended to this buffer.
2044 * @param auth the auth conversation
2045 * @param buffer return location for buffer to append bytes to
2048 _dbus_auth_get_buffer (DBusAuth *auth,
2049 DBusString **buffer)
2051 _dbus_assert (auth != NULL);
2052 _dbus_assert (!auth->buffer_outstanding);
2054 *buffer = &auth->incoming;
2056 auth->buffer_outstanding = TRUE;
2060 * Returns a buffer with new data read into it.
2062 * @param auth the auth conversation
2063 * @param buffer the buffer being returned
2064 * @param bytes_read number of new bytes added
2067 _dbus_auth_return_buffer (DBusAuth *auth,
2071 _dbus_assert (buffer == &auth->incoming);
2072 _dbus_assert (auth->buffer_outstanding);
2074 auth->buffer_outstanding = FALSE;
2078 * Returns leftover bytes that were not used as part of the auth
2079 * conversation. These bytes will be part of the message stream
2080 * instead. This function may not be called until authentication has
2083 * @param auth the auth conversation
2084 * @param str return location for pointer to string of unused bytes
2087 _dbus_auth_get_unused_bytes (DBusAuth *auth,
2088 const DBusString **str)
2090 if (!DBUS_AUTH_IN_END_STATE (auth))
2093 *str = &auth->incoming;
2098 * Gets rid of unused bytes returned by _dbus_auth_get_unused_bytes()
2099 * after we've gotten them and successfully moved them elsewhere.
2101 * @param auth the auth conversation
2104 _dbus_auth_delete_unused_bytes (DBusAuth *auth)
2106 if (!DBUS_AUTH_IN_END_STATE (auth))
2109 _dbus_string_set_length (&auth->incoming, 0);
2113 * Called post-authentication, indicates whether we need to encode
2114 * the message stream with _dbus_auth_encode_data() prior to
2115 * sending it to the peer.
2117 * @param auth the auth conversation
2118 * @returns #TRUE if we need to encode the stream
2121 _dbus_auth_needs_encoding (DBusAuth *auth)
2123 if (!auth->authenticated)
2126 if (auth->mech != NULL)
2128 if (DBUS_AUTH_IS_CLIENT (auth))
2129 return auth->mech->client_encode_func != NULL;
2131 return auth->mech->server_encode_func != NULL;
2138 * Called post-authentication, encodes a block of bytes for sending to
2139 * the peer. If no encoding was negotiated, just copies the bytes
2140 * (you can avoid this by checking _dbus_auth_needs_encoding()).
2142 * @param auth the auth conversation
2143 * @param plaintext the plain text data
2144 * @param encoded initialized string to where encoded data is appended
2145 * @returns #TRUE if we had enough memory and successfully encoded
2148 _dbus_auth_encode_data (DBusAuth *auth,
2149 const DBusString *plaintext,
2150 DBusString *encoded)
2152 _dbus_assert (plaintext != encoded);
2154 if (!auth->authenticated)
2157 if (_dbus_auth_needs_encoding (auth))
2159 if (DBUS_AUTH_IS_CLIENT (auth))
2160 return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
2162 return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
2166 return _dbus_string_copy (plaintext, 0, encoded,
2167 _dbus_string_get_length (encoded));
2172 * Called post-authentication, indicates whether we need to decode
2173 * the message stream with _dbus_auth_decode_data() after
2174 * receiving it from the peer.
2176 * @param auth the auth conversation
2177 * @returns #TRUE if we need to encode the stream
2180 _dbus_auth_needs_decoding (DBusAuth *auth)
2182 if (!auth->authenticated)
2185 if (auth->mech != NULL)
2187 if (DBUS_AUTH_IS_CLIENT (auth))
2188 return auth->mech->client_decode_func != NULL;
2190 return auth->mech->server_decode_func != NULL;
2198 * Called post-authentication, decodes a block of bytes received from
2199 * the peer. If no encoding was negotiated, just copies the bytes (you
2200 * can avoid this by checking _dbus_auth_needs_decoding()).
2202 * @todo We need to be able to distinguish "out of memory" error
2203 * from "the data is hosed" error.
2205 * @param auth the auth conversation
2206 * @param encoded the encoded data
2207 * @param plaintext initialized string where decoded data is appended
2208 * @returns #TRUE if we had enough memory and successfully decoded
2211 _dbus_auth_decode_data (DBusAuth *auth,
2212 const DBusString *encoded,
2213 DBusString *plaintext)
2215 _dbus_assert (plaintext != encoded);
2217 if (!auth->authenticated)
2220 if (_dbus_auth_needs_decoding (auth))
2222 if (DBUS_AUTH_IS_CLIENT (auth))
2223 return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
2225 return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
2229 return _dbus_string_copy (encoded, 0, plaintext,
2230 _dbus_string_get_length (plaintext));
2235 * Sets credentials received via reliable means from the operating
2238 * @param auth the auth conversation
2239 * @param credentials the credentials received
2242 _dbus_auth_set_credentials (DBusAuth *auth,
2243 const DBusCredentials *credentials)
2245 auth->credentials = *credentials;
2249 * Gets the identity we authorized the client as. Apps may have
2250 * different policies as to what identities they allow.
2252 * @param auth the auth conversation
2253 * @param credentials the credentials we've authorized
2256 _dbus_auth_get_identity (DBusAuth *auth,
2257 DBusCredentials *credentials)
2259 if (auth->authenticated)
2260 *credentials = auth->authorized_identity;
2262 _dbus_credentials_clear (credentials);
2266 * Sets the "authentication context" which scopes cookies
2267 * with the DBUS_COOKIE_SHA1 auth mechanism for example.
2269 * @param auth the auth conversation
2270 * @param context the context
2271 * @returns #FALSE if no memory
2274 _dbus_auth_set_context (DBusAuth *auth,
2275 const DBusString *context)
2277 return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context),
2278 &auth->context, 0, _dbus_string_get_length (context));
2283 #ifdef DBUS_BUILD_TESTS
2284 #include "dbus-test.h"
2285 #include "dbus-auth-script.h"
2289 process_test_subdir (const DBusString *test_base_dir,
2292 DBusString test_directory;
2293 DBusString filename;
2301 if (!_dbus_string_init (&test_directory))
2302 _dbus_assert_not_reached ("didn't allocate test_directory\n");
2304 _dbus_string_init_const (&filename, subdir);
2306 if (!_dbus_string_copy (test_base_dir, 0,
2307 &test_directory, 0))
2308 _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
2310 if (!_dbus_concat_dir_and_file (&test_directory, &filename))
2311 _dbus_assert_not_reached ("couldn't allocate full path");
2313 _dbus_string_free (&filename);
2314 if (!_dbus_string_init (&filename))
2315 _dbus_assert_not_reached ("didn't allocate filename string\n");
2317 dbus_error_init (&error);
2318 dir = _dbus_directory_open (&test_directory, &error);
2321 _dbus_warn ("Could not open %s: %s\n",
2322 _dbus_string_get_const_data (&test_directory),
2324 dbus_error_free (&error);
2328 printf ("Testing:\n");
2331 while (_dbus_directory_get_next_file (dir, &filename, &error))
2333 DBusString full_path;
2335 if (!_dbus_string_init (&full_path))
2336 _dbus_assert_not_reached ("couldn't init string");
2338 if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
2339 _dbus_assert_not_reached ("couldn't copy dir to full_path");
2341 if (!_dbus_concat_dir_and_file (&full_path, &filename))
2342 _dbus_assert_not_reached ("couldn't concat file to dir");
2344 if (!_dbus_string_ends_with_c_str (&filename, ".auth-script"))
2346 _dbus_verbose ("Skipping non-.auth-script file %s\n",
2347 _dbus_string_get_const_data (&filename));
2348 _dbus_string_free (&full_path);
2352 printf (" %s\n", _dbus_string_get_const_data (&filename));
2354 if (!_dbus_auth_script_run (&full_path))
2356 _dbus_string_free (&full_path);
2360 _dbus_string_free (&full_path);
2363 if (dbus_error_is_set (&error))
2365 _dbus_warn ("Could not get next file in %s: %s\n",
2366 _dbus_string_get_const_data (&test_directory), error.message);
2367 dbus_error_free (&error);
2376 _dbus_directory_close (dir);
2377 _dbus_string_free (&test_directory);
2378 _dbus_string_free (&filename);
2384 process_test_dirs (const char *test_data_dir)
2386 DBusString test_directory;
2391 _dbus_string_init_const (&test_directory, test_data_dir);
2393 if (!process_test_subdir (&test_directory, "auth"))
2400 _dbus_string_free (&test_directory);
2406 _dbus_auth_test (const char *test_data_dir)
2409 if (test_data_dir == NULL)
2412 if (!process_test_dirs (test_data_dir))
2418 #endif /* DBUS_BUILD_TESTS */