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 * The name of the auth ("client" or "server")
271 * @param auth the auth conversation
274 #define DBUS_AUTH_NAME(auth) (DBUS_AUTH_IS_SERVER(auth) ? "server" : "client")
277 _dbus_auth_new (int size)
281 auth = dbus_malloc0 (size);
287 _dbus_credentials_clear (&auth->credentials);
288 _dbus_credentials_clear (&auth->authorized_identity);
289 _dbus_credentials_clear (&auth->desired_identity);
291 auth->keyring = NULL;
292 auth->cookie_id = -1;
294 /* note that we don't use the max string length feature,
295 * because you can't use that feature if you're going to
296 * try to recover from out-of-memory (it creates
297 * what looks like unrecoverable inability to alloc
298 * more space in the string). But we do handle
299 * overlong buffers in _dbus_auth_do_work().
302 if (!_dbus_string_init (&auth->incoming))
305 if (!_dbus_string_init (&auth->outgoing))
308 if (!_dbus_string_init (&auth->identity))
311 if (!_dbus_string_init (&auth->context))
314 if (!_dbus_string_init (&auth->challenge))
317 /* default context if none is specified */
318 if (!_dbus_string_append (&auth->context, "org_freedesktop_general"))
324 _dbus_string_free (&auth->challenge);
326 _dbus_string_free (&auth->context);
328 _dbus_string_free (&auth->identity);
330 _dbus_string_free (&auth->outgoing);
332 _dbus_string_free (&auth->incoming);
339 shutdown_mech (DBusAuth *auth)
341 /* Cancel any auth */
342 auth->authenticated_pending_begin = FALSE;
343 auth->authenticated = FALSE;
344 auth->already_asked_for_initial_response = FALSE;
345 _dbus_string_set_length (&auth->identity, 0);
347 _dbus_credentials_clear (&auth->authorized_identity);
348 _dbus_credentials_clear (&auth->desired_identity);
350 if (auth->mech != NULL)
352 _dbus_verbose ("%s: Shutting down mechanism %s\n",
353 DBUS_AUTH_NAME (auth), auth->mech->mechanism);
355 if (DBUS_AUTH_IS_CLIENT (auth))
356 (* auth->mech->client_shutdown_func) (auth);
358 (* auth->mech->server_shutdown_func) (auth);
364 /* Returns TRUE but with an empty string hash if the
365 * cookie_id isn't known. As with all this code
366 * TRUE just means we had enough memory.
369 sha1_compute_hash (DBusAuth *auth,
371 const DBusString *server_challenge,
372 const DBusString *client_challenge,
379 _dbus_assert (auth->keyring != NULL);
383 if (!_dbus_string_init (&cookie))
386 if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id,
390 if (_dbus_string_get_length (&cookie) == 0)
396 if (!_dbus_string_init (&to_hash))
399 if (!_dbus_string_copy (server_challenge, 0,
400 &to_hash, _dbus_string_get_length (&to_hash)))
403 if (!_dbus_string_append (&to_hash, ":"))
406 if (!_dbus_string_copy (client_challenge, 0,
407 &to_hash, _dbus_string_get_length (&to_hash)))
410 if (!_dbus_string_append (&to_hash, ":"))
413 if (!_dbus_string_copy (&cookie, 0,
414 &to_hash, _dbus_string_get_length (&to_hash)))
417 if (!_dbus_sha_compute (&to_hash, hash))
423 _dbus_string_zero (&to_hash);
424 _dbus_string_free (&to_hash);
426 _dbus_string_zero (&cookie);
427 _dbus_string_free (&cookie);
431 /** http://www.ietf.org/rfc/rfc2831.txt suggests at least 64 bits of
432 * entropy, we use 128. This is the number of bytes in the random
435 #define N_CHALLENGE_BYTES (128/8)
438 sha1_handle_first_client_response (DBusAuth *auth,
439 const DBusString *data)
441 /* We haven't sent a challenge yet, we're expecting a desired
442 * username from the client.
452 _dbus_string_set_length (&auth->challenge, 0);
454 if (_dbus_string_get_length (data) > 0)
456 if (_dbus_string_get_length (&auth->identity) > 0)
458 /* Tried to send two auth identities, wtf */
459 _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
460 DBUS_AUTH_NAME (auth));
461 return send_rejected (auth);
465 /* this is our auth identity */
466 if (!_dbus_string_copy (data, 0, &auth->identity, 0))
471 if (!_dbus_credentials_from_username (data, &auth->desired_identity))
473 _dbus_verbose ("%s: Did not get a valid username from client\n",
474 DBUS_AUTH_NAME (auth));
475 return send_rejected (auth);
478 if (!_dbus_string_init (&tmp))
481 if (!_dbus_string_init (&tmp2))
483 _dbus_string_free (&tmp);
487 old_len = _dbus_string_get_length (&auth->outgoing);
489 /* we cache the keyring for speed, so here we drop it if it's the
490 * wrong one. FIXME caching the keyring here is useless since we use
491 * a different DBusAuth for every connection.
494 !_dbus_keyring_is_for_user (auth->keyring,
497 _dbus_keyring_unref (auth->keyring);
498 auth->keyring = NULL;
501 if (auth->keyring == NULL)
505 dbus_error_init (&error);
506 auth->keyring = _dbus_keyring_new_homedir (data,
510 if (auth->keyring == NULL)
512 if (dbus_error_has_name (&error,
513 DBUS_ERROR_NO_MEMORY))
515 dbus_error_free (&error);
520 _DBUS_ASSERT_ERROR_IS_SET (&error);
521 _dbus_verbose ("%s: Error loading keyring: %s\n",
522 DBUS_AUTH_NAME (auth), error.message);
523 if (send_rejected (auth))
524 retval = TRUE; /* retval is only about mem */
525 dbus_error_free (&error);
531 _dbus_assert (!dbus_error_is_set (&error));
535 _dbus_assert (auth->keyring != NULL);
537 dbus_error_init (&error);
538 auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error);
539 if (auth->cookie_id < 0)
541 _DBUS_ASSERT_ERROR_IS_SET (&error);
542 _dbus_verbose ("%s: Could not get a cookie ID to send to client: %s\n",
543 DBUS_AUTH_NAME (auth), error.message);
544 if (send_rejected (auth))
546 dbus_error_free (&error);
551 _dbus_assert (!dbus_error_is_set (&error));
554 if (!_dbus_string_copy (&auth->context, 0,
555 &tmp2, _dbus_string_get_length (&tmp2)))
558 if (!_dbus_string_append (&tmp2, " "))
561 if (!_dbus_string_append_int (&tmp2, auth->cookie_id))
564 if (!_dbus_string_append (&tmp2, " "))
567 if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
570 _dbus_string_set_length (&auth->challenge, 0);
571 if (!_dbus_string_hex_encode (&tmp, 0, &auth->challenge, 0))
574 if (!_dbus_string_hex_encode (&tmp, 0, &tmp2,
575 _dbus_string_get_length (&tmp2)))
578 if (!_dbus_string_append (&auth->outgoing,
582 if (!_dbus_string_base64_encode (&tmp2, 0, &auth->outgoing,
583 _dbus_string_get_length (&auth->outgoing)))
586 if (!_dbus_string_append (&auth->outgoing,
593 _dbus_string_zero (&tmp);
594 _dbus_string_free (&tmp);
595 _dbus_string_zero (&tmp2);
596 _dbus_string_free (&tmp2);
598 _dbus_string_set_length (&auth->outgoing, old_len);
603 sha1_handle_second_client_response (DBusAuth *auth,
604 const DBusString *data)
606 /* We are expecting a response which is the hex-encoded client
607 * challenge, space, then SHA-1 hash of the concatenation of our
608 * challenge, ":", client challenge, ":", secret key, all
612 DBusString client_challenge;
613 DBusString client_hash;
615 DBusString correct_hash;
619 if (!_dbus_string_find_blank (data, 0, &i))
621 _dbus_verbose ("%s: no space separator in client response\n",
622 DBUS_AUTH_NAME (auth));
623 return send_rejected (auth);
626 if (!_dbus_string_init (&client_challenge))
629 if (!_dbus_string_init (&client_hash))
632 if (!_dbus_string_copy_len (data, 0, i, &client_challenge,
636 _dbus_string_skip_blank (data, i, &i);
638 if (!_dbus_string_copy_len (data, i,
639 _dbus_string_get_length (data) - i,
644 if (_dbus_string_get_length (&client_challenge) == 0 ||
645 _dbus_string_get_length (&client_hash) == 0)
647 _dbus_verbose ("%s: zero-length client challenge or hash\n",
648 DBUS_AUTH_NAME (auth));
649 if (send_rejected (auth))
654 if (!_dbus_string_init (&correct_hash))
657 if (!sha1_compute_hash (auth, auth->cookie_id,
663 /* if cookie_id was invalid, then we get an empty hash */
664 if (_dbus_string_get_length (&correct_hash) == 0)
666 if (send_rejected (auth))
671 if (!_dbus_string_equal (&client_hash, &correct_hash))
673 if (send_rejected (auth))
678 if (!_dbus_string_append (&auth->outgoing,
682 _dbus_verbose ("%s: authenticated client with UID "DBUS_UID_FORMAT" using DBUS_COOKIE_SHA1\n",
683 DBUS_AUTH_NAME (auth), auth->desired_identity.uid);
685 auth->authorized_identity = auth->desired_identity;
686 auth->authenticated_pending_begin = TRUE;
690 _dbus_string_zero (&correct_hash);
691 _dbus_string_free (&correct_hash);
693 _dbus_string_zero (&client_hash);
694 _dbus_string_free (&client_hash);
696 _dbus_string_free (&client_challenge);
702 handle_server_data_cookie_sha1_mech (DBusAuth *auth,
703 const DBusString *data)
705 if (auth->cookie_id < 0)
706 return sha1_handle_first_client_response (auth, data);
708 return sha1_handle_second_client_response (auth, data);
712 handle_server_shutdown_cookie_sha1_mech (DBusAuth *auth)
714 auth->cookie_id = -1;
715 _dbus_string_set_length (&auth->challenge, 0);
719 handle_client_initial_response_cookie_sha1_mech (DBusAuth *auth,
720 DBusString *response)
722 const DBusString *username;
727 if (!_dbus_username_from_current_process (&username))
730 if (!_dbus_string_base64_encode (username, 0,
732 _dbus_string_get_length (response)))
742 handle_client_data_cookie_sha1_mech (DBusAuth *auth,
743 const DBusString *data)
745 /* The data we get from the server should be the cookie context
746 * name, the cookie ID, and the server challenge, separated by
747 * spaces. We send back our challenge string and the correct hash.
751 DBusString cookie_id_str;
752 DBusString server_challenge;
753 DBusString client_challenge;
754 DBusString correct_hash;
762 if (!_dbus_string_find_blank (data, 0, &i))
764 if (_dbus_string_append (&auth->outgoing,
765 "ERROR \"Server did not send context/ID/challenge properly\"\r\n"))
770 if (!_dbus_string_init (&context))
773 if (!_dbus_string_copy_len (data, 0, i,
777 _dbus_string_skip_blank (data, i, &i);
778 if (!_dbus_string_find_blank (data, i, &j))
780 if (_dbus_string_append (&auth->outgoing,
781 "ERROR \"Server did not send context/ID/challenge properly\"\r\n"))
786 if (!_dbus_string_init (&cookie_id_str))
789 if (!_dbus_string_copy_len (data, i, j - i,
793 if (!_dbus_string_init (&server_challenge))
797 _dbus_string_skip_blank (data, i, &i);
798 j = _dbus_string_get_length (data);
800 if (!_dbus_string_copy_len (data, i, j - i,
801 &server_challenge, 0))
804 if (!_dbus_keyring_validate_context (&context))
806 if (_dbus_string_append (&auth->outgoing,
807 "ERROR \"Server sent invalid cookie context\"\r\n"))
812 if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL))
814 if (_dbus_string_append (&auth->outgoing,
815 "ERROR \"Could not parse cookie ID as an integer\"\r\n"))
820 if (_dbus_string_get_length (&server_challenge) == 0)
822 if (_dbus_string_append (&auth->outgoing,
823 "ERROR \"Empty server challenge string\"\r\n"))
828 if (auth->keyring == NULL)
832 dbus_error_init (&error);
833 auth->keyring = _dbus_keyring_new_homedir (NULL,
837 if (auth->keyring == NULL)
839 if (dbus_error_has_name (&error,
840 DBUS_ERROR_NO_MEMORY))
842 dbus_error_free (&error);
847 _DBUS_ASSERT_ERROR_IS_SET (&error);
849 _dbus_verbose ("%s: Error loading keyring: %s\n",
850 DBUS_AUTH_NAME (auth), error.message);
852 if (_dbus_string_append (&auth->outgoing,
853 "ERROR \"Could not load cookie file\"\r\n"))
854 retval = TRUE; /* retval is only about mem */
856 dbus_error_free (&error);
862 _dbus_assert (!dbus_error_is_set (&error));
866 _dbus_assert (auth->keyring != NULL);
868 if (!_dbus_string_init (&tmp))
871 if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
874 if (!_dbus_string_init (&client_challenge))
877 if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0))
880 if (!_dbus_string_init (&correct_hash))
883 if (!sha1_compute_hash (auth, val,
889 if (_dbus_string_get_length (&correct_hash) == 0)
891 /* couldn't find the cookie ID or something */
892 if (_dbus_string_append (&auth->outgoing,
893 "ERROR \"Don't have the requested cookie ID\"\r\n"))
898 _dbus_string_set_length (&tmp, 0);
900 if (!_dbus_string_copy (&client_challenge, 0, &tmp,
901 _dbus_string_get_length (&tmp)))
904 if (!_dbus_string_append (&tmp, " "))
907 if (!_dbus_string_copy (&correct_hash, 0, &tmp,
908 _dbus_string_get_length (&tmp)))
911 old_len = _dbus_string_get_length (&auth->outgoing);
912 if (!_dbus_string_append (&auth->outgoing, "DATA "))
915 if (!_dbus_string_base64_encode (&tmp, 0,
917 _dbus_string_get_length (&auth->outgoing)))
919 _dbus_string_set_length (&auth->outgoing, old_len);
923 if (!_dbus_string_append (&auth->outgoing, "\r\n"))
925 _dbus_string_set_length (&auth->outgoing, old_len);
932 _dbus_string_zero (&correct_hash);
933 _dbus_string_free (&correct_hash);
935 _dbus_string_free (&client_challenge);
937 _dbus_string_zero (&tmp);
938 _dbus_string_free (&tmp);
940 _dbus_string_free (&server_challenge);
942 _dbus_string_free (&cookie_id_str);
944 _dbus_string_free (&context);
950 handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth)
952 auth->cookie_id = -1;
953 _dbus_string_set_length (&auth->challenge, 0);
957 handle_server_data_external_mech (DBusAuth *auth,
958 const DBusString *data)
960 if (auth->credentials.uid == DBUS_UID_UNSET)
962 _dbus_verbose ("%s: no credentials, mechanism EXTERNAL can't authenticate\n",
963 DBUS_AUTH_NAME (auth));
964 return send_rejected (auth);
967 if (_dbus_string_get_length (data) > 0)
969 if (_dbus_string_get_length (&auth->identity) > 0)
971 /* Tried to send two auth identities, wtf */
972 _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
973 DBUS_AUTH_NAME (auth));
974 return send_rejected (auth);
978 /* this is our auth identity */
979 if (!_dbus_string_copy (data, 0, &auth->identity, 0))
984 /* Poke client for an auth identity, if none given */
985 if (_dbus_string_get_length (&auth->identity) == 0 &&
986 !auth->already_asked_for_initial_response)
988 if (_dbus_string_append (&auth->outgoing,
991 _dbus_verbose ("%s: sending empty challenge asking client for auth identity\n",
992 DBUS_AUTH_NAME (auth));
993 auth->already_asked_for_initial_response = TRUE;
1000 _dbus_credentials_clear (&auth->desired_identity);
1002 /* If auth->identity is still empty here, then client
1003 * responded with an empty string after we poked it for
1004 * an initial response. This means to try to auth the
1005 * identity provided in the credentials.
1007 if (_dbus_string_get_length (&auth->identity) == 0)
1009 auth->desired_identity.uid = auth->credentials.uid;
1013 if (!_dbus_uid_from_string (&auth->identity,
1014 &auth->desired_identity.uid))
1016 _dbus_verbose ("%s: could not get credentials from uid string\n",
1017 DBUS_AUTH_NAME (auth));
1018 return send_rejected (auth);
1022 if (auth->desired_identity.uid == DBUS_UID_UNSET)
1024 _dbus_verbose ("%s: desired user %s is no good\n",
1025 DBUS_AUTH_NAME (auth),
1026 _dbus_string_get_const_data (&auth->identity));
1027 return send_rejected (auth);
1030 if (_dbus_credentials_match (&auth->desired_identity,
1031 &auth->credentials))
1033 /* client has authenticated */
1034 if (!_dbus_string_append (&auth->outgoing,
1038 _dbus_verbose ("%s: authenticated client with UID "DBUS_UID_FORMAT
1039 " matching socket credentials UID "DBUS_UID_FORMAT"\n",
1040 DBUS_AUTH_NAME (auth),
1041 auth->desired_identity.uid,
1042 auth->credentials.uid);
1044 auth->authorized_identity.uid = auth->desired_identity.uid;
1046 auth->authenticated_pending_begin = TRUE;
1052 _dbus_verbose ("%s: credentials uid="DBUS_UID_FORMAT
1053 " gid="DBUS_GID_FORMAT
1054 " do not allow uid="DBUS_UID_FORMAT
1055 " gid="DBUS_GID_FORMAT"\n",
1056 DBUS_AUTH_NAME (auth),
1057 auth->credentials.uid, auth->credentials.gid,
1058 auth->desired_identity.uid, auth->desired_identity.gid);
1059 return send_rejected (auth);
1064 handle_server_shutdown_external_mech (DBusAuth *auth)
1070 handle_client_initial_response_external_mech (DBusAuth *auth,
1071 DBusString *response)
1073 /* We always append our UID as an initial response, so the server
1074 * doesn't have to send back an empty challenge to check whether we
1075 * want to specify an identity. i.e. this avoids a round trip that
1076 * the spec for the EXTERNAL mechanism otherwise requires.
1078 DBusString plaintext;
1080 if (!_dbus_string_init (&plaintext))
1083 if (!_dbus_string_append_uint (&plaintext,
1087 if (!_dbus_string_base64_encode (&plaintext, 0,
1089 _dbus_string_get_length (response)))
1092 _dbus_string_free (&plaintext);
1097 _dbus_string_free (&plaintext);
1102 handle_client_data_external_mech (DBusAuth *auth,
1103 const DBusString *data)
1110 handle_client_shutdown_external_mech (DBusAuth *auth)
1115 /* Put mechanisms here in order of preference.
1116 * What I eventually want to have is:
1118 * - a mechanism that checks UNIX domain socket credentials
1119 * - a simple magic cookie mechanism like X11 or ICE
1120 * - mechanisms that chain to Cyrus SASL, so we can use anything it
1121 * offers such as Kerberos, X509, whatever.
1124 static const DBusAuthMechanismHandler
1125 all_mechanisms[] = {
1127 handle_server_data_external_mech,
1129 handle_server_shutdown_external_mech,
1130 handle_client_initial_response_external_mech,
1131 handle_client_data_external_mech,
1133 handle_client_shutdown_external_mech },
1134 { "DBUS_COOKIE_SHA1",
1135 handle_server_data_cookie_sha1_mech,
1137 handle_server_shutdown_cookie_sha1_mech,
1138 handle_client_initial_response_cookie_sha1_mech,
1139 handle_client_data_cookie_sha1_mech,
1141 handle_client_shutdown_cookie_sha1_mech },
1145 static const DBusAuthMechanismHandler*
1146 find_mech (const DBusString *name,
1147 char **allowed_mechs)
1151 if (allowed_mechs != NULL &&
1152 !_dbus_string_array_contains ((const char**) allowed_mechs,
1153 _dbus_string_get_const_data (name)))
1157 while (all_mechanisms[i].mechanism != NULL)
1159 if (_dbus_string_equal_c_str (name,
1160 all_mechanisms[i].mechanism))
1162 return &all_mechanisms[i];
1171 send_rejected (DBusAuth *auth)
1174 DBusAuthServer *server_auth;
1177 if (!_dbus_string_init (&command))
1180 if (!_dbus_string_append (&command,
1185 while (all_mechanisms[i].mechanism != NULL)
1187 if (!_dbus_string_append (&command,
1191 if (!_dbus_string_append (&command,
1192 all_mechanisms[i].mechanism))
1198 if (!_dbus_string_append (&command, "\r\n"))
1201 if (!_dbus_string_copy (&command, 0, &auth->outgoing,
1202 _dbus_string_get_length (&auth->outgoing)))
1205 shutdown_mech (auth);
1207 _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
1208 server_auth = DBUS_AUTH_SERVER (auth);
1209 server_auth->failures += 1;
1211 _dbus_string_free (&command);
1216 _dbus_string_free (&command);
1221 process_auth (DBusAuth *auth,
1222 const DBusString *command,
1223 const DBusString *args)
1227 /* We are already using a mechanism, client is on crack */
1228 if (!_dbus_string_append (&auth->outgoing,
1229 "ERROR \"Sent AUTH while another AUTH in progress\"\r\n"))
1234 else if (_dbus_string_get_length (args) == 0)
1236 /* No args to the auth, send mechanisms */
1237 if (!send_rejected (auth))
1246 DBusString base64_response;
1247 DBusString decoded_response;
1249 _dbus_string_find_blank (args, 0, &i);
1251 if (!_dbus_string_init (&mech))
1254 if (!_dbus_string_init (&base64_response))
1256 _dbus_string_free (&mech);
1260 if (!_dbus_string_init (&decoded_response))
1262 _dbus_string_free (&mech);
1263 _dbus_string_free (&base64_response);
1267 if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
1270 if (!_dbus_string_copy (args, i, &base64_response, 0))
1273 if (!_dbus_string_base64_decode (&base64_response, 0,
1274 &decoded_response, 0))
1277 auth->mech = find_mech (&mech, auth->allowed_mechs);
1278 if (auth->mech != NULL)
1280 _dbus_verbose ("%s: Trying mechanism %s with initial response of %d bytes\n",
1281 DBUS_AUTH_NAME (auth),
1282 auth->mech->mechanism,
1283 _dbus_string_get_length (&decoded_response));
1285 if (!(* auth->mech->server_data_func) (auth,
1291 /* Unsupported mechanism */
1292 if (!send_rejected (auth))
1296 _dbus_string_free (&mech);
1297 _dbus_string_free (&base64_response);
1298 _dbus_string_free (&decoded_response);
1304 _dbus_string_free (&mech);
1305 _dbus_string_free (&base64_response);
1306 _dbus_string_free (&decoded_response);
1312 process_cancel (DBusAuth *auth,
1313 const DBusString *command,
1314 const DBusString *args)
1316 if (!send_rejected (auth))
1323 process_begin (DBusAuth *auth,
1324 const DBusString *command,
1325 const DBusString *args)
1327 if (auth->authenticated_pending_begin)
1328 auth->authenticated = TRUE;
1331 auth->need_disconnect = TRUE; /* client trying to send data before auth,
1334 shutdown_mech (auth);
1341 process_data_server (DBusAuth *auth,
1342 const DBusString *command,
1343 const DBusString *args)
1345 if (auth->mech != NULL)
1349 if (!_dbus_string_init (&decoded))
1352 if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
1354 _dbus_string_free (&decoded);
1358 #ifdef DBUS_ENABLE_VERBOSE_MODE
1359 if (_dbus_string_validate_ascii (&decoded, 0,
1360 _dbus_string_get_length (&decoded)))
1361 _dbus_verbose ("%s: data: '%s'\n",
1362 DBUS_AUTH_NAME (auth),
1363 _dbus_string_get_const_data (&decoded));
1366 if (!(* auth->mech->server_data_func) (auth, &decoded))
1368 _dbus_string_free (&decoded);
1372 _dbus_string_free (&decoded);
1376 if (!_dbus_string_append (&auth->outgoing,
1377 "ERROR \"Not currently in an auth conversation\"\r\n"))
1385 process_error_server (DBusAuth *auth,
1386 const DBusString *command,
1387 const DBusString *args)
1389 /* Server got error from client, reject the auth,
1390 * as we don't have anything more intelligent to do.
1392 if (!send_rejected (auth))
1398 /* return FALSE if no memory, TRUE if all OK */
1400 get_word (const DBusString *str,
1406 _dbus_string_skip_blank (str, *start, start);
1407 _dbus_string_find_blank (str, *start, &i);
1411 if (!_dbus_string_copy_len (str, *start, i - *start, word, 0))
1421 record_mechanisms (DBusAuth *auth,
1422 const DBusString *command,
1423 const DBusString *args)
1428 if (auth->already_got_mechanisms)
1431 len = _dbus_string_get_length (args);
1437 const DBusAuthMechanismHandler *mech;
1439 if (!_dbus_string_init (&m))
1442 if (!get_word (args, &next, &m))
1444 _dbus_string_free (&m);
1448 mech = find_mech (&m, auth->allowed_mechs);
1452 /* FIXME right now we try mechanisms in the order
1453 * the server lists them; should we do them in
1454 * some more deterministic order?
1456 * Probably in all_mechanisms order, our order of
1457 * preference. Of course when the server is us,
1458 * it lists things in that order anyhow.
1461 _dbus_verbose ("%s: Adding mechanism %s to list we will try\n",
1462 DBUS_AUTH_NAME (auth), mech->mechanism);
1464 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1467 _dbus_string_free (&m);
1473 _dbus_verbose ("%s: Server offered mechanism \"%s\" that we don't know how to use\n",
1474 DBUS_AUTH_NAME (auth),
1475 _dbus_string_get_const_data (&m));
1478 _dbus_string_free (&m);
1481 auth->already_got_mechanisms = TRUE;
1486 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1492 client_try_next_mechanism (DBusAuth *auth)
1494 const DBusAuthMechanismHandler *mech;
1495 DBusString auth_command;
1496 DBusAuthClient *client;
1498 client = DBUS_AUTH_CLIENT (auth);
1500 /* Pop any mechs not in the list of allowed mechanisms */
1502 while (client->mechs_to_try != NULL)
1504 mech = client->mechs_to_try->data;
1506 if (auth->allowed_mechs != NULL &&
1507 !_dbus_string_array_contains ((const char**) auth->allowed_mechs,
1510 /* don't try this one after all */
1511 _dbus_verbose ("%s: Mechanism %s isn't in the list of allowed mechanisms\n",
1512 DBUS_AUTH_NAME (auth), mech->mechanism);
1514 _dbus_list_pop_first (& client->mechs_to_try);
1517 break; /* we'll try this one */
1523 if (!_dbus_string_init (&auth_command))
1526 if (!_dbus_string_append (&auth_command,
1529 _dbus_string_free (&auth_command);
1533 if (!_dbus_string_append (&auth_command,
1536 _dbus_string_free (&auth_command);
1540 if (mech->client_initial_response_func != NULL)
1542 if (!_dbus_string_append (&auth_command, " "))
1544 _dbus_string_free (&auth_command);
1548 if (!(* mech->client_initial_response_func) (auth, &auth_command))
1550 _dbus_string_free (&auth_command);
1555 if (!_dbus_string_append (&auth_command,
1558 _dbus_string_free (&auth_command);
1562 if (!_dbus_string_copy (&auth_command, 0,
1564 _dbus_string_get_length (&auth->outgoing)))
1566 _dbus_string_free (&auth_command);
1571 _dbus_list_pop_first (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1573 _dbus_verbose ("%s: Trying mechanism %s\n",
1574 DBUS_AUTH_NAME (auth),
1575 auth->mech->mechanism);
1577 _dbus_string_free (&auth_command);
1583 process_rejected (DBusAuth *auth,
1584 const DBusString *command,
1585 const DBusString *args)
1587 shutdown_mech (auth);
1589 if (!auth->already_got_mechanisms)
1591 if (!record_mechanisms (auth, command, args))
1595 if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
1597 if (!client_try_next_mechanism (auth))
1603 auth->need_disconnect = TRUE;
1610 process_ok (DBusAuth *auth,
1611 const DBusString *command,
1612 const DBusString *args)
1614 if (!_dbus_string_append (&auth->outgoing,
1618 auth->authenticated_pending_output = TRUE;
1624 process_data_client (DBusAuth *auth,
1625 const DBusString *command,
1626 const DBusString *args)
1628 if (auth->mech != NULL)
1632 if (!_dbus_string_init (&decoded))
1635 if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
1637 _dbus_string_free (&decoded);
1641 #ifdef DBUS_ENABLE_VERBOSE_MODE
1642 if (_dbus_string_validate_ascii (&decoded, 0,
1643 _dbus_string_get_length (&decoded)))
1645 _dbus_verbose ("%s: data: '%s'\n",
1646 DBUS_AUTH_NAME (auth),
1647 _dbus_string_get_const_data (&decoded));
1651 if (!(* auth->mech->client_data_func) (auth, &decoded))
1653 _dbus_string_free (&decoded);
1657 _dbus_string_free (&decoded);
1661 if (!_dbus_string_append (&auth->outgoing,
1662 "ERROR \"Got DATA when not in an auth exchange\"\r\n"))
1670 process_error_client (DBusAuth *auth,
1671 const DBusString *command,
1672 const DBusString *args)
1674 /* Cancel current mechanism, as we don't have anything
1675 * more clever to do.
1677 if (!_dbus_string_append (&auth->outgoing,
1685 process_unknown (DBusAuth *auth,
1686 const DBusString *command,
1687 const DBusString *args)
1689 if (!_dbus_string_append (&auth->outgoing,
1690 "ERROR \"Unknown command\"\r\n"))
1696 /* returns whether to call it again right away */
1698 process_command (DBusAuth *auth)
1706 /* _dbus_verbose ("%s: trying process_command()\n"); */
1711 if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
1714 if (!_dbus_string_init (&command))
1716 auth->needed_memory = TRUE;
1720 if (!_dbus_string_init (&args))
1722 _dbus_string_free (&command);
1723 auth->needed_memory = TRUE;
1727 if (eol > _DBUS_ONE_MEGABYTE)
1729 /* This is a giant line, someone is trying to hose us. */
1730 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command too long\"\r\n"))
1736 if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0))
1739 if (!_dbus_string_validate_ascii (&command, 0,
1740 _dbus_string_get_length (&command)))
1742 _dbus_verbose ("%s: Command contained non-ASCII chars or embedded nul\n",
1743 DBUS_AUTH_NAME (auth));
1744 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command contained non-ASCII\"\r\n"))
1750 _dbus_verbose ("%s: got command \"%s\"\n",
1751 DBUS_AUTH_NAME (auth),
1752 _dbus_string_get_const_data (&command));
1754 _dbus_string_find_blank (&command, 0, &i);
1755 _dbus_string_skip_blank (&command, i, &j);
1758 _dbus_string_delete (&command, i, j - i);
1760 if (!_dbus_string_move (&command, i, &args, 0))
1764 while (auth->handlers[i].command != NULL)
1766 if (_dbus_string_equal_c_str (&command,
1767 auth->handlers[i].command))
1769 _dbus_verbose ("%s: Processing auth command %s\n",
1770 DBUS_AUTH_NAME (auth),
1771 auth->handlers[i].command);
1773 if (!(* auth->handlers[i].func) (auth, &command, &args))
1781 if (auth->handlers[i].command == NULL)
1783 if (!process_unknown (auth, &command, &args))
1789 /* We've succeeded in processing the whole command so drop it out
1790 * of the incoming buffer and return TRUE to try another command.
1793 _dbus_string_delete (&auth->incoming, 0, eol);
1796 _dbus_string_delete (&auth->incoming, 0, 2);
1801 _dbus_string_free (&args);
1802 _dbus_string_free (&command);
1805 auth->needed_memory = TRUE;
1807 auth->needed_memory = FALSE;
1816 * @addtogroup DBusAuth
1821 * Creates a new auth conversation object for the server side.
1822 * See doc/dbus-sasl-profile.txt for full details on what
1825 * @returns the new object or #NULL if no memory
1828 _dbus_auth_server_new (void)
1831 DBusAuthServer *server_auth;
1833 auth = _dbus_auth_new (sizeof (DBusAuthServer));
1837 auth->handlers = server_handlers;
1839 server_auth = DBUS_AUTH_SERVER (auth);
1841 /* perhaps this should be per-mechanism with a lower
1844 server_auth->failures = 0;
1845 server_auth->max_failures = 6;
1851 * Creates a new auth conversation object for the client side.
1852 * See doc/dbus-sasl-profile.txt for full details on what
1855 * @returns the new object or #NULL if no memory
1858 _dbus_auth_client_new (void)
1862 auth = _dbus_auth_new (sizeof (DBusAuthClient));
1866 auth->handlers = client_handlers;
1868 /* Add a default mechanism to try */
1869 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1870 (void*) &all_mechanisms[0]))
1872 _dbus_auth_unref (auth);
1876 /* Now try the mechanism we just added */
1877 if (!client_try_next_mechanism (auth))
1879 _dbus_auth_unref (auth);
1887 * Increments the refcount of an auth object.
1889 * @param auth the auth conversation
1892 _dbus_auth_ref (DBusAuth *auth)
1894 _dbus_assert (auth != NULL);
1896 auth->refcount += 1;
1900 * Decrements the refcount of an auth object.
1902 * @param auth the auth conversation
1905 _dbus_auth_unref (DBusAuth *auth)
1907 _dbus_assert (auth != NULL);
1908 _dbus_assert (auth->refcount > 0);
1910 auth->refcount -= 1;
1911 if (auth->refcount == 0)
1913 shutdown_mech (auth);
1915 if (DBUS_AUTH_IS_CLIENT (auth))
1917 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1921 _dbus_keyring_unref (auth->keyring);
1923 _dbus_string_free (&auth->context);
1924 _dbus_string_free (&auth->challenge);
1925 _dbus_string_free (&auth->identity);
1926 _dbus_string_free (&auth->incoming);
1927 _dbus_string_free (&auth->outgoing);
1929 dbus_free_string_array (auth->allowed_mechs);
1936 * Sets an array of authentication mechanism names
1937 * that we are willing to use.
1939 * @param auth the auth conversation
1940 * @param mechanisms #NULL-terminated array of mechanism names
1941 * @returns #FALSE if no memory
1944 _dbus_auth_set_mechanisms (DBusAuth *auth,
1945 const char **mechanisms)
1949 if (mechanisms != NULL)
1951 copy = _dbus_dup_string_array (mechanisms);
1958 dbus_free_string_array (auth->allowed_mechs);
1960 auth->allowed_mechs = copy;
1966 * @param auth the auth conversation object
1967 * @returns #TRUE if we're in a final state
1969 #define DBUS_AUTH_IN_END_STATE(auth) ((auth)->need_disconnect || (auth)->authenticated)
1972 * Analyzes buffered input and moves the auth conversation forward,
1973 * returning the new state of the auth conversation.
1975 * @param auth the auth conversation
1976 * @returns the new state
1979 _dbus_auth_do_work (DBusAuth *auth)
1981 auth->needed_memory = FALSE;
1983 /* Max amount we'll buffer up before deciding someone's on crack */
1984 #define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE)
1988 if (DBUS_AUTH_IN_END_STATE (auth))
1991 if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
1992 _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
1994 auth->need_disconnect = TRUE;
1995 _dbus_verbose ("%s: Disconnecting due to excessive data buffered in auth phase\n",
1996 DBUS_AUTH_NAME (auth));
2000 if (auth->mech == NULL &&
2001 auth->already_got_mechanisms &&
2002 DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
2004 auth->need_disconnect = TRUE;
2005 _dbus_verbose ("%s: Disconnecting because we are out of mechanisms to try using\n",
2006 DBUS_AUTH_NAME (auth));
2010 while (process_command (auth));
2012 if (DBUS_AUTH_IS_SERVER (auth) &&
2013 DBUS_AUTH_SERVER (auth)->failures >=
2014 DBUS_AUTH_SERVER (auth)->max_failures)
2015 auth->need_disconnect = TRUE;
2017 if (auth->need_disconnect)
2018 return DBUS_AUTH_STATE_NEED_DISCONNECT;
2019 else if (auth->authenticated)
2021 if (_dbus_string_get_length (&auth->incoming) > 0)
2022 return DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES;
2024 return DBUS_AUTH_STATE_AUTHENTICATED;
2026 else if (auth->needed_memory)
2027 return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
2028 else if (_dbus_string_get_length (&auth->outgoing) > 0)
2029 return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
2031 return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
2035 * Gets bytes that need to be sent to the peer we're conversing with.
2036 * After writing some bytes, _dbus_auth_bytes_sent() must be called
2037 * to notify the auth object that they were written.
2039 * @param auth the auth conversation
2040 * @param str return location for a ref to the buffer to send
2041 * @returns #FALSE if nothing to send
2044 _dbus_auth_get_bytes_to_send (DBusAuth *auth,
2045 const DBusString **str)
2047 _dbus_assert (auth != NULL);
2048 _dbus_assert (str != NULL);
2052 if (DBUS_AUTH_IN_END_STATE (auth))
2055 if (_dbus_string_get_length (&auth->outgoing) == 0)
2058 *str = &auth->outgoing;
2064 * Notifies the auth conversation object that
2065 * the given number of bytes of the outgoing buffer
2066 * have been written out.
2068 * @param auth the auth conversation
2069 * @param bytes_sent number of bytes written out
2072 _dbus_auth_bytes_sent (DBusAuth *auth,
2075 _dbus_verbose ("%s: Sent %d bytes of: %s\n",
2076 DBUS_AUTH_NAME (auth),
2078 _dbus_string_get_const_data (&auth->outgoing));
2080 _dbus_string_delete (&auth->outgoing,
2083 if (auth->authenticated_pending_output &&
2084 _dbus_string_get_length (&auth->outgoing) == 0)
2085 auth->authenticated = TRUE;
2089 * Get a buffer to be used for reading bytes from the peer we're conversing
2090 * with. Bytes should be appended to this buffer.
2092 * @param auth the auth conversation
2093 * @param buffer return location for buffer to append bytes to
2096 _dbus_auth_get_buffer (DBusAuth *auth,
2097 DBusString **buffer)
2099 _dbus_assert (auth != NULL);
2100 _dbus_assert (!auth->buffer_outstanding);
2102 *buffer = &auth->incoming;
2104 auth->buffer_outstanding = TRUE;
2108 * Returns a buffer with new data read into it.
2110 * @param auth the auth conversation
2111 * @param buffer the buffer being returned
2112 * @param bytes_read number of new bytes added
2115 _dbus_auth_return_buffer (DBusAuth *auth,
2119 _dbus_assert (buffer == &auth->incoming);
2120 _dbus_assert (auth->buffer_outstanding);
2122 auth->buffer_outstanding = FALSE;
2126 * Returns leftover bytes that were not used as part of the auth
2127 * conversation. These bytes will be part of the message stream
2128 * instead. This function may not be called until authentication has
2131 * @param auth the auth conversation
2132 * @param str return location for pointer to string of unused bytes
2135 _dbus_auth_get_unused_bytes (DBusAuth *auth,
2136 const DBusString **str)
2138 if (!DBUS_AUTH_IN_END_STATE (auth))
2141 *str = &auth->incoming;
2146 * Gets rid of unused bytes returned by _dbus_auth_get_unused_bytes()
2147 * after we've gotten them and successfully moved them elsewhere.
2149 * @param auth the auth conversation
2152 _dbus_auth_delete_unused_bytes (DBusAuth *auth)
2154 if (!DBUS_AUTH_IN_END_STATE (auth))
2157 _dbus_string_set_length (&auth->incoming, 0);
2161 * Called post-authentication, indicates whether we need to encode
2162 * the message stream with _dbus_auth_encode_data() prior to
2163 * sending it to the peer.
2165 * @param auth the auth conversation
2166 * @returns #TRUE if we need to encode the stream
2169 _dbus_auth_needs_encoding (DBusAuth *auth)
2171 if (!auth->authenticated)
2174 if (auth->mech != NULL)
2176 if (DBUS_AUTH_IS_CLIENT (auth))
2177 return auth->mech->client_encode_func != NULL;
2179 return auth->mech->server_encode_func != NULL;
2186 * Called post-authentication, encodes a block of bytes for sending to
2187 * the peer. If no encoding was negotiated, just copies the bytes
2188 * (you can avoid this by checking _dbus_auth_needs_encoding()).
2190 * @param auth the auth conversation
2191 * @param plaintext the plain text data
2192 * @param encoded initialized string to where encoded data is appended
2193 * @returns #TRUE if we had enough memory and successfully encoded
2196 _dbus_auth_encode_data (DBusAuth *auth,
2197 const DBusString *plaintext,
2198 DBusString *encoded)
2200 _dbus_assert (plaintext != encoded);
2202 if (!auth->authenticated)
2205 if (_dbus_auth_needs_encoding (auth))
2207 if (DBUS_AUTH_IS_CLIENT (auth))
2208 return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
2210 return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
2214 return _dbus_string_copy (plaintext, 0, encoded,
2215 _dbus_string_get_length (encoded));
2220 * Called post-authentication, indicates whether we need to decode
2221 * the message stream with _dbus_auth_decode_data() after
2222 * receiving it from the peer.
2224 * @param auth the auth conversation
2225 * @returns #TRUE if we need to encode the stream
2228 _dbus_auth_needs_decoding (DBusAuth *auth)
2230 if (!auth->authenticated)
2233 if (auth->mech != NULL)
2235 if (DBUS_AUTH_IS_CLIENT (auth))
2236 return auth->mech->client_decode_func != NULL;
2238 return auth->mech->server_decode_func != NULL;
2246 * Called post-authentication, decodes a block of bytes received from
2247 * the peer. If no encoding was negotiated, just copies the bytes (you
2248 * can avoid this by checking _dbus_auth_needs_decoding()).
2250 * @todo We need to be able to distinguish "out of memory" error
2251 * from "the data is hosed" error.
2253 * @param auth the auth conversation
2254 * @param encoded the encoded data
2255 * @param plaintext initialized string where decoded data is appended
2256 * @returns #TRUE if we had enough memory and successfully decoded
2259 _dbus_auth_decode_data (DBusAuth *auth,
2260 const DBusString *encoded,
2261 DBusString *plaintext)
2263 _dbus_assert (plaintext != encoded);
2265 if (!auth->authenticated)
2268 if (_dbus_auth_needs_decoding (auth))
2270 if (DBUS_AUTH_IS_CLIENT (auth))
2271 return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
2273 return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
2277 return _dbus_string_copy (encoded, 0, plaintext,
2278 _dbus_string_get_length (plaintext));
2283 * Sets credentials received via reliable means from the operating
2286 * @param auth the auth conversation
2287 * @param credentials the credentials received
2290 _dbus_auth_set_credentials (DBusAuth *auth,
2291 const DBusCredentials *credentials)
2293 auth->credentials = *credentials;
2297 * Gets the identity we authorized the client as. Apps may have
2298 * different policies as to what identities they allow.
2300 * @param auth the auth conversation
2301 * @param credentials the credentials we've authorized
2304 _dbus_auth_get_identity (DBusAuth *auth,
2305 DBusCredentials *credentials)
2307 if (auth->authenticated)
2308 *credentials = auth->authorized_identity;
2310 _dbus_credentials_clear (credentials);
2314 * Sets the "authentication context" which scopes cookies
2315 * with the DBUS_COOKIE_SHA1 auth mechanism for example.
2317 * @param auth the auth conversation
2318 * @param context the context
2319 * @returns #FALSE if no memory
2322 _dbus_auth_set_context (DBusAuth *auth,
2323 const DBusString *context)
2325 return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context),
2326 &auth->context, 0, _dbus_string_get_length (context));
2331 #ifdef DBUS_BUILD_TESTS
2332 #include "dbus-test.h"
2333 #include "dbus-auth-script.h"
2337 process_test_subdir (const DBusString *test_base_dir,
2340 DBusString test_directory;
2341 DBusString filename;
2349 if (!_dbus_string_init (&test_directory))
2350 _dbus_assert_not_reached ("didn't allocate test_directory\n");
2352 _dbus_string_init_const (&filename, subdir);
2354 if (!_dbus_string_copy (test_base_dir, 0,
2355 &test_directory, 0))
2356 _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
2358 if (!_dbus_concat_dir_and_file (&test_directory, &filename))
2359 _dbus_assert_not_reached ("couldn't allocate full path");
2361 _dbus_string_free (&filename);
2362 if (!_dbus_string_init (&filename))
2363 _dbus_assert_not_reached ("didn't allocate filename string\n");
2365 dbus_error_init (&error);
2366 dir = _dbus_directory_open (&test_directory, &error);
2369 _dbus_warn ("Could not open %s: %s\n",
2370 _dbus_string_get_const_data (&test_directory),
2372 dbus_error_free (&error);
2376 printf ("Testing:\n");
2379 while (_dbus_directory_get_next_file (dir, &filename, &error))
2381 DBusString full_path;
2383 if (!_dbus_string_init (&full_path))
2384 _dbus_assert_not_reached ("couldn't init string");
2386 if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
2387 _dbus_assert_not_reached ("couldn't copy dir to full_path");
2389 if (!_dbus_concat_dir_and_file (&full_path, &filename))
2390 _dbus_assert_not_reached ("couldn't concat file to dir");
2392 if (!_dbus_string_ends_with_c_str (&filename, ".auth-script"))
2394 _dbus_verbose ("Skipping non-.auth-script file %s\n",
2395 _dbus_string_get_const_data (&filename));
2396 _dbus_string_free (&full_path);
2400 printf (" %s\n", _dbus_string_get_const_data (&filename));
2402 if (!_dbus_auth_script_run (&full_path))
2404 _dbus_string_free (&full_path);
2408 _dbus_string_free (&full_path);
2411 if (dbus_error_is_set (&error))
2413 _dbus_warn ("Could not get next file in %s: %s\n",
2414 _dbus_string_get_const_data (&test_directory), error.message);
2415 dbus_error_free (&error);
2424 _dbus_directory_close (dir);
2425 _dbus_string_free (&test_directory);
2426 _dbus_string_free (&filename);
2432 process_test_dirs (const char *test_data_dir)
2434 DBusString test_directory;
2439 _dbus_string_init_const (&test_directory, test_data_dir);
2441 if (!process_test_subdir (&test_directory, "auth"))
2448 _dbus_string_free (&test_directory);
2454 _dbus_auth_test (const char *test_data_dir)
2457 if (test_data_dir == NULL)
2460 if (!process_test_dirs (test_data_dir))
2466 #endif /* DBUS_BUILD_TESTS */