1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-auth.c Authentication
4 * Copyright (C) 2002 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;
160 static dbus_bool_t process_auth (DBusAuth *auth,
161 const DBusString *command,
162 const DBusString *args);
163 static dbus_bool_t process_cancel (DBusAuth *auth,
164 const DBusString *command,
165 const DBusString *args);
166 static dbus_bool_t process_begin (DBusAuth *auth,
167 const DBusString *command,
168 const DBusString *args);
169 static dbus_bool_t process_data_server (DBusAuth *auth,
170 const DBusString *command,
171 const DBusString *args);
172 static dbus_bool_t process_error_server (DBusAuth *auth,
173 const DBusString *command,
174 const DBusString *args);
175 static dbus_bool_t process_mechanisms (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);
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 { "MECHANISMS", process_mechanisms },
208 { "REJECTED", process_rejected },
209 { "OK", process_ok },
210 { "DATA", process_data_client },
211 { "ERROR", process_error_client },
216 * @param auth the auth conversation
217 * @returns #TRUE if the conversation is the server side
219 #define DBUS_AUTH_IS_SERVER(auth) ((auth)->handlers == server_handlers)
221 * @param auth the auth conversation
222 * @returns #TRUE if the conversation is the client side
224 #define DBUS_AUTH_IS_CLIENT(auth) ((auth)->handlers == client_handlers)
226 * @param auth the auth conversation
227 * @returns auth cast to DBusAuthClient
229 #define DBUS_AUTH_CLIENT(auth) ((DBusAuthClient*)(auth))
231 * @param auth the auth conversation
232 * @returns auth cast to DBusAuthServer
234 #define DBUS_AUTH_SERVER(auth) ((DBusAuthServer*)(auth))
237 _dbus_auth_new (int size)
241 auth = dbus_malloc0 (size);
247 auth->credentials.pid = -1;
248 auth->credentials.uid = -1;
249 auth->credentials.gid = -1;
251 auth->authorized_identity.pid = -1;
252 auth->authorized_identity.uid = -1;
253 auth->authorized_identity.gid = -1;
255 /* note that we don't use the max string length feature,
256 * because you can't use that feature if you're going to
257 * try to recover from out-of-memory (it creates
258 * what looks like unrecoverable inability to alloc
259 * more space in the string). But we do handle
260 * overlong buffers in _dbus_auth_do_work().
263 if (!_dbus_string_init (&auth->incoming, _DBUS_INT_MAX))
269 if (!_dbus_string_init (&auth->outgoing, _DBUS_INT_MAX))
271 _dbus_string_free (&auth->incoming);
276 if (!_dbus_string_init (&auth->identity, _DBUS_INT_MAX))
278 _dbus_string_free (&auth->incoming);
279 _dbus_string_free (&auth->outgoing);
288 get_state (DBusAuth *auth)
290 if (auth->need_disconnect)
291 return DBUS_AUTH_STATE_NEED_DISCONNECT;
292 else if (auth->authenticated)
294 if (_dbus_string_get_length (&auth->incoming) > 0)
295 return DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES;
297 return DBUS_AUTH_STATE_AUTHENTICATED;
299 else if (auth->needed_memory)
300 return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
301 else if (_dbus_string_get_length (&auth->outgoing) > 0)
302 return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
304 return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
308 shutdown_mech (DBusAuth *auth)
310 /* Cancel any auth */
311 auth->authenticated_pending_begin = FALSE;
312 auth->authenticated = FALSE;
313 auth->already_asked_for_initial_response = FALSE;
314 _dbus_string_set_length (&auth->identity, 0);
315 auth->authorized_identity.pid = -1;
316 auth->authorized_identity.uid = -1;
317 auth->authorized_identity.gid = -1;
319 if (auth->mech != NULL)
321 _dbus_verbose ("Shutting down mechanism %s\n",
322 auth->mech->mechanism);
324 if (DBUS_AUTH_IS_CLIENT (auth))
325 (* auth->mech->client_shutdown_func) (auth);
327 (* auth->mech->server_shutdown_func) (auth);
334 handle_server_data_stupid_test_mech (DBusAuth *auth,
335 const DBusString *data)
337 if (!_dbus_string_append (&auth->outgoing,
341 auth->authenticated_pending_begin = TRUE;
347 handle_server_shutdown_stupid_test_mech (DBusAuth *auth)
353 handle_client_data_stupid_test_mech (DBusAuth *auth,
354 const DBusString *data)
361 handle_client_shutdown_stupid_test_mech (DBusAuth *auth)
366 /* the stupid test mech is a base64-encoded string;
367 * all the inefficiency, none of the security!
370 handle_encode_stupid_test_mech (DBusAuth *auth,
371 const DBusString *plaintext,
374 if (!_dbus_string_base64_encode (plaintext, 0, encoded,
375 _dbus_string_get_length (encoded)))
382 handle_decode_stupid_test_mech (DBusAuth *auth,
383 const DBusString *encoded,
384 DBusString *plaintext)
386 if (!_dbus_string_base64_decode (encoded, 0, plaintext,
387 _dbus_string_get_length (plaintext)))
394 do_rejection (DBusAuth *auth)
396 if (_dbus_string_append (&auth->outgoing,
399 shutdown_mech (auth);
400 _dbus_verbose ("rejected client auth\n");
408 handle_server_data_external_mech (DBusAuth *auth,
409 const DBusString *data)
411 DBusCredentials desired_identity;
413 if (auth->credentials.uid < 0)
415 _dbus_verbose ("no credentials, mechanism EXTERNAL can't authenticate\n");
416 return do_rejection (auth);
419 if (_dbus_string_get_length (data) > 0)
421 if (_dbus_string_get_length (&auth->identity) > 0)
423 /* Tried to send two auth identities, wtf */
424 return do_rejection (auth);
428 /* this is our auth identity */
429 if (!_dbus_string_copy (data, 0, &auth->identity, 0))
434 /* Poke client for an auth identity, if none given */
435 if (_dbus_string_get_length (&auth->identity) == 0 &&
436 !auth->already_asked_for_initial_response)
438 if (_dbus_string_append (&auth->outgoing,
441 _dbus_verbose ("sending empty challenge asking client for auth identity\n");
442 auth->already_asked_for_initial_response = TRUE;
449 desired_identity.pid = -1;
450 desired_identity.uid = -1;
451 desired_identity.gid = -1;
453 /* If auth->identity is still empty here, then client
454 * responded with an empty string after we poked it for
455 * an initial response. This means to try to auth the
456 * identity provided in the credentials.
458 if (_dbus_string_get_length (&auth->identity) == 0)
460 desired_identity.uid = auth->credentials.uid;
464 if (!_dbus_credentials_from_uid_string (&auth->identity,
466 return do_rejection (auth);
469 if (desired_identity.uid < 0)
471 _dbus_verbose ("desired UID %d is no good\n", desired_identity.uid);
472 return do_rejection (auth);
475 if (_dbus_credentials_match (&auth->credentials,
478 /* client has authenticated */
479 _dbus_verbose ("authenticated client with UID %d matching socket credentials UID %d\n",
480 desired_identity.uid,
481 auth->credentials.uid);
483 if (!_dbus_string_append (&auth->outgoing,
487 auth->authorized_identity.uid = desired_identity.uid;
489 auth->authenticated_pending_begin = TRUE;
495 return do_rejection (auth);
500 handle_server_shutdown_external_mech (DBusAuth *auth)
506 handle_client_initial_response_external_mech (DBusAuth *auth,
507 DBusString *response)
509 /* We always append our UID as an initial response, so the server
510 * doesn't have to send back an empty challenge to check whether we
511 * want to specify an identity. i.e. this avoids a round trip that
512 * the spec for the EXTERNAL mechanism otherwise requires.
514 DBusString plaintext;
516 if (!_dbus_string_init (&plaintext, _DBUS_INT_MAX))
519 if (!_dbus_string_append_our_uid (&plaintext))
522 if (!_dbus_string_base64_encode (&plaintext, 0,
524 _dbus_string_get_length (response)))
527 _dbus_string_free (&plaintext);
532 _dbus_string_free (&plaintext);
537 handle_client_data_external_mech (DBusAuth *auth,
538 const DBusString *data)
545 handle_client_shutdown_external_mech (DBusAuth *auth)
550 /* Put mechanisms here in order of preference.
551 * What I eventually want to have is:
553 * - a mechanism that checks UNIX domain socket credentials
554 * - a simple magic cookie mechanism like X11 or ICE
555 * - mechanisms that chain to Cyrus SASL, so we can use anything it
556 * offers such as Kerberos, X509, whatever.
559 static const DBusAuthMechanismHandler
562 handle_server_data_external_mech,
564 handle_server_shutdown_external_mech,
565 handle_client_initial_response_external_mech,
566 handle_client_data_external_mech,
568 handle_client_shutdown_external_mech },
569 /* Obviously this has to die for production use */
570 { "DBUS_STUPID_TEST_MECH",
571 handle_server_data_stupid_test_mech,
572 handle_encode_stupid_test_mech,
573 handle_decode_stupid_test_mech,
574 handle_server_shutdown_stupid_test_mech,
576 handle_client_data_stupid_test_mech,
577 handle_encode_stupid_test_mech,
578 handle_decode_stupid_test_mech,
579 handle_client_shutdown_stupid_test_mech },
583 static const DBusAuthMechanismHandler*
584 find_mech (const DBusString *name)
589 while (all_mechanisms[i].mechanism != NULL)
591 if (_dbus_string_equal_c_str (name,
592 all_mechanisms[i].mechanism))
594 return &all_mechanisms[i];
603 send_mechanisms (DBusAuth *auth)
608 if (!_dbus_string_init (&command, _DBUS_INT_MAX))
611 if (!_dbus_string_append (&command,
616 while (all_mechanisms[i].mechanism != NULL)
618 if (!_dbus_string_append (&command,
622 if (!_dbus_string_append (&command,
623 all_mechanisms[i].mechanism))
629 if (!_dbus_string_append (&command, "\r\n"))
632 if (!_dbus_string_copy (&command, 0, &auth->outgoing,
633 _dbus_string_get_length (&auth->outgoing)))
639 _dbus_string_free (&command);
644 process_auth (DBusAuth *auth,
645 const DBusString *command,
646 const DBusString *args)
650 /* We are already using a mechanism, client is on crack */
651 if (!_dbus_string_append (&auth->outgoing,
652 "ERROR \"Sent AUTH while another AUTH in progress\"\r\n"))
657 else if (_dbus_string_get_length (args) == 0)
659 /* No args to the auth, send mechanisms */
660 if (!send_mechanisms (auth))
669 DBusString base64_response;
670 DBusString decoded_response;
672 _dbus_string_find_blank (args, 0, &i);
674 if (!_dbus_string_init (&mech, _DBUS_INT_MAX))
677 if (!_dbus_string_init (&base64_response, _DBUS_INT_MAX))
679 _dbus_string_free (&mech);
683 if (!_dbus_string_init (&decoded_response, _DBUS_INT_MAX))
685 _dbus_string_free (&mech);
686 _dbus_string_free (&base64_response);
690 if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
693 if (!_dbus_string_copy (args, i, &base64_response, 0))
696 if (!_dbus_string_base64_decode (&base64_response, 0,
697 &decoded_response, 0))
700 auth->mech = find_mech (&mech);
701 if (auth->mech != NULL)
703 _dbus_verbose ("Trying mechanism %s with initial response of %d bytes\n",
704 auth->mech->mechanism,
705 _dbus_string_get_length (&decoded_response));
707 if (!(* auth->mech->server_data_func) (auth,
713 /* Unsupported mechanism */
714 if (!send_mechanisms (auth))
722 _dbus_string_free (&mech);
723 _dbus_string_free (&base64_response);
724 _dbus_string_free (&decoded_response);
730 process_cancel (DBusAuth *auth,
731 const DBusString *command,
732 const DBusString *args)
734 shutdown_mech (auth);
740 process_begin (DBusAuth *auth,
741 const DBusString *command,
742 const DBusString *args)
744 if (auth->authenticated_pending_begin)
745 auth->authenticated = TRUE;
748 auth->need_disconnect = TRUE; /* client trying to send data before auth,
751 shutdown_mech (auth);
758 process_data_server (DBusAuth *auth,
759 const DBusString *command,
760 const DBusString *args)
762 if (auth->mech != NULL)
766 if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
769 if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
771 _dbus_string_free (&decoded);
775 if (!(* auth->mech->server_data_func) (auth, &decoded))
777 _dbus_string_free (&decoded);
781 _dbus_string_free (&decoded);
785 if (!_dbus_string_append (&auth->outgoing,
786 "ERROR \"Not currently in an auth conversation\"\r\n"))
794 process_error_server (DBusAuth *auth,
795 const DBusString *command,
796 const DBusString *args)
802 /* return FALSE if no memory, TRUE if all OK */
804 get_word (const DBusString *str,
810 _dbus_string_skip_blank (str, *start, start);
811 _dbus_string_find_blank (str, *start, &i);
815 if (!_dbus_string_copy_len (str, *start, i, word, 0))
825 process_mechanisms (DBusAuth *auth,
826 const DBusString *command,
827 const DBusString *args)
832 if (auth->already_got_mechanisms)
835 len = _dbus_string_get_length (args);
841 const DBusAuthMechanismHandler *mech;
843 if (!_dbus_string_init (&m, _DBUS_INT_MAX))
846 if (!get_word (args, &next, &m))
849 mech = find_mech (&m);
853 /* FIXME right now we try mechanisms in the order
854 * the server lists them; should we do them in
855 * some more deterministic order?
857 * Probably in all_mechanisms order, our order of
858 * preference. Of course when the server is us,
859 * it lists things in that order anyhow.
862 _dbus_verbose ("Adding mechanism %s to list we will try\n",
865 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
873 _dbus_string_get_const_data (&m, &s);
874 _dbus_verbose ("Server offered mechanism \"%s\" that we don't know how to use\n",
878 _dbus_string_free (&m);
881 auth->already_got_mechanisms = TRUE;
886 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
892 client_try_next_mechanism (DBusAuth *auth)
894 const DBusAuthMechanismHandler *mech;
895 DBusString auth_command;
897 if (DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
900 mech = DBUS_AUTH_CLIENT (auth)->mechs_to_try->data;
902 if (!_dbus_string_init (&auth_command, _DBUS_INT_MAX))
905 if (!_dbus_string_append (&auth_command,
908 _dbus_string_free (&auth_command);
912 if (!_dbus_string_append (&auth_command,
915 _dbus_string_free (&auth_command);
919 if (mech->client_initial_response_func != NULL)
921 if (!_dbus_string_append (&auth_command, " "))
923 _dbus_string_free (&auth_command);
927 if (!(* mech->client_initial_response_func) (auth, &auth_command))
929 _dbus_string_free (&auth_command);
934 if (!_dbus_string_append (&auth_command,
937 _dbus_string_free (&auth_command);
941 if (!_dbus_string_copy (&auth_command, 0,
943 _dbus_string_get_length (&auth->outgoing)))
945 _dbus_string_free (&auth_command);
950 _dbus_list_pop_first (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
952 _dbus_verbose ("Trying mechanism %s\n",
953 auth->mech->mechanism);
959 process_rejected (DBusAuth *auth,
960 const DBusString *command,
961 const DBusString *args)
963 shutdown_mech (auth);
965 if (!auth->already_got_mechanisms)
967 /* Ask for mechanisms */
968 if (!_dbus_string_append (&auth->outgoing,
972 else if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
974 client_try_next_mechanism (auth);
979 auth->need_disconnect = TRUE;
986 process_ok (DBusAuth *auth,
987 const DBusString *command,
988 const DBusString *args)
990 if (!_dbus_string_append (&auth->outgoing,
994 auth->authenticated_pending_output = TRUE;
1001 process_data_client (DBusAuth *auth,
1002 const DBusString *command,
1003 const DBusString *args)
1005 if (auth->mech != NULL)
1009 if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
1012 if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
1014 _dbus_string_free (&decoded);
1018 if (!(* auth->mech->client_data_func) (auth, &decoded))
1020 _dbus_string_free (&decoded);
1024 _dbus_string_free (&decoded);
1028 if (!_dbus_string_append (&auth->outgoing,
1029 "ERROR \"Got DATA when not in an auth exchange\"\r\n"))
1037 process_error_client (DBusAuth *auth,
1038 const DBusString *command,
1039 const DBusString *args)
1045 process_unknown (DBusAuth *auth,
1046 const DBusString *command,
1047 const DBusString *args)
1049 if (!_dbus_string_append (&auth->outgoing,
1050 "ERROR \"Unknown command\"\r\n"))
1056 /* returns whether to call it again right away */
1058 process_command (DBusAuth *auth)
1069 if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
1072 if (!_dbus_string_init (&command, _DBUS_INT_MAX))
1074 auth->needed_memory = TRUE;
1078 if (!_dbus_string_init (&args, _DBUS_INT_MAX))
1080 auth->needed_memory = TRUE;
1084 if (eol > _DBUS_ONE_MEGABYTE)
1086 /* This is a giant line, someone is trying to hose us. */
1087 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command too long\"\r\n"))
1093 if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0))
1096 if (!_dbus_string_validate_ascii (&command, 0,
1097 _dbus_string_get_length (&command)))
1099 _dbus_verbose ("Command contained non-ASCII chars or embedded nul\n");
1100 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command contained non-ASCII\"\r\n"))
1108 _dbus_string_get_const_data (&command, &q);
1109 _dbus_verbose ("got command \"%s\"\n", q);
1112 _dbus_string_find_blank (&command, 0, &i);
1113 _dbus_string_skip_blank (&command, i, &j);
1116 _dbus_string_delete (&command, i, j - i);
1118 if (!_dbus_string_move (&command, i, &args, 0))
1122 while (auth->handlers[i].command != NULL)
1124 if (_dbus_string_equal_c_str (&command,
1125 auth->handlers[i].command))
1127 _dbus_verbose ("Processing auth command %s\n",
1128 auth->handlers[i].command);
1130 if (!(* auth->handlers[i].func) (auth, &command, &args))
1138 if (auth->handlers[i].command == NULL)
1140 if (!process_unknown (auth, &command, &args))
1146 /* We've succeeded in processing the whole command so drop it out
1147 * of the incoming buffer and return TRUE to try another command.
1150 _dbus_string_delete (&auth->incoming, 0, eol);
1153 _dbus_string_delete (&auth->incoming, 0, 2);
1158 _dbus_string_free (&args);
1159 _dbus_string_free (&command);
1162 auth->needed_memory = TRUE;
1164 auth->needed_memory = FALSE;
1173 * @addtogroup DBusAuth
1178 * Creates a new auth conversation object for the server side.
1179 * See doc/dbus-sasl-profile.txt for full details on what
1182 * @returns the new object or #NULL if no memory
1185 _dbus_auth_server_new (void)
1189 auth = _dbus_auth_new (sizeof (DBusAuthServer));
1193 auth->handlers = server_handlers;
1199 * Creates a new auth conversation object for the client side.
1200 * See doc/dbus-sasl-profile.txt for full details on what
1203 * @returns the new object or #NULL if no memory
1206 _dbus_auth_client_new (void)
1210 auth = _dbus_auth_new (sizeof (DBusAuthClient));
1214 auth->handlers = client_handlers;
1216 /* Add a default mechanism to try */
1217 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1218 (void*) &all_mechanisms[0]))
1220 _dbus_auth_unref (auth);
1224 /* Now try the mechanism we just added */
1225 if (!client_try_next_mechanism (auth))
1227 _dbus_auth_unref (auth);
1235 * Increments the refcount of an auth object.
1237 * @param auth the auth conversation
1240 _dbus_auth_ref (DBusAuth *auth)
1242 _dbus_assert (auth != NULL);
1244 auth->refcount += 1;
1248 * Decrements the refcount of an auth object.
1250 * @param auth the auth conversation
1253 _dbus_auth_unref (DBusAuth *auth)
1255 _dbus_assert (auth != NULL);
1256 _dbus_assert (auth->refcount > 0);
1258 auth->refcount -= 1;
1259 if (auth->refcount == 0)
1261 shutdown_mech (auth);
1263 if (DBUS_AUTH_IS_CLIENT (auth))
1265 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1268 _dbus_string_free (&auth->incoming);
1269 _dbus_string_free (&auth->outgoing);
1275 * @param auth the auth conversation object
1276 * @returns #TRUE if we're in a final state
1278 #define DBUS_AUTH_IN_END_STATE(auth) ((auth)->need_disconnect || (auth)->authenticated)
1281 * Analyzes buffered input and moves the auth conversation forward,
1282 * returning the new state of the auth conversation.
1284 * @param auth the auth conversation
1285 * @returns the new state
1288 _dbus_auth_do_work (DBusAuth *auth)
1290 if (DBUS_AUTH_IN_END_STATE (auth))
1291 return get_state (auth);
1293 auth->needed_memory = FALSE;
1295 /* Max amount we'll buffer up before deciding someone's on crack */
1296 #define MAX_BUFFER (16 * 1024)
1300 if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
1301 _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
1303 auth->need_disconnect = TRUE;
1304 _dbus_verbose ("Disconnecting due to excessive data buffered in auth phase\n");
1308 if (auth->mech == NULL &&
1309 auth->already_got_mechanisms &&
1310 DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
1312 auth->need_disconnect = TRUE;
1313 _dbus_verbose ("Disconnecting because we are out of mechanisms to try using\n");
1317 while (process_command (auth));
1319 return get_state (auth);
1323 * Gets bytes that need to be sent to the peer we're conversing with.
1324 * After writing some bytes, _dbus_auth_bytes_sent() must be called
1325 * to notify the auth object that they were written.
1327 * @param auth the auth conversation
1328 * @param str return location for a ref to the buffer to send
1329 * @returns #FALSE if nothing to send
1332 _dbus_auth_get_bytes_to_send (DBusAuth *auth,
1333 const DBusString **str)
1335 _dbus_assert (auth != NULL);
1336 _dbus_assert (str != NULL);
1340 if (DBUS_AUTH_IN_END_STATE (auth))
1343 if (_dbus_string_get_length (&auth->outgoing) == 0)
1346 *str = &auth->outgoing;
1352 * Notifies the auth conversation object that
1353 * the given number of bytes of the outgoing buffer
1354 * have been written out.
1356 * @param auth the auth conversation
1357 * @param bytes_sent number of bytes written out
1360 _dbus_auth_bytes_sent (DBusAuth *auth,
1363 _dbus_string_delete (&auth->outgoing,
1366 if (auth->authenticated_pending_output &&
1367 _dbus_string_get_length (&auth->outgoing) == 0)
1368 auth->authenticated = TRUE;
1372 * Stores bytes received from the peer we're conversing with.
1374 * @param auth the auth conversation
1375 * @param str the received bytes.
1376 * @returns #FALSE if not enough memory to store the bytes.
1379 _dbus_auth_bytes_received (DBusAuth *auth,
1380 const DBusString *str)
1382 _dbus_assert (auth != NULL);
1383 _dbus_assert (str != NULL);
1385 if (DBUS_AUTH_IN_END_STATE (auth))
1388 auth->needed_memory = FALSE;
1390 if (!_dbus_string_copy (str, 0,
1392 _dbus_string_get_length (&auth->incoming)))
1394 auth->needed_memory = TRUE;
1398 _dbus_auth_do_work (auth);
1404 * Returns leftover bytes that were not used as part of the auth
1405 * conversation. These bytes will be part of the message stream
1406 * instead. This function may not be called until authentication has
1409 * @param auth the auth conversation
1410 * @param str string to append the unused bytes to
1411 * @returns #FALSE if not enough memory to return the bytes
1414 _dbus_auth_get_unused_bytes (DBusAuth *auth,
1417 if (!DBUS_AUTH_IN_END_STATE (auth))
1420 if (!_dbus_string_move (&auth->incoming,
1422 _dbus_string_get_length (str)))
1429 * Called post-authentication, indicates whether we need to encode
1430 * the message stream with _dbus_auth_encode_data() prior to
1431 * sending it to the peer.
1433 * @param auth the auth conversation
1434 * @returns #TRUE if we need to encode the stream
1437 _dbus_auth_needs_encoding (DBusAuth *auth)
1439 if (!auth->authenticated)
1442 if (auth->mech != NULL)
1444 if (DBUS_AUTH_IS_CLIENT (auth))
1445 return auth->mech->client_encode_func != NULL;
1447 return auth->mech->server_encode_func != NULL;
1454 * Called post-authentication, encodes a block of bytes for sending to
1455 * the peer. If no encoding was negotiated, just copies the bytes
1456 * (you can avoid this by checking _dbus_auth_needs_encoding()).
1458 * @param auth the auth conversation
1459 * @param plaintext the plain text data
1460 * @param encoded initialized string to where encoded data is appended
1461 * @returns #TRUE if we had enough memory and successfully encoded
1464 _dbus_auth_encode_data (DBusAuth *auth,
1465 const DBusString *plaintext,
1466 DBusString *encoded)
1468 _dbus_assert (plaintext != encoded);
1470 if (!auth->authenticated)
1473 if (_dbus_auth_needs_encoding (auth))
1475 if (DBUS_AUTH_IS_CLIENT (auth))
1476 return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
1478 return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
1482 return _dbus_string_copy (plaintext, 0, encoded,
1483 _dbus_string_get_length (encoded));
1488 * Called post-authentication, indicates whether we need to decode
1489 * the message stream with _dbus_auth_decode_data() after
1490 * receiving it from the peer.
1492 * @param auth the auth conversation
1493 * @returns #TRUE if we need to encode the stream
1496 _dbus_auth_needs_decoding (DBusAuth *auth)
1498 if (!auth->authenticated)
1501 if (auth->mech != NULL)
1503 if (DBUS_AUTH_IS_CLIENT (auth))
1504 return auth->mech->client_decode_func != NULL;
1506 return auth->mech->server_decode_func != NULL;
1514 * Called post-authentication, decodes a block of bytes received from
1515 * the peer. If no encoding was negotiated, just copies the bytes (you
1516 * can avoid this by checking _dbus_auth_needs_decoding()).
1518 * @todo We need to be able to distinguish "out of memory" error
1519 * from "the data is hosed" error.
1521 * @param auth the auth conversation
1522 * @param encoded the encoded data
1523 * @param plaintext initialized string where decoded data is appended
1524 * @returns #TRUE if we had enough memory and successfully decoded
1527 _dbus_auth_decode_data (DBusAuth *auth,
1528 const DBusString *encoded,
1529 DBusString *plaintext)
1531 _dbus_assert (plaintext != encoded);
1533 if (!auth->authenticated)
1536 if (_dbus_auth_needs_decoding (auth))
1538 if (DBUS_AUTH_IS_CLIENT (auth))
1539 return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
1541 return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
1545 return _dbus_string_copy (encoded, 0, plaintext,
1546 _dbus_string_get_length (plaintext));
1551 * Sets credentials received via reliable means from the operating
1554 * @param auth the auth conversation
1555 * @param credentials the credentials received
1558 _dbus_auth_set_credentials (DBusAuth *auth,
1559 const DBusCredentials *credentials)
1561 auth->credentials = *credentials;
1565 * Gets the identity we authorized the client as. Apps may have
1566 * different policies as to what identities they allow.
1568 * @param auth the auth conversation
1569 * @param credentials the credentials we've authorized
1572 _dbus_auth_get_identity (DBusAuth *auth,
1573 DBusCredentials *credentials)
1575 if (auth->authenticated)
1577 *credentials = auth->authorized_identity;
1581 credentials->pid = -1;
1582 credentials->uid = -1;
1583 credentials->gid = -1;