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"
28 /* See doc/dbus-sasl-profile.txt */
31 * @defgroup DBusAuth Authentication
32 * @ingroup DBusInternals
33 * @brief DBusAuth object
35 * DBusAuth manages the authentication negotiation when a connection
36 * is first established, and also manage any encryption used over a
39 * The file doc/dbus-sasl-profile.txt documents the network protocol
40 * used for authentication.
44 * @defgroup DBusAuthInternals Authentication implementation details
45 * @ingroup DBusInternals
46 * @brief DBusAuth implementation details
48 * Private details of authentication code.
54 * Processes a command. Returns whether we had enough memory to
55 * complete the operation.
57 typedef dbus_bool_t (* DBusProcessAuthCommandFunction) (DBusAuth *auth,
58 const DBusString *command,
59 const DBusString *args);
64 DBusProcessAuthCommandFunction func;
65 } DBusAuthCommandHandler;
68 * This function appends an initial client response to the given string
70 typedef dbus_bool_t (* DBusInitialResponseFunction) (DBusAuth *auth,
71 DBusString *response);
74 * This function processes a block of data received from the peer.
75 * i.e. handles a DATA command.
77 typedef dbus_bool_t (* DBusAuthDataFunction) (DBusAuth *auth,
78 const DBusString *data);
81 * This function encodes a block of data from the peer.
83 typedef dbus_bool_t (* DBusAuthEncodeFunction) (DBusAuth *auth,
84 const DBusString *data,
88 * This function decodes a block of data from the peer.
90 typedef dbus_bool_t (* DBusAuthDecodeFunction) (DBusAuth *auth,
91 const DBusString *data,
95 * This function is called when the mechanism is abandoned.
97 typedef void (* DBusAuthShutdownFunction) (DBusAuth *auth);
101 const char *mechanism;
102 DBusAuthDataFunction server_data_func;
103 DBusAuthEncodeFunction server_encode_func;
104 DBusAuthDecodeFunction server_decode_func;
105 DBusAuthShutdownFunction server_shutdown_func;
106 DBusInitialResponseFunction client_initial_response_func;
107 DBusAuthDataFunction client_data_func;
108 DBusAuthEncodeFunction client_encode_func;
109 DBusAuthDecodeFunction client_decode_func;
110 DBusAuthShutdownFunction client_shutdown_func;
111 } DBusAuthMechanismHandler;
114 * Internal members of DBusAuth.
118 int refcount; /**< reference count */
120 DBusString incoming; /**< Incoming data buffer */
121 DBusString outgoing; /**< Outgoing data buffer */
123 const DBusAuthCommandHandler *handlers; /**< Handlers for commands */
125 const DBusAuthMechanismHandler *mech; /**< Current auth mechanism */
127 DBusString identity; /**< Current identity we're authorizing
131 DBusCredentials credentials; /**< Credentials, fields may be -1 */
133 DBusCredentials authorized_identity; /**< Credentials that are authorized */
135 unsigned int needed_memory : 1; /**< We needed memory to continue since last
136 * successful getting something done
138 unsigned int need_disconnect : 1; /**< We've given up, time to disconnect */
139 unsigned int authenticated : 1; /**< We are authenticated */
140 unsigned int authenticated_pending_output : 1; /**< Authenticated once we clear outgoing buffer */
141 unsigned int authenticated_pending_begin : 1; /**< Authenticated once we get BEGIN */
142 unsigned int already_got_mechanisms : 1; /**< Client already got mech list */
143 unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */
150 DBusList *mechs_to_try; /**< Mechanisms we got from the server that we're going to try using */
158 int failures; /**< Number of times client has been rejected */
159 int max_failures; /**< Number of times we reject before disconnect */
163 static dbus_bool_t process_auth (DBusAuth *auth,
164 const DBusString *command,
165 const DBusString *args);
166 static dbus_bool_t process_cancel (DBusAuth *auth,
167 const DBusString *command,
168 const DBusString *args);
169 static dbus_bool_t process_begin (DBusAuth *auth,
170 const DBusString *command,
171 const DBusString *args);
172 static dbus_bool_t process_data_server (DBusAuth *auth,
173 const DBusString *command,
174 const DBusString *args);
175 static dbus_bool_t process_error_server (DBusAuth *auth,
176 const DBusString *command,
177 const DBusString *args);
178 static dbus_bool_t process_rejected (DBusAuth *auth,
179 const DBusString *command,
180 const DBusString *args);
181 static dbus_bool_t process_ok (DBusAuth *auth,
182 const DBusString *command,
183 const DBusString *args);
184 static dbus_bool_t process_data_client (DBusAuth *auth,
185 const DBusString *command,
186 const DBusString *args);
187 static dbus_bool_t process_error_client (DBusAuth *auth,
188 const DBusString *command,
189 const DBusString *args);
192 static dbus_bool_t client_try_next_mechanism (DBusAuth *auth);
193 static dbus_bool_t send_rejected (DBusAuth *auth);
195 static DBusAuthCommandHandler
196 server_handlers[] = {
197 { "AUTH", process_auth },
198 { "CANCEL", process_cancel },
199 { "BEGIN", process_begin },
200 { "DATA", process_data_server },
201 { "ERROR", process_error_server },
205 static DBusAuthCommandHandler
206 client_handlers[] = {
207 { "REJECTED", process_rejected },
208 { "OK", process_ok },
209 { "DATA", process_data_client },
210 { "ERROR", process_error_client },
215 * @param auth the auth conversation
216 * @returns #TRUE if the conversation is the server side
218 #define DBUS_AUTH_IS_SERVER(auth) ((auth)->handlers == server_handlers)
220 * @param auth the auth conversation
221 * @returns #TRUE if the conversation is the client side
223 #define DBUS_AUTH_IS_CLIENT(auth) ((auth)->handlers == client_handlers)
225 * @param auth the auth conversation
226 * @returns auth cast to DBusAuthClient
228 #define DBUS_AUTH_CLIENT(auth) ((DBusAuthClient*)(auth))
230 * @param auth the auth conversation
231 * @returns auth cast to DBusAuthServer
233 #define DBUS_AUTH_SERVER(auth) ((DBusAuthServer*)(auth))
236 _dbus_auth_new (int size)
240 auth = dbus_malloc0 (size);
246 auth->credentials.pid = -1;
247 auth->credentials.uid = -1;
248 auth->credentials.gid = -1;
250 auth->authorized_identity.pid = -1;
251 auth->authorized_identity.uid = -1;
252 auth->authorized_identity.gid = -1;
254 /* note that we don't use the max string length feature,
255 * because you can't use that feature if you're going to
256 * try to recover from out-of-memory (it creates
257 * what looks like unrecoverable inability to alloc
258 * more space in the string). But we do handle
259 * overlong buffers in _dbus_auth_do_work().
262 if (!_dbus_string_init (&auth->incoming, _DBUS_INT_MAX))
268 if (!_dbus_string_init (&auth->outgoing, _DBUS_INT_MAX))
270 _dbus_string_free (&auth->incoming);
275 if (!_dbus_string_init (&auth->identity, _DBUS_INT_MAX))
277 _dbus_string_free (&auth->incoming);
278 _dbus_string_free (&auth->outgoing);
287 get_state (DBusAuth *auth)
289 if (DBUS_AUTH_IS_SERVER (auth) &&
290 DBUS_AUTH_SERVER (auth)->failures >=
291 DBUS_AUTH_SERVER (auth)->max_failures)
292 auth->need_disconnect = TRUE;
294 if (auth->need_disconnect)
295 return DBUS_AUTH_STATE_NEED_DISCONNECT;
296 else if (auth->authenticated)
298 if (_dbus_string_get_length (&auth->incoming) > 0)
299 return DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES;
301 return DBUS_AUTH_STATE_AUTHENTICATED;
303 else if (auth->needed_memory)
304 return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
305 else if (_dbus_string_get_length (&auth->outgoing) > 0)
306 return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
308 return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
312 shutdown_mech (DBusAuth *auth)
314 /* Cancel any auth */
315 auth->authenticated_pending_begin = FALSE;
316 auth->authenticated = FALSE;
317 auth->already_asked_for_initial_response = FALSE;
318 _dbus_string_set_length (&auth->identity, 0);
319 auth->authorized_identity.pid = -1;
320 auth->authorized_identity.uid = -1;
321 auth->authorized_identity.gid = -1;
323 if (auth->mech != NULL)
325 _dbus_verbose ("Shutting down mechanism %s\n",
326 auth->mech->mechanism);
328 if (DBUS_AUTH_IS_CLIENT (auth))
329 (* auth->mech->client_shutdown_func) (auth);
331 (* auth->mech->server_shutdown_func) (auth);
338 handle_server_data_stupid_test_mech (DBusAuth *auth,
339 const DBusString *data)
341 if (!_dbus_string_append (&auth->outgoing,
345 auth->authenticated_pending_begin = TRUE;
351 handle_server_shutdown_stupid_test_mech (DBusAuth *auth)
357 handle_client_data_stupid_test_mech (DBusAuth *auth,
358 const DBusString *data)
365 handle_client_shutdown_stupid_test_mech (DBusAuth *auth)
370 /* the stupid test mech is a base64-encoded string;
371 * all the inefficiency, none of the security!
374 handle_encode_stupid_test_mech (DBusAuth *auth,
375 const DBusString *plaintext,
378 if (!_dbus_string_base64_encode (plaintext, 0, encoded,
379 _dbus_string_get_length (encoded)))
386 handle_decode_stupid_test_mech (DBusAuth *auth,
387 const DBusString *encoded,
388 DBusString *plaintext)
390 if (!_dbus_string_base64_decode (encoded, 0, plaintext,
391 _dbus_string_get_length (plaintext)))
398 handle_server_data_external_mech (DBusAuth *auth,
399 const DBusString *data)
401 DBusCredentials desired_identity;
403 if (auth->credentials.uid < 0)
405 _dbus_verbose ("no credentials, mechanism EXTERNAL can't authenticate\n");
406 return send_rejected (auth);
409 if (_dbus_string_get_length (data) > 0)
411 if (_dbus_string_get_length (&auth->identity) > 0)
413 /* Tried to send two auth identities, wtf */
414 return send_rejected (auth);
418 /* this is our auth identity */
419 if (!_dbus_string_copy (data, 0, &auth->identity, 0))
424 /* Poke client for an auth identity, if none given */
425 if (_dbus_string_get_length (&auth->identity) == 0 &&
426 !auth->already_asked_for_initial_response)
428 if (_dbus_string_append (&auth->outgoing,
431 _dbus_verbose ("sending empty challenge asking client for auth identity\n");
432 auth->already_asked_for_initial_response = TRUE;
439 desired_identity.pid = -1;
440 desired_identity.uid = -1;
441 desired_identity.gid = -1;
443 /* If auth->identity is still empty here, then client
444 * responded with an empty string after we poked it for
445 * an initial response. This means to try to auth the
446 * identity provided in the credentials.
448 if (_dbus_string_get_length (&auth->identity) == 0)
450 desired_identity.uid = auth->credentials.uid;
454 if (!_dbus_credentials_from_uid_string (&auth->identity,
456 return send_rejected (auth);
459 if (desired_identity.uid < 0)
461 _dbus_verbose ("desired UID %d is no good\n", desired_identity.uid);
462 return send_rejected (auth);
465 if (_dbus_credentials_match (&auth->credentials,
468 /* client has authenticated */
469 _dbus_verbose ("authenticated client with UID %d matching socket credentials UID %d\n",
470 desired_identity.uid,
471 auth->credentials.uid);
473 if (!_dbus_string_append (&auth->outgoing,
477 auth->authorized_identity.uid = desired_identity.uid;
479 auth->authenticated_pending_begin = TRUE;
485 return send_rejected (auth);
490 handle_server_shutdown_external_mech (DBusAuth *auth)
496 handle_client_initial_response_external_mech (DBusAuth *auth,
497 DBusString *response)
499 /* We always append our UID as an initial response, so the server
500 * doesn't have to send back an empty challenge to check whether we
501 * want to specify an identity. i.e. this avoids a round trip that
502 * the spec for the EXTERNAL mechanism otherwise requires.
504 DBusString plaintext;
506 if (!_dbus_string_init (&plaintext, _DBUS_INT_MAX))
509 if (!_dbus_string_append_our_uid (&plaintext))
512 if (!_dbus_string_base64_encode (&plaintext, 0,
514 _dbus_string_get_length (response)))
517 _dbus_string_free (&plaintext);
522 _dbus_string_free (&plaintext);
527 handle_client_data_external_mech (DBusAuth *auth,
528 const DBusString *data)
535 handle_client_shutdown_external_mech (DBusAuth *auth)
540 /* Put mechanisms here in order of preference.
541 * What I eventually want to have is:
543 * - a mechanism that checks UNIX domain socket credentials
544 * - a simple magic cookie mechanism like X11 or ICE
545 * - mechanisms that chain to Cyrus SASL, so we can use anything it
546 * offers such as Kerberos, X509, whatever.
549 static const DBusAuthMechanismHandler
552 handle_server_data_external_mech,
554 handle_server_shutdown_external_mech,
555 handle_client_initial_response_external_mech,
556 handle_client_data_external_mech,
558 handle_client_shutdown_external_mech },
559 /* Obviously this has to die for production use */
560 { "DBUS_STUPID_TEST_MECH",
561 handle_server_data_stupid_test_mech,
562 handle_encode_stupid_test_mech,
563 handle_decode_stupid_test_mech,
564 handle_server_shutdown_stupid_test_mech,
566 handle_client_data_stupid_test_mech,
567 handle_encode_stupid_test_mech,
568 handle_decode_stupid_test_mech,
569 handle_client_shutdown_stupid_test_mech },
573 static const DBusAuthMechanismHandler*
574 find_mech (const DBusString *name)
579 while (all_mechanisms[i].mechanism != NULL)
581 if (_dbus_string_equal_c_str (name,
582 all_mechanisms[i].mechanism))
584 return &all_mechanisms[i];
593 send_rejected (DBusAuth *auth)
596 DBusAuthServer *server_auth;
599 if (!_dbus_string_init (&command, _DBUS_INT_MAX))
602 if (!_dbus_string_append (&command,
607 while (all_mechanisms[i].mechanism != NULL)
609 if (!_dbus_string_append (&command,
613 if (!_dbus_string_append (&command,
614 all_mechanisms[i].mechanism))
620 if (!_dbus_string_append (&command, "\r\n"))
623 if (!_dbus_string_copy (&command, 0, &auth->outgoing,
624 _dbus_string_get_length (&auth->outgoing)))
627 _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
628 server_auth = DBUS_AUTH_SERVER (auth);
629 server_auth->failures += 1;
634 _dbus_string_free (&command);
639 process_auth (DBusAuth *auth,
640 const DBusString *command,
641 const DBusString *args)
645 /* We are already using a mechanism, client is on crack */
646 if (!_dbus_string_append (&auth->outgoing,
647 "ERROR \"Sent AUTH while another AUTH in progress\"\r\n"))
652 else if (_dbus_string_get_length (args) == 0)
654 /* No args to the auth, send mechanisms */
655 if (!send_rejected (auth))
664 DBusString base64_response;
665 DBusString decoded_response;
667 _dbus_string_find_blank (args, 0, &i);
669 if (!_dbus_string_init (&mech, _DBUS_INT_MAX))
672 if (!_dbus_string_init (&base64_response, _DBUS_INT_MAX))
674 _dbus_string_free (&mech);
678 if (!_dbus_string_init (&decoded_response, _DBUS_INT_MAX))
680 _dbus_string_free (&mech);
681 _dbus_string_free (&base64_response);
685 if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
688 if (!_dbus_string_copy (args, i, &base64_response, 0))
691 if (!_dbus_string_base64_decode (&base64_response, 0,
692 &decoded_response, 0))
695 auth->mech = find_mech (&mech);
696 if (auth->mech != NULL)
698 _dbus_verbose ("Trying mechanism %s with initial response of %d bytes\n",
699 auth->mech->mechanism,
700 _dbus_string_get_length (&decoded_response));
702 if (!(* auth->mech->server_data_func) (auth,
708 /* Unsupported mechanism */
709 if (!send_rejected (auth))
713 _dbus_string_free (&mech);
714 _dbus_string_free (&base64_response);
715 _dbus_string_free (&decoded_response);
721 _dbus_string_free (&mech);
722 _dbus_string_free (&base64_response);
723 _dbus_string_free (&decoded_response);
729 process_cancel (DBusAuth *auth,
730 const DBusString *command,
731 const DBusString *args)
733 shutdown_mech (auth);
739 process_begin (DBusAuth *auth,
740 const DBusString *command,
741 const DBusString *args)
743 if (auth->authenticated_pending_begin)
744 auth->authenticated = TRUE;
747 auth->need_disconnect = TRUE; /* client trying to send data before auth,
750 shutdown_mech (auth);
757 process_data_server (DBusAuth *auth,
758 const DBusString *command,
759 const DBusString *args)
761 if (auth->mech != NULL)
765 if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
768 if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
770 _dbus_string_free (&decoded);
774 if (!(* auth->mech->server_data_func) (auth, &decoded))
776 _dbus_string_free (&decoded);
780 _dbus_string_free (&decoded);
784 if (!_dbus_string_append (&auth->outgoing,
785 "ERROR \"Not currently in an auth conversation\"\r\n"))
793 process_error_server (DBusAuth *auth,
794 const DBusString *command,
795 const DBusString *args)
801 /* return FALSE if no memory, TRUE if all OK */
803 get_word (const DBusString *str,
809 _dbus_string_skip_blank (str, *start, start);
810 _dbus_string_find_blank (str, *start, &i);
814 if (!_dbus_string_copy_len (str, *start, i, word, 0))
824 record_mechanisms (DBusAuth *auth,
825 const DBusString *command,
826 const DBusString *args)
831 if (auth->already_got_mechanisms)
834 len = _dbus_string_get_length (args);
840 const DBusAuthMechanismHandler *mech;
842 if (!_dbus_string_init (&m, _DBUS_INT_MAX))
845 if (!get_word (args, &next, &m))
848 mech = find_mech (&m);
852 /* FIXME right now we try mechanisms in the order
853 * the server lists them; should we do them in
854 * some more deterministic order?
856 * Probably in all_mechanisms order, our order of
857 * preference. Of course when the server is us,
858 * it lists things in that order anyhow.
861 _dbus_verbose ("Adding mechanism %s to list we will try\n",
864 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
872 _dbus_string_get_const_data (&m, &s);
873 _dbus_verbose ("Server offered mechanism \"%s\" that we don't know how to use\n",
877 _dbus_string_free (&m);
880 auth->already_got_mechanisms = TRUE;
885 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
891 client_try_next_mechanism (DBusAuth *auth)
893 const DBusAuthMechanismHandler *mech;
894 DBusString auth_command;
896 if (DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
899 mech = DBUS_AUTH_CLIENT (auth)->mechs_to_try->data;
901 if (!_dbus_string_init (&auth_command, _DBUS_INT_MAX))
904 if (!_dbus_string_append (&auth_command,
907 _dbus_string_free (&auth_command);
911 if (!_dbus_string_append (&auth_command,
914 _dbus_string_free (&auth_command);
918 if (mech->client_initial_response_func != NULL)
920 if (!_dbus_string_append (&auth_command, " "))
922 _dbus_string_free (&auth_command);
926 if (!(* mech->client_initial_response_func) (auth, &auth_command))
928 _dbus_string_free (&auth_command);
933 if (!_dbus_string_append (&auth_command,
936 _dbus_string_free (&auth_command);
940 if (!_dbus_string_copy (&auth_command, 0,
942 _dbus_string_get_length (&auth->outgoing)))
944 _dbus_string_free (&auth_command);
949 _dbus_list_pop_first (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
951 _dbus_verbose ("Trying mechanism %s\n",
952 auth->mech->mechanism);
958 process_rejected (DBusAuth *auth,
959 const DBusString *command,
960 const DBusString *args)
962 shutdown_mech (auth);
964 if (!auth->already_got_mechanisms)
966 if (!record_mechanisms (auth, command, args))
970 if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
972 client_try_next_mechanism (auth);
977 auth->need_disconnect = TRUE;
984 process_ok (DBusAuth *auth,
985 const DBusString *command,
986 const DBusString *args)
988 if (!_dbus_string_append (&auth->outgoing,
992 auth->authenticated_pending_output = TRUE;
999 process_data_client (DBusAuth *auth,
1000 const DBusString *command,
1001 const DBusString *args)
1003 if (auth->mech != NULL)
1007 if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
1010 if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
1012 _dbus_string_free (&decoded);
1016 if (!(* auth->mech->client_data_func) (auth, &decoded))
1018 _dbus_string_free (&decoded);
1022 _dbus_string_free (&decoded);
1026 if (!_dbus_string_append (&auth->outgoing,
1027 "ERROR \"Got DATA when not in an auth exchange\"\r\n"))
1035 process_error_client (DBusAuth *auth,
1036 const DBusString *command,
1037 const DBusString *args)
1043 process_unknown (DBusAuth *auth,
1044 const DBusString *command,
1045 const DBusString *args)
1047 if (!_dbus_string_append (&auth->outgoing,
1048 "ERROR \"Unknown command\"\r\n"))
1054 /* returns whether to call it again right away */
1056 process_command (DBusAuth *auth)
1067 if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
1070 if (!_dbus_string_init (&command, _DBUS_INT_MAX))
1072 auth->needed_memory = TRUE;
1076 if (!_dbus_string_init (&args, _DBUS_INT_MAX))
1078 auth->needed_memory = TRUE;
1082 if (eol > _DBUS_ONE_MEGABYTE)
1084 /* This is a giant line, someone is trying to hose us. */
1085 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command too long\"\r\n"))
1091 if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0))
1094 if (!_dbus_string_validate_ascii (&command, 0,
1095 _dbus_string_get_length (&command)))
1097 _dbus_verbose ("Command contained non-ASCII chars or embedded nul\n");
1098 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command contained non-ASCII\"\r\n"))
1106 _dbus_string_get_const_data (&command, &q);
1107 _dbus_verbose ("got command \"%s\"\n", q);
1110 _dbus_string_find_blank (&command, 0, &i);
1111 _dbus_string_skip_blank (&command, i, &j);
1114 _dbus_string_delete (&command, i, j - i);
1116 if (!_dbus_string_move (&command, i, &args, 0))
1120 while (auth->handlers[i].command != NULL)
1122 if (_dbus_string_equal_c_str (&command,
1123 auth->handlers[i].command))
1125 _dbus_verbose ("Processing auth command %s\n",
1126 auth->handlers[i].command);
1128 if (!(* auth->handlers[i].func) (auth, &command, &args))
1136 if (auth->handlers[i].command == NULL)
1138 if (!process_unknown (auth, &command, &args))
1144 /* We've succeeded in processing the whole command so drop it out
1145 * of the incoming buffer and return TRUE to try another command.
1148 _dbus_string_delete (&auth->incoming, 0, eol);
1151 _dbus_string_delete (&auth->incoming, 0, 2);
1156 _dbus_string_free (&args);
1157 _dbus_string_free (&command);
1160 auth->needed_memory = TRUE;
1162 auth->needed_memory = FALSE;
1171 * @addtogroup DBusAuth
1176 * Creates a new auth conversation object for the server side.
1177 * See doc/dbus-sasl-profile.txt for full details on what
1180 * @returns the new object or #NULL if no memory
1183 _dbus_auth_server_new (void)
1186 DBusAuthServer *server_auth;
1188 auth = _dbus_auth_new (sizeof (DBusAuthServer));
1192 auth->handlers = server_handlers;
1194 server_auth = DBUS_AUTH_SERVER (auth);
1196 /* perhaps this should be per-mechanism with a lower
1199 server_auth->failures = 0;
1200 server_auth->max_failures = 6;
1206 * Creates a new auth conversation object for the client side.
1207 * See doc/dbus-sasl-profile.txt for full details on what
1210 * @returns the new object or #NULL if no memory
1213 _dbus_auth_client_new (void)
1217 auth = _dbus_auth_new (sizeof (DBusAuthClient));
1221 auth->handlers = client_handlers;
1223 /* Add a default mechanism to try */
1224 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1225 (void*) &all_mechanisms[0]))
1227 _dbus_auth_unref (auth);
1231 /* Now try the mechanism we just added */
1232 if (!client_try_next_mechanism (auth))
1234 _dbus_auth_unref (auth);
1242 * Increments the refcount of an auth object.
1244 * @param auth the auth conversation
1247 _dbus_auth_ref (DBusAuth *auth)
1249 _dbus_assert (auth != NULL);
1251 auth->refcount += 1;
1255 * Decrements the refcount of an auth object.
1257 * @param auth the auth conversation
1260 _dbus_auth_unref (DBusAuth *auth)
1262 _dbus_assert (auth != NULL);
1263 _dbus_assert (auth->refcount > 0);
1265 auth->refcount -= 1;
1266 if (auth->refcount == 0)
1268 shutdown_mech (auth);
1270 if (DBUS_AUTH_IS_CLIENT (auth))
1272 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1275 _dbus_string_free (&auth->identity);
1276 _dbus_string_free (&auth->incoming);
1277 _dbus_string_free (&auth->outgoing);
1283 * @param auth the auth conversation object
1284 * @returns #TRUE if we're in a final state
1286 #define DBUS_AUTH_IN_END_STATE(auth) ((auth)->need_disconnect || (auth)->authenticated)
1289 * Analyzes buffered input and moves the auth conversation forward,
1290 * returning the new state of the auth conversation.
1292 * @param auth the auth conversation
1293 * @returns the new state
1296 _dbus_auth_do_work (DBusAuth *auth)
1298 if (DBUS_AUTH_IN_END_STATE (auth))
1299 return get_state (auth);
1301 auth->needed_memory = FALSE;
1303 /* Max amount we'll buffer up before deciding someone's on crack */
1304 #define MAX_BUFFER (16 * 1024)
1308 if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
1309 _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
1311 auth->need_disconnect = TRUE;
1312 _dbus_verbose ("Disconnecting due to excessive data buffered in auth phase\n");
1316 if (auth->mech == NULL &&
1317 auth->already_got_mechanisms &&
1318 DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
1320 auth->need_disconnect = TRUE;
1321 _dbus_verbose ("Disconnecting because we are out of mechanisms to try using\n");
1325 while (process_command (auth));
1327 return get_state (auth);
1331 * Gets bytes that need to be sent to the peer we're conversing with.
1332 * After writing some bytes, _dbus_auth_bytes_sent() must be called
1333 * to notify the auth object that they were written.
1335 * @param auth the auth conversation
1336 * @param str return location for a ref to the buffer to send
1337 * @returns #FALSE if nothing to send
1340 _dbus_auth_get_bytes_to_send (DBusAuth *auth,
1341 const DBusString **str)
1343 _dbus_assert (auth != NULL);
1344 _dbus_assert (str != NULL);
1348 if (DBUS_AUTH_IN_END_STATE (auth))
1351 if (_dbus_string_get_length (&auth->outgoing) == 0)
1354 *str = &auth->outgoing;
1360 * Notifies the auth conversation object that
1361 * the given number of bytes of the outgoing buffer
1362 * have been written out.
1364 * @param auth the auth conversation
1365 * @param bytes_sent number of bytes written out
1368 _dbus_auth_bytes_sent (DBusAuth *auth,
1371 _dbus_string_delete (&auth->outgoing,
1374 if (auth->authenticated_pending_output &&
1375 _dbus_string_get_length (&auth->outgoing) == 0)
1376 auth->authenticated = TRUE;
1380 * Stores bytes received from the peer we're conversing with.
1382 * @param auth the auth conversation
1383 * @param str the received bytes.
1384 * @returns #FALSE if not enough memory to store the bytes.
1387 _dbus_auth_bytes_received (DBusAuth *auth,
1388 const DBusString *str)
1390 _dbus_assert (auth != NULL);
1391 _dbus_assert (str != NULL);
1393 if (DBUS_AUTH_IN_END_STATE (auth))
1396 auth->needed_memory = FALSE;
1398 if (!_dbus_string_copy (str, 0,
1400 _dbus_string_get_length (&auth->incoming)))
1402 auth->needed_memory = TRUE;
1406 _dbus_auth_do_work (auth);
1412 * Returns leftover bytes that were not used as part of the auth
1413 * conversation. These bytes will be part of the message stream
1414 * instead. This function may not be called until authentication has
1417 * @param auth the auth conversation
1418 * @param str string to append the unused bytes to
1419 * @returns #FALSE if not enough memory to return the bytes
1422 _dbus_auth_get_unused_bytes (DBusAuth *auth,
1425 if (!DBUS_AUTH_IN_END_STATE (auth))
1428 if (!_dbus_string_move (&auth->incoming,
1430 _dbus_string_get_length (str)))
1437 * Called post-authentication, indicates whether we need to encode
1438 * the message stream with _dbus_auth_encode_data() prior to
1439 * sending it to the peer.
1441 * @param auth the auth conversation
1442 * @returns #TRUE if we need to encode the stream
1445 _dbus_auth_needs_encoding (DBusAuth *auth)
1447 if (!auth->authenticated)
1450 if (auth->mech != NULL)
1452 if (DBUS_AUTH_IS_CLIENT (auth))
1453 return auth->mech->client_encode_func != NULL;
1455 return auth->mech->server_encode_func != NULL;
1462 * Called post-authentication, encodes a block of bytes for sending to
1463 * the peer. If no encoding was negotiated, just copies the bytes
1464 * (you can avoid this by checking _dbus_auth_needs_encoding()).
1466 * @param auth the auth conversation
1467 * @param plaintext the plain text data
1468 * @param encoded initialized string to where encoded data is appended
1469 * @returns #TRUE if we had enough memory and successfully encoded
1472 _dbus_auth_encode_data (DBusAuth *auth,
1473 const DBusString *plaintext,
1474 DBusString *encoded)
1476 _dbus_assert (plaintext != encoded);
1478 if (!auth->authenticated)
1481 if (_dbus_auth_needs_encoding (auth))
1483 if (DBUS_AUTH_IS_CLIENT (auth))
1484 return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
1486 return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
1490 return _dbus_string_copy (plaintext, 0, encoded,
1491 _dbus_string_get_length (encoded));
1496 * Called post-authentication, indicates whether we need to decode
1497 * the message stream with _dbus_auth_decode_data() after
1498 * receiving it from the peer.
1500 * @param auth the auth conversation
1501 * @returns #TRUE if we need to encode the stream
1504 _dbus_auth_needs_decoding (DBusAuth *auth)
1506 if (!auth->authenticated)
1509 if (auth->mech != NULL)
1511 if (DBUS_AUTH_IS_CLIENT (auth))
1512 return auth->mech->client_decode_func != NULL;
1514 return auth->mech->server_decode_func != NULL;
1522 * Called post-authentication, decodes a block of bytes received from
1523 * the peer. If no encoding was negotiated, just copies the bytes (you
1524 * can avoid this by checking _dbus_auth_needs_decoding()).
1526 * @todo We need to be able to distinguish "out of memory" error
1527 * from "the data is hosed" error.
1529 * @param auth the auth conversation
1530 * @param encoded the encoded data
1531 * @param plaintext initialized string where decoded data is appended
1532 * @returns #TRUE if we had enough memory and successfully decoded
1535 _dbus_auth_decode_data (DBusAuth *auth,
1536 const DBusString *encoded,
1537 DBusString *plaintext)
1539 _dbus_assert (plaintext != encoded);
1541 if (!auth->authenticated)
1544 if (_dbus_auth_needs_decoding (auth))
1546 if (DBUS_AUTH_IS_CLIENT (auth))
1547 return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
1549 return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
1553 return _dbus_string_copy (encoded, 0, plaintext,
1554 _dbus_string_get_length (plaintext));
1559 * Sets credentials received via reliable means from the operating
1562 * @param auth the auth conversation
1563 * @param credentials the credentials received
1566 _dbus_auth_set_credentials (DBusAuth *auth,
1567 const DBusCredentials *credentials)
1569 auth->credentials = *credentials;
1573 * Gets the identity we authorized the client as. Apps may have
1574 * different policies as to what identities they allow.
1576 * @param auth the auth conversation
1577 * @param credentials the credentials we've authorized
1580 _dbus_auth_get_identity (DBusAuth *auth,
1581 DBusCredentials *credentials)
1583 if (auth->authenticated)
1585 *credentials = auth->authorized_identity;
1589 credentials->pid = -1;
1590 credentials->uid = -1;
1591 credentials->gid = -1;