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))
718 _dbus_string_free (&mech);
719 _dbus_string_free (&base64_response);
720 _dbus_string_free (&decoded_response);
726 _dbus_string_free (&mech);
727 _dbus_string_free (&base64_response);
728 _dbus_string_free (&decoded_response);
734 process_cancel (DBusAuth *auth,
735 const DBusString *command,
736 const DBusString *args)
738 shutdown_mech (auth);
744 process_begin (DBusAuth *auth,
745 const DBusString *command,
746 const DBusString *args)
748 if (auth->authenticated_pending_begin)
749 auth->authenticated = TRUE;
752 auth->need_disconnect = TRUE; /* client trying to send data before auth,
755 shutdown_mech (auth);
762 process_data_server (DBusAuth *auth,
763 const DBusString *command,
764 const DBusString *args)
766 if (auth->mech != NULL)
770 if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
773 if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
775 _dbus_string_free (&decoded);
779 if (!(* auth->mech->server_data_func) (auth, &decoded))
781 _dbus_string_free (&decoded);
785 _dbus_string_free (&decoded);
789 if (!_dbus_string_append (&auth->outgoing,
790 "ERROR \"Not currently in an auth conversation\"\r\n"))
798 process_error_server (DBusAuth *auth,
799 const DBusString *command,
800 const DBusString *args)
806 /* return FALSE if no memory, TRUE if all OK */
808 get_word (const DBusString *str,
814 _dbus_string_skip_blank (str, *start, start);
815 _dbus_string_find_blank (str, *start, &i);
819 if (!_dbus_string_copy_len (str, *start, i, word, 0))
829 process_mechanisms (DBusAuth *auth,
830 const DBusString *command,
831 const DBusString *args)
836 if (auth->already_got_mechanisms)
839 len = _dbus_string_get_length (args);
845 const DBusAuthMechanismHandler *mech;
847 if (!_dbus_string_init (&m, _DBUS_INT_MAX))
850 if (!get_word (args, &next, &m))
853 mech = find_mech (&m);
857 /* FIXME right now we try mechanisms in the order
858 * the server lists them; should we do them in
859 * some more deterministic order?
861 * Probably in all_mechanisms order, our order of
862 * preference. Of course when the server is us,
863 * it lists things in that order anyhow.
866 _dbus_verbose ("Adding mechanism %s to list we will try\n",
869 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
877 _dbus_string_get_const_data (&m, &s);
878 _dbus_verbose ("Server offered mechanism \"%s\" that we don't know how to use\n",
882 _dbus_string_free (&m);
885 auth->already_got_mechanisms = TRUE;
890 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
896 client_try_next_mechanism (DBusAuth *auth)
898 const DBusAuthMechanismHandler *mech;
899 DBusString auth_command;
901 if (DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
904 mech = DBUS_AUTH_CLIENT (auth)->mechs_to_try->data;
906 if (!_dbus_string_init (&auth_command, _DBUS_INT_MAX))
909 if (!_dbus_string_append (&auth_command,
912 _dbus_string_free (&auth_command);
916 if (!_dbus_string_append (&auth_command,
919 _dbus_string_free (&auth_command);
923 if (mech->client_initial_response_func != NULL)
925 if (!_dbus_string_append (&auth_command, " "))
927 _dbus_string_free (&auth_command);
931 if (!(* mech->client_initial_response_func) (auth, &auth_command))
933 _dbus_string_free (&auth_command);
938 if (!_dbus_string_append (&auth_command,
941 _dbus_string_free (&auth_command);
945 if (!_dbus_string_copy (&auth_command, 0,
947 _dbus_string_get_length (&auth->outgoing)))
949 _dbus_string_free (&auth_command);
954 _dbus_list_pop_first (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
956 _dbus_verbose ("Trying mechanism %s\n",
957 auth->mech->mechanism);
963 process_rejected (DBusAuth *auth,
964 const DBusString *command,
965 const DBusString *args)
967 shutdown_mech (auth);
969 if (!auth->already_got_mechanisms)
971 /* Ask for mechanisms */
972 if (!_dbus_string_append (&auth->outgoing,
976 else if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
978 client_try_next_mechanism (auth);
983 auth->need_disconnect = TRUE;
990 process_ok (DBusAuth *auth,
991 const DBusString *command,
992 const DBusString *args)
994 if (!_dbus_string_append (&auth->outgoing,
998 auth->authenticated_pending_output = TRUE;
1005 process_data_client (DBusAuth *auth,
1006 const DBusString *command,
1007 const DBusString *args)
1009 if (auth->mech != NULL)
1013 if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
1016 if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
1018 _dbus_string_free (&decoded);
1022 if (!(* auth->mech->client_data_func) (auth, &decoded))
1024 _dbus_string_free (&decoded);
1028 _dbus_string_free (&decoded);
1032 if (!_dbus_string_append (&auth->outgoing,
1033 "ERROR \"Got DATA when not in an auth exchange\"\r\n"))
1041 process_error_client (DBusAuth *auth,
1042 const DBusString *command,
1043 const DBusString *args)
1049 process_unknown (DBusAuth *auth,
1050 const DBusString *command,
1051 const DBusString *args)
1053 if (!_dbus_string_append (&auth->outgoing,
1054 "ERROR \"Unknown command\"\r\n"))
1060 /* returns whether to call it again right away */
1062 process_command (DBusAuth *auth)
1073 if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
1076 if (!_dbus_string_init (&command, _DBUS_INT_MAX))
1078 auth->needed_memory = TRUE;
1082 if (!_dbus_string_init (&args, _DBUS_INT_MAX))
1084 auth->needed_memory = TRUE;
1088 if (eol > _DBUS_ONE_MEGABYTE)
1090 /* This is a giant line, someone is trying to hose us. */
1091 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command too long\"\r\n"))
1097 if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0))
1100 if (!_dbus_string_validate_ascii (&command, 0,
1101 _dbus_string_get_length (&command)))
1103 _dbus_verbose ("Command contained non-ASCII chars or embedded nul\n");
1104 if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command contained non-ASCII\"\r\n"))
1112 _dbus_string_get_const_data (&command, &q);
1113 _dbus_verbose ("got command \"%s\"\n", q);
1116 _dbus_string_find_blank (&command, 0, &i);
1117 _dbus_string_skip_blank (&command, i, &j);
1120 _dbus_string_delete (&command, i, j - i);
1122 if (!_dbus_string_move (&command, i, &args, 0))
1126 while (auth->handlers[i].command != NULL)
1128 if (_dbus_string_equal_c_str (&command,
1129 auth->handlers[i].command))
1131 _dbus_verbose ("Processing auth command %s\n",
1132 auth->handlers[i].command);
1134 if (!(* auth->handlers[i].func) (auth, &command, &args))
1142 if (auth->handlers[i].command == NULL)
1144 if (!process_unknown (auth, &command, &args))
1150 /* We've succeeded in processing the whole command so drop it out
1151 * of the incoming buffer and return TRUE to try another command.
1154 _dbus_string_delete (&auth->incoming, 0, eol);
1157 _dbus_string_delete (&auth->incoming, 0, 2);
1162 _dbus_string_free (&args);
1163 _dbus_string_free (&command);
1166 auth->needed_memory = TRUE;
1168 auth->needed_memory = FALSE;
1177 * @addtogroup DBusAuth
1182 * Creates a new auth conversation object for the server side.
1183 * See doc/dbus-sasl-profile.txt for full details on what
1186 * @returns the new object or #NULL if no memory
1189 _dbus_auth_server_new (void)
1193 auth = _dbus_auth_new (sizeof (DBusAuthServer));
1197 auth->handlers = server_handlers;
1203 * Creates a new auth conversation object for the client side.
1204 * See doc/dbus-sasl-profile.txt for full details on what
1207 * @returns the new object or #NULL if no memory
1210 _dbus_auth_client_new (void)
1214 auth = _dbus_auth_new (sizeof (DBusAuthClient));
1218 auth->handlers = client_handlers;
1220 /* Add a default mechanism to try */
1221 if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1222 (void*) &all_mechanisms[0]))
1224 _dbus_auth_unref (auth);
1228 /* Now try the mechanism we just added */
1229 if (!client_try_next_mechanism (auth))
1231 _dbus_auth_unref (auth);
1239 * Increments the refcount of an auth object.
1241 * @param auth the auth conversation
1244 _dbus_auth_ref (DBusAuth *auth)
1246 _dbus_assert (auth != NULL);
1248 auth->refcount += 1;
1252 * Decrements the refcount of an auth object.
1254 * @param auth the auth conversation
1257 _dbus_auth_unref (DBusAuth *auth)
1259 _dbus_assert (auth != NULL);
1260 _dbus_assert (auth->refcount > 0);
1262 auth->refcount -= 1;
1263 if (auth->refcount == 0)
1265 shutdown_mech (auth);
1267 if (DBUS_AUTH_IS_CLIENT (auth))
1269 _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1272 _dbus_string_free (&auth->identity);
1273 _dbus_string_free (&auth->incoming);
1274 _dbus_string_free (&auth->outgoing);
1280 * @param auth the auth conversation object
1281 * @returns #TRUE if we're in a final state
1283 #define DBUS_AUTH_IN_END_STATE(auth) ((auth)->need_disconnect || (auth)->authenticated)
1286 * Analyzes buffered input and moves the auth conversation forward,
1287 * returning the new state of the auth conversation.
1289 * @param auth the auth conversation
1290 * @returns the new state
1293 _dbus_auth_do_work (DBusAuth *auth)
1295 if (DBUS_AUTH_IN_END_STATE (auth))
1296 return get_state (auth);
1298 auth->needed_memory = FALSE;
1300 /* Max amount we'll buffer up before deciding someone's on crack */
1301 #define MAX_BUFFER (16 * 1024)
1305 if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
1306 _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
1308 auth->need_disconnect = TRUE;
1309 _dbus_verbose ("Disconnecting due to excessive data buffered in auth phase\n");
1313 if (auth->mech == NULL &&
1314 auth->already_got_mechanisms &&
1315 DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
1317 auth->need_disconnect = TRUE;
1318 _dbus_verbose ("Disconnecting because we are out of mechanisms to try using\n");
1322 while (process_command (auth));
1324 return get_state (auth);
1328 * Gets bytes that need to be sent to the peer we're conversing with.
1329 * After writing some bytes, _dbus_auth_bytes_sent() must be called
1330 * to notify the auth object that they were written.
1332 * @param auth the auth conversation
1333 * @param str return location for a ref to the buffer to send
1334 * @returns #FALSE if nothing to send
1337 _dbus_auth_get_bytes_to_send (DBusAuth *auth,
1338 const DBusString **str)
1340 _dbus_assert (auth != NULL);
1341 _dbus_assert (str != NULL);
1345 if (DBUS_AUTH_IN_END_STATE (auth))
1348 if (_dbus_string_get_length (&auth->outgoing) == 0)
1351 *str = &auth->outgoing;
1357 * Notifies the auth conversation object that
1358 * the given number of bytes of the outgoing buffer
1359 * have been written out.
1361 * @param auth the auth conversation
1362 * @param bytes_sent number of bytes written out
1365 _dbus_auth_bytes_sent (DBusAuth *auth,
1368 _dbus_string_delete (&auth->outgoing,
1371 if (auth->authenticated_pending_output &&
1372 _dbus_string_get_length (&auth->outgoing) == 0)
1373 auth->authenticated = TRUE;
1377 * Stores bytes received from the peer we're conversing with.
1379 * @param auth the auth conversation
1380 * @param str the received bytes.
1381 * @returns #FALSE if not enough memory to store the bytes.
1384 _dbus_auth_bytes_received (DBusAuth *auth,
1385 const DBusString *str)
1387 _dbus_assert (auth != NULL);
1388 _dbus_assert (str != NULL);
1390 if (DBUS_AUTH_IN_END_STATE (auth))
1393 auth->needed_memory = FALSE;
1395 if (!_dbus_string_copy (str, 0,
1397 _dbus_string_get_length (&auth->incoming)))
1399 auth->needed_memory = TRUE;
1403 _dbus_auth_do_work (auth);
1409 * Returns leftover bytes that were not used as part of the auth
1410 * conversation. These bytes will be part of the message stream
1411 * instead. This function may not be called until authentication has
1414 * @param auth the auth conversation
1415 * @param str string to append the unused bytes to
1416 * @returns #FALSE if not enough memory to return the bytes
1419 _dbus_auth_get_unused_bytes (DBusAuth *auth,
1422 if (!DBUS_AUTH_IN_END_STATE (auth))
1425 if (!_dbus_string_move (&auth->incoming,
1427 _dbus_string_get_length (str)))
1434 * Called post-authentication, indicates whether we need to encode
1435 * the message stream with _dbus_auth_encode_data() prior to
1436 * sending it to the peer.
1438 * @param auth the auth conversation
1439 * @returns #TRUE if we need to encode the stream
1442 _dbus_auth_needs_encoding (DBusAuth *auth)
1444 if (!auth->authenticated)
1447 if (auth->mech != NULL)
1449 if (DBUS_AUTH_IS_CLIENT (auth))
1450 return auth->mech->client_encode_func != NULL;
1452 return auth->mech->server_encode_func != NULL;
1459 * Called post-authentication, encodes a block of bytes for sending to
1460 * the peer. If no encoding was negotiated, just copies the bytes
1461 * (you can avoid this by checking _dbus_auth_needs_encoding()).
1463 * @param auth the auth conversation
1464 * @param plaintext the plain text data
1465 * @param encoded initialized string to where encoded data is appended
1466 * @returns #TRUE if we had enough memory and successfully encoded
1469 _dbus_auth_encode_data (DBusAuth *auth,
1470 const DBusString *plaintext,
1471 DBusString *encoded)
1473 _dbus_assert (plaintext != encoded);
1475 if (!auth->authenticated)
1478 if (_dbus_auth_needs_encoding (auth))
1480 if (DBUS_AUTH_IS_CLIENT (auth))
1481 return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
1483 return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
1487 return _dbus_string_copy (plaintext, 0, encoded,
1488 _dbus_string_get_length (encoded));
1493 * Called post-authentication, indicates whether we need to decode
1494 * the message stream with _dbus_auth_decode_data() after
1495 * receiving it from the peer.
1497 * @param auth the auth conversation
1498 * @returns #TRUE if we need to encode the stream
1501 _dbus_auth_needs_decoding (DBusAuth *auth)
1503 if (!auth->authenticated)
1506 if (auth->mech != NULL)
1508 if (DBUS_AUTH_IS_CLIENT (auth))
1509 return auth->mech->client_decode_func != NULL;
1511 return auth->mech->server_decode_func != NULL;
1519 * Called post-authentication, decodes a block of bytes received from
1520 * the peer. If no encoding was negotiated, just copies the bytes (you
1521 * can avoid this by checking _dbus_auth_needs_decoding()).
1523 * @todo We need to be able to distinguish "out of memory" error
1524 * from "the data is hosed" error.
1526 * @param auth the auth conversation
1527 * @param encoded the encoded data
1528 * @param plaintext initialized string where decoded data is appended
1529 * @returns #TRUE if we had enough memory and successfully decoded
1532 _dbus_auth_decode_data (DBusAuth *auth,
1533 const DBusString *encoded,
1534 DBusString *plaintext)
1536 _dbus_assert (plaintext != encoded);
1538 if (!auth->authenticated)
1541 if (_dbus_auth_needs_decoding (auth))
1543 if (DBUS_AUTH_IS_CLIENT (auth))
1544 return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
1546 return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
1550 return _dbus_string_copy (encoded, 0, plaintext,
1551 _dbus_string_get_length (plaintext));
1556 * Sets credentials received via reliable means from the operating
1559 * @param auth the auth conversation
1560 * @param credentials the credentials received
1563 _dbus_auth_set_credentials (DBusAuth *auth,
1564 const DBusCredentials *credentials)
1566 auth->credentials = *credentials;
1570 * Gets the identity we authorized the client as. Apps may have
1571 * different policies as to what identities they allow.
1573 * @param auth the auth conversation
1574 * @param credentials the credentials we've authorized
1577 _dbus_auth_get_identity (DBusAuth *auth,
1578 DBusCredentials *credentials)
1580 if (auth->authenticated)
1582 *credentials = auth->authorized_identity;
1586 credentials->pid = -1;
1587 credentials->uid = -1;
1588 credentials->gid = -1;