dbus-auth.c revision 3791dcca16cb46b0ff7305beff75d1aa2645940c
17252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström/* -*- mode: C; c-file-style: "gnu" -*- */
27252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström/* dbus-auth.c Authentication
37252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström *
47252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * Copyright (C) 2002, 2003 Red Hat Inc.
57252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström *
67252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * Licensed under the Academic Free License version 1.2
77252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström *
87252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * This program is free software; you can redistribute it and/or modify
97252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * it under the terms of the GNU General Public License as published by
107252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * the Free Software Foundation; either version 2 of the License, or
117252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * (at your option) any later version.
127252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström *
137252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * This program is distributed in the hope that it will be useful,
14415d2cd7454d93b3727fce9147090a24e4c3ccbaPeter Boström * but WITHOUT ANY WARRANTY; without even the implied warranty of
1571f6f4405c1c5f60097f8d10841378088e78e8b9Zeke Chin * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
167252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * GNU General Public License for more details.
177252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström *
187252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * You should have received a copy of the GNU General Public License
197252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * along with this program; if not, write to the Free Software
207252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
217252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström *
2271f6f4405c1c5f60097f8d10841378088e78e8b9Zeke Chin */
2391d6edef35e7275879c30ce16ecb8b6dc73c6e4ahenrikg#include "dbus-auth.h"
2471f6f4405c1c5f60097f8d10841378088e78e8b9Zeke Chin#include "dbus-string.h"
257252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström#include "dbus-list.h"
267252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström#include "dbus-internals.h"
277252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
287252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström/* See doc/dbus-sasl-profile.txt */
297252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
307252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström/**
317252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * @defgroup DBusAuth Authentication
327252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * @ingroup  DBusInternals
337252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * @brief DBusAuth object
347252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström *
357252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * DBusAuth manages the authentication negotiation when a connection
367252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * is first established, and also manage any encryption used over a
377252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * connection.
387252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström *
3971f6f4405c1c5f60097f8d10841378088e78e8b9Zeke Chin * The file doc/dbus-sasl-profile.txt documents the network protocol
4071f6f4405c1c5f60097f8d10841378088e78e8b9Zeke Chin * used for authentication.
417252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström */
427252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
437252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström/**
447252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * @defgroup DBusAuthInternals Authentication implementation details
457252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * @ingroup  DBusInternals
467252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * @brief DBusAuth implementation details
477252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström *
487252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * Private details of authentication code.
497252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström *
507252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * @{
517252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström */
527252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
537252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström/**
547252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * Processes a command. Returns whether we had enough memory to
557252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * complete the operation.
567252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström */
577252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boströmtypedef dbus_bool_t (* DBusProcessAuthCommandFunction) (DBusAuth         *auth,
587252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström                                                        const DBusString *command,
597252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström                                                        const DBusString *args);
607252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
617252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boströmtypedef struct
627252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström{
637252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  const char *command;
647252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusProcessAuthCommandFunction func;
657252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström} DBusAuthCommandHandler;
667252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
6791d6edef35e7275879c30ce16ecb8b6dc73c6e4ahenrikg/**
687252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * This function appends an initial client response to the given string
697252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström */
707252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boströmtypedef dbus_bool_t (* DBusInitialResponseFunction)  (DBusAuth         *auth,
717252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström                                                      DBusString       *response);
727252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
737252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström/**
747252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * This function processes a block of data received from the peer.
757252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * i.e. handles a DATA command.
767252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström */
777252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boströmtypedef dbus_bool_t (* DBusAuthDataFunction)     (DBusAuth         *auth,
787252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström                                                  const DBusString *data);
79b7d9a97ce41022e984348efb5f28bf6dd6c6b779Peter Boström
80b7d9a97ce41022e984348efb5f28bf6dd6c6b779Peter Boström/**
81b7d9a97ce41022e984348efb5f28bf6dd6c6b779Peter Boström * This function encodes a block of data from the peer.
827252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström */
837252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boströmtypedef dbus_bool_t (* DBusAuthEncodeFunction)   (DBusAuth         *auth,
847252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström                                                  const DBusString *data,
857252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström                                                  DBusString       *encoded);
867252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
877252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström/**
887252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * This function decodes a block of data from the peer.
897252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström */
907252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boströmtypedef dbus_bool_t (* DBusAuthDecodeFunction)   (DBusAuth         *auth,
917252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström                                                  const DBusString *data,
927252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström                                                  DBusString       *decoded);
9349e196af4060624d620297a6bc017699daa33550Peter Boström
947252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström/**
957252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * This function is called when the mechanism is abandoned.
967252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström */
977252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boströmtypedef void        (* DBusAuthShutdownFunction) (DBusAuth       *auth);
987252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
997252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boströmtypedef struct
1007252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström{
1017252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  const char *mechanism;
1027252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusAuthDataFunction server_data_func;
1037252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusAuthEncodeFunction server_encode_func;
1047252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusAuthDecodeFunction server_decode_func;
1057252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusAuthShutdownFunction server_shutdown_func;
1067252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusInitialResponseFunction client_initial_response_func;
1077252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusAuthDataFunction client_data_func;
1087252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusAuthEncodeFunction client_encode_func;
1097252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusAuthDecodeFunction client_decode_func;
1107252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusAuthShutdownFunction client_shutdown_func;
1117252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström} DBusAuthMechanismHandler;
1127252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
1137252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström/**
1147252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström * Internal members of DBusAuth.
1157252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström */
1167252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boströmstruct DBusAuth
1177252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström{
1187252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  int refcount;           /**< reference count */
1197252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
1207252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusString incoming;    /**< Incoming data buffer */
1217252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusString outgoing;    /**< Outgoing data buffer */
1227252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
1237252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  const DBusAuthCommandHandler *handlers; /**< Handlers for commands */
1247252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
1257252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  const DBusAuthMechanismHandler *mech;   /**< Current auth mechanism */
1267252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
1277252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusString identity;                   /**< Current identity we're authorizing
1287252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström                                          *   as.
1297252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström                                          */
1307252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
1317252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusCredentials credentials;      /**< Credentials, fields may be -1 */
1327252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
1337252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  DBusCredentials authorized_identity; /**< Credentials that are authorized */
1347252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
1357252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström  unsigned int needed_memory : 1;   /**< We needed memory to continue since last
1367252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström                                     * successful getting something done
137796cfaf7f76aa740cc7f4bb2c94f88637e475324perkj                                     */
138796cfaf7f76aa740cc7f4bb2c94f88637e475324perkj  unsigned int need_disconnect : 1; /**< We've given up, time to disconnect */
139796cfaf7f76aa740cc7f4bb2c94f88637e475324perkj  unsigned int authenticated : 1;   /**< We are authenticated */
140796cfaf7f76aa740cc7f4bb2c94f88637e475324perkj  unsigned int authenticated_pending_output : 1; /**< Authenticated once we clear outgoing buffer */
141796cfaf7f76aa740cc7f4bb2c94f88637e475324perkj  unsigned int authenticated_pending_begin : 1;  /**< Authenticated once we get BEGIN */
142796cfaf7f76aa740cc7f4bb2c94f88637e475324perkj  unsigned int already_got_mechanisms : 1;       /**< Client already got mech list */
143b7d9a97ce41022e984348efb5f28bf6dd6c6b779Peter Boström  unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */
144b7d9a97ce41022e984348efb5f28bf6dd6c6b779Peter Boström};
145b7d9a97ce41022e984348efb5f28bf6dd6c6b779Peter Boström
146b7d9a97ce41022e984348efb5f28bf6dd6c6b779Peter Boströmtypedef struct
147b7d9a97ce41022e984348efb5f28bf6dd6c6b779Peter Boström{
148b7d9a97ce41022e984348efb5f28bf6dd6c6b779Peter Boström  DBusAuth base;
1497252a2ba8035c4128917a9558a3e34fc9dbe7c44Peter Boström
150  DBusList *mechs_to_try; /**< Mechanisms we got from the server that we're going to try using */
151
152} DBusAuthClient;
153
154typedef struct
155{
156  DBusAuth base;
157
158  int failures;     /**< Number of times client has been rejected */
159  int max_failures; /**< Number of times we reject before disconnect */
160
161} DBusAuthServer;
162
163static dbus_bool_t process_auth         (DBusAuth         *auth,
164                                         const DBusString *command,
165                                         const DBusString *args);
166static dbus_bool_t process_cancel       (DBusAuth         *auth,
167                                         const DBusString *command,
168                                         const DBusString *args);
169static dbus_bool_t process_begin        (DBusAuth         *auth,
170                                         const DBusString *command,
171                                         const DBusString *args);
172static dbus_bool_t process_data_server  (DBusAuth         *auth,
173                                         const DBusString *command,
174                                         const DBusString *args);
175static dbus_bool_t process_error_server (DBusAuth         *auth,
176                                         const DBusString *command,
177                                         const DBusString *args);
178static dbus_bool_t process_rejected     (DBusAuth         *auth,
179                                         const DBusString *command,
180                                         const DBusString *args);
181static dbus_bool_t process_ok           (DBusAuth         *auth,
182                                         const DBusString *command,
183                                         const DBusString *args);
184static dbus_bool_t process_data_client  (DBusAuth         *auth,
185                                         const DBusString *command,
186                                         const DBusString *args);
187static dbus_bool_t process_error_client (DBusAuth         *auth,
188                                         const DBusString *command,
189                                         const DBusString *args);
190
191
192static dbus_bool_t client_try_next_mechanism (DBusAuth *auth);
193static dbus_bool_t send_rejected             (DBusAuth *auth);
194
195static DBusAuthCommandHandler
196server_handlers[] = {
197  { "AUTH", process_auth },
198  { "CANCEL", process_cancel },
199  { "BEGIN", process_begin },
200  { "DATA", process_data_server },
201  { "ERROR", process_error_server },
202  { NULL, NULL }
203};
204
205static DBusAuthCommandHandler
206client_handlers[] = {
207  { "REJECTED", process_rejected },
208  { "OK", process_ok },
209  { "DATA", process_data_client },
210  { "ERROR", process_error_client },
211  { NULL, NULL }
212};
213
214/**
215 * @param auth the auth conversation
216 * @returns #TRUE if the conversation is the server side
217 */
218#define DBUS_AUTH_IS_SERVER(auth) ((auth)->handlers == server_handlers)
219/**
220 * @param auth the auth conversation
221 * @returns #TRUE if the conversation is the client side
222 */
223#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->handlers == client_handlers)
224/**
225 * @param auth the auth conversation
226 * @returns auth cast to DBusAuthClient
227 */
228#define DBUS_AUTH_CLIENT(auth)    ((DBusAuthClient*)(auth))
229/**
230 * @param auth the auth conversation
231 * @returns auth cast to DBusAuthServer
232 */
233#define DBUS_AUTH_SERVER(auth)    ((DBusAuthServer*)(auth))
234
235static DBusAuth*
236_dbus_auth_new (int size)
237{
238  DBusAuth *auth;
239
240  auth = dbus_malloc0 (size);
241  if (auth == NULL)
242    return NULL;
243
244  auth->refcount = 1;
245
246  auth->credentials.pid = -1;
247  auth->credentials.uid = -1;
248  auth->credentials.gid = -1;
249
250  auth->authorized_identity.pid = -1;
251  auth->authorized_identity.uid = -1;
252  auth->authorized_identity.gid = -1;
253
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().
260   */
261
262  if (!_dbus_string_init (&auth->incoming, _DBUS_INT_MAX))
263    {
264      dbus_free (auth);
265      return NULL;
266    }
267
268  if (!_dbus_string_init (&auth->outgoing, _DBUS_INT_MAX))
269    {
270      _dbus_string_free (&auth->incoming);
271      dbus_free (auth);
272      return NULL;
273    }
274
275  if (!_dbus_string_init (&auth->identity, _DBUS_INT_MAX))
276    {
277      _dbus_string_free (&auth->incoming);
278      _dbus_string_free (&auth->outgoing);
279      dbus_free (auth);
280      return NULL;
281    }
282
283  return auth;
284}
285
286static DBusAuthState
287get_state (DBusAuth *auth)
288{
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;
293
294  if (auth->need_disconnect)
295    return DBUS_AUTH_STATE_NEED_DISCONNECT;
296  else if (auth->authenticated)
297    {
298      if (_dbus_string_get_length (&auth->incoming) > 0)
299        return DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES;
300      else
301        return DBUS_AUTH_STATE_AUTHENTICATED;
302    }
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;
307  else
308    return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
309}
310
311static void
312shutdown_mech (DBusAuth *auth)
313{
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;
322
323  if (auth->mech != NULL)
324    {
325      _dbus_verbose ("Shutting down mechanism %s\n",
326                     auth->mech->mechanism);
327
328      if (DBUS_AUTH_IS_CLIENT (auth))
329        (* auth->mech->client_shutdown_func) (auth);
330      else
331        (* auth->mech->server_shutdown_func) (auth);
332
333      auth->mech = NULL;
334    }
335}
336
337static dbus_bool_t
338handle_server_data_stupid_test_mech (DBusAuth         *auth,
339                                     const DBusString *data)
340{
341  if (!_dbus_string_append (&auth->outgoing,
342                            "OK\r\n"))
343    return FALSE;
344
345  auth->authenticated_pending_begin = TRUE;
346
347  return TRUE;
348}
349
350static void
351handle_server_shutdown_stupid_test_mech (DBusAuth *auth)
352{
353
354}
355
356static dbus_bool_t
357handle_client_data_stupid_test_mech (DBusAuth         *auth,
358                                     const DBusString *data)
359{
360
361  return TRUE;
362}
363
364static void
365handle_client_shutdown_stupid_test_mech (DBusAuth *auth)
366{
367
368}
369
370/* the stupid test mech is a base64-encoded string;
371 * all the inefficiency, none of the security!
372 */
373static dbus_bool_t
374handle_encode_stupid_test_mech (DBusAuth         *auth,
375                                const DBusString *plaintext,
376                                DBusString       *encoded)
377{
378  if (!_dbus_string_base64_encode (plaintext, 0, encoded,
379                                   _dbus_string_get_length (encoded)))
380    return FALSE;
381
382  return TRUE;
383}
384
385static dbus_bool_t
386handle_decode_stupid_test_mech (DBusAuth         *auth,
387                                const DBusString *encoded,
388                                DBusString       *plaintext)
389{
390  if (!_dbus_string_base64_decode (encoded, 0, plaintext,
391                                   _dbus_string_get_length (plaintext)))
392    return FALSE;
393
394  return TRUE;
395}
396
397static dbus_bool_t
398handle_server_data_external_mech (DBusAuth         *auth,
399                                  const DBusString *data)
400{
401  DBusCredentials desired_identity;
402
403  if (auth->credentials.uid < 0)
404    {
405      _dbus_verbose ("no credentials, mechanism EXTERNAL can't authenticate\n");
406      return send_rejected (auth);
407    }
408
409  if (_dbus_string_get_length (data) > 0)
410    {
411      if (_dbus_string_get_length (&auth->identity) > 0)
412        {
413          /* Tried to send two auth identities, wtf */
414          return send_rejected (auth);
415        }
416      else
417        {
418          /* this is our auth identity */
419          if (!_dbus_string_copy (data, 0, &auth->identity, 0))
420            return FALSE;
421        }
422    }
423
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)
427    {
428      if (_dbus_string_append (&auth->outgoing,
429                               "DATA\r\n"))
430        {
431          _dbus_verbose ("sending empty challenge asking client for auth identity\n");
432          auth->already_asked_for_initial_response = TRUE;
433          return TRUE;
434        }
435      else
436        return FALSE;
437    }
438
439  desired_identity.pid = -1;
440  desired_identity.uid = -1;
441  desired_identity.gid = -1;
442
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.
447   */
448  if (_dbus_string_get_length (&auth->identity) == 0)
449    {
450      desired_identity.uid = auth->credentials.uid;
451    }
452  else
453    {
454      if (!_dbus_credentials_from_uid_string (&auth->identity,
455                                              &desired_identity))
456        return send_rejected (auth);
457    }
458
459  if (desired_identity.uid < 0)
460    {
461      _dbus_verbose ("desired UID %d is no good\n", desired_identity.uid);
462      return send_rejected (auth);
463    }
464
465  if (_dbus_credentials_match (&auth->credentials,
466                               &desired_identity))
467    {
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);
472
473      if (!_dbus_string_append (&auth->outgoing,
474                                "OK\r\n"))
475        return FALSE;
476
477      auth->authorized_identity.uid = desired_identity.uid;
478
479      auth->authenticated_pending_begin = TRUE;
480
481      return TRUE;
482    }
483  else
484    {
485      return send_rejected (auth);
486    }
487}
488
489static void
490handle_server_shutdown_external_mech (DBusAuth *auth)
491{
492
493}
494
495static dbus_bool_t
496handle_client_initial_response_external_mech (DBusAuth         *auth,
497                                              DBusString       *response)
498{
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.
503   */
504  DBusString plaintext;
505
506  if (!_dbus_string_init (&plaintext, _DBUS_INT_MAX))
507    return FALSE;
508
509  if (!_dbus_string_append_our_uid (&plaintext))
510    goto failed;
511
512  if (!_dbus_string_base64_encode (&plaintext, 0,
513                                   response,
514                                   _dbus_string_get_length (response)))
515    goto failed;
516
517  _dbus_string_free (&plaintext);
518
519  return TRUE;
520
521 failed:
522  _dbus_string_free (&plaintext);
523  return FALSE;
524}
525
526static dbus_bool_t
527handle_client_data_external_mech (DBusAuth         *auth,
528                                  const DBusString *data)
529{
530
531  return TRUE;
532}
533
534static void
535handle_client_shutdown_external_mech (DBusAuth *auth)
536{
537
538}
539
540/* Put mechanisms here in order of preference.
541 * What I eventually want to have is:
542 *
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.
547 *
548 */
549static const DBusAuthMechanismHandler
550all_mechanisms[] = {
551  { "EXTERNAL",
552    handle_server_data_external_mech,
553    NULL, NULL,
554    handle_server_shutdown_external_mech,
555    handle_client_initial_response_external_mech,
556    handle_client_data_external_mech,
557    NULL, NULL,
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,
565    NULL,
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 },
570  { NULL, NULL }
571};
572
573static const DBusAuthMechanismHandler*
574find_mech (const DBusString *name)
575{
576  int i;
577
578  i = 0;
579  while (all_mechanisms[i].mechanism != NULL)
580    {
581      if (_dbus_string_equal_c_str (name,
582                                    all_mechanisms[i].mechanism))
583
584        return &all_mechanisms[i];
585
586      ++i;
587    }
588
589  return NULL;
590}
591
592static dbus_bool_t
593send_rejected (DBusAuth *auth)
594{
595  DBusString command;
596  DBusAuthServer *server_auth;
597  int i;
598
599  if (!_dbus_string_init (&command, _DBUS_INT_MAX))
600    return FALSE;
601
602  if (!_dbus_string_append (&command,
603                            "REJECTED"))
604    goto nomem;
605
606  i = 0;
607  while (all_mechanisms[i].mechanism != NULL)
608    {
609      if (!_dbus_string_append (&command,
610                                " "))
611        goto nomem;
612
613      if (!_dbus_string_append (&command,
614                                all_mechanisms[i].mechanism))
615        goto nomem;
616
617      ++i;
618    }
619
620  if (!_dbus_string_append (&command, "\r\n"))
621    goto nomem;
622
623  if (!_dbus_string_copy (&command, 0, &auth->outgoing,
624                          _dbus_string_get_length (&auth->outgoing)))
625    goto nomem;
626
627  _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
628  server_auth = DBUS_AUTH_SERVER (auth);
629  server_auth->failures += 1;
630
631  return TRUE;
632
633 nomem:
634  _dbus_string_free (&command);
635  return FALSE;
636}
637
638static dbus_bool_t
639process_auth (DBusAuth         *auth,
640              const DBusString *command,
641              const DBusString *args)
642{
643  if (auth->mech)
644    {
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"))
648        return FALSE;
649
650      return TRUE;
651    }
652  else if (_dbus_string_get_length (args) == 0)
653    {
654      /* No args to the auth, send mechanisms */
655      if (!send_rejected (auth))
656        return FALSE;
657
658      return TRUE;
659    }
660  else
661    {
662      int i;
663      DBusString mech;
664      DBusString base64_response;
665      DBusString decoded_response;
666
667      _dbus_string_find_blank (args, 0, &i);
668
669      if (!_dbus_string_init (&mech, _DBUS_INT_MAX))
670        return FALSE;
671
672      if (!_dbus_string_init (&base64_response, _DBUS_INT_MAX))
673        {
674          _dbus_string_free (&mech);
675          return FALSE;
676        }
677
678      if (!_dbus_string_init (&decoded_response, _DBUS_INT_MAX))
679        {
680          _dbus_string_free (&mech);
681          _dbus_string_free (&base64_response);
682          return FALSE;
683        }
684
685      if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
686        goto failed;
687
688      if (!_dbus_string_copy (args, i, &base64_response, 0))
689        goto failed;
690
691      if (!_dbus_string_base64_decode (&base64_response, 0,
692                                       &decoded_response, 0))
693        goto failed;
694
695      auth->mech = find_mech (&mech);
696      if (auth->mech != NULL)
697        {
698          _dbus_verbose ("Trying mechanism %s with initial response of %d bytes\n",
699                         auth->mech->mechanism,
700                         _dbus_string_get_length (&decoded_response));
701
702          if (!(* auth->mech->server_data_func) (auth,
703                                                 &decoded_response))
704            goto failed;
705        }
706      else
707        {
708          /* Unsupported mechanism */
709          if (!send_rejected (auth))
710            return FALSE;
711        }
712
713      _dbus_string_free (&mech);
714      _dbus_string_free (&base64_response);
715      _dbus_string_free (&decoded_response);
716
717      return TRUE;
718
719    failed:
720      auth->mech = NULL;
721      _dbus_string_free (&mech);
722      _dbus_string_free (&base64_response);
723      _dbus_string_free (&decoded_response);
724      return FALSE;
725    }
726}
727
728static dbus_bool_t
729process_cancel (DBusAuth         *auth,
730                const DBusString *command,
731                const DBusString *args)
732{
733  shutdown_mech (auth);
734
735  return TRUE;
736}
737
738static dbus_bool_t
739process_begin (DBusAuth         *auth,
740               const DBusString *command,
741               const DBusString *args)
742{
743  if (auth->authenticated_pending_begin)
744    auth->authenticated = TRUE;
745  else
746    {
747      auth->need_disconnect = TRUE; /* client trying to send data before auth,
748                                     * kick it
749                                     */
750      shutdown_mech (auth);
751    }
752
753  return TRUE;
754}
755
756static dbus_bool_t
757process_data_server (DBusAuth         *auth,
758                     const DBusString *command,
759                     const DBusString *args)
760{
761  if (auth->mech != NULL)
762    {
763      DBusString decoded;
764
765      if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
766        return FALSE;
767
768      if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
769        {
770          _dbus_string_free (&decoded);
771          return FALSE;
772        }
773
774      if (!(* auth->mech->server_data_func) (auth, &decoded))
775        {
776          _dbus_string_free (&decoded);
777          return FALSE;
778        }
779
780      _dbus_string_free (&decoded);
781    }
782  else
783    {
784      if (!_dbus_string_append (&auth->outgoing,
785                                "ERROR \"Not currently in an auth conversation\"\r\n"))
786        return FALSE;
787    }
788
789  return TRUE;
790}
791
792static dbus_bool_t
793process_error_server (DBusAuth         *auth,
794                      const DBusString *command,
795                      const DBusString *args)
796{
797
798  return TRUE;
799}
800
801/* return FALSE if no memory, TRUE if all OK */
802static dbus_bool_t
803get_word (const DBusString *str,
804          int              *start,
805          DBusString       *word)
806{
807  int i;
808
809  _dbus_string_skip_blank (str, *start, start);
810  _dbus_string_find_blank (str, *start, &i);
811
812  if (i > *start)
813    {
814      if (!_dbus_string_copy_len (str, *start, i, word, 0))
815        return FALSE;
816
817      *start = i;
818    }
819
820  return TRUE;
821}
822
823static dbus_bool_t
824record_mechanisms (DBusAuth         *auth,
825                   const DBusString *command,
826                   const DBusString *args)
827{
828  int next;
829  int len;
830
831  if (auth->already_got_mechanisms)
832    return TRUE;
833
834  len = _dbus_string_get_length (args);
835
836  next = 0;
837  while (next < len)
838    {
839      DBusString m;
840      const DBusAuthMechanismHandler *mech;
841
842      if (!_dbus_string_init (&m, _DBUS_INT_MAX))
843        goto nomem;
844
845      if (!get_word (args, &next, &m))
846        goto nomem;
847
848      mech = find_mech (&m);
849
850      if (mech != NULL)
851        {
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?
855           *
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.
859           */
860
861          _dbus_verbose ("Adding mechanism %s to list we will try\n",
862                         mech->mechanism);
863
864          if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
865                                  (void*) mech))
866            goto nomem;
867        }
868      else
869        {
870          const char *s;
871
872          _dbus_string_get_const_data (&m, &s);
873          _dbus_verbose ("Server offered mechanism \"%s\" that we don't know how to use\n",
874                         s);
875        }
876
877      _dbus_string_free (&m);
878    }
879
880  auth->already_got_mechanisms = TRUE;
881
882  return TRUE;
883
884 nomem:
885  _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
886
887  return FALSE;
888}
889
890static dbus_bool_t
891client_try_next_mechanism (DBusAuth *auth)
892{
893  const DBusAuthMechanismHandler *mech;
894  DBusString auth_command;
895
896  if (DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
897    return FALSE;
898
899  mech = DBUS_AUTH_CLIENT (auth)->mechs_to_try->data;
900
901  if (!_dbus_string_init (&auth_command, _DBUS_INT_MAX))
902    return FALSE;
903
904  if (!_dbus_string_append (&auth_command,
905                            "AUTH "))
906    {
907      _dbus_string_free (&auth_command);
908      return FALSE;
909    }
910
911  if (!_dbus_string_append (&auth_command,
912                            mech->mechanism))
913    {
914      _dbus_string_free (&auth_command);
915      return FALSE;
916    }
917
918  if (mech->client_initial_response_func != NULL)
919    {
920      if (!_dbus_string_append (&auth_command, " "))
921        {
922          _dbus_string_free (&auth_command);
923          return FALSE;
924        }
925
926      if (!(* mech->client_initial_response_func) (auth, &auth_command))
927        {
928          _dbus_string_free (&auth_command);
929          return FALSE;
930        }
931    }
932
933  if (!_dbus_string_append (&auth_command,
934                            "\r\n"))
935    {
936      _dbus_string_free (&auth_command);
937      return FALSE;
938    }
939
940  if (!_dbus_string_copy (&auth_command, 0,
941                          &auth->outgoing,
942                          _dbus_string_get_length (&auth->outgoing)))
943    {
944      _dbus_string_free (&auth_command);
945      return FALSE;
946    }
947
948  auth->mech = mech;
949  _dbus_list_pop_first (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
950
951  _dbus_verbose ("Trying mechanism %s\n",
952                 auth->mech->mechanism);
953
954  return TRUE;
955}
956
957static dbus_bool_t
958process_rejected (DBusAuth         *auth,
959                  const DBusString *command,
960                  const DBusString *args)
961{
962  shutdown_mech (auth);
963
964  if (!auth->already_got_mechanisms)
965    {
966      if (!record_mechanisms (auth, command, args))
967        return FALSE;
968    }
969
970  if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
971    {
972      client_try_next_mechanism (auth);
973    }
974  else
975    {
976      /* Give up */
977      auth->need_disconnect = TRUE;
978    }
979
980  return TRUE;
981}
982
983static dbus_bool_t
984process_ok (DBusAuth         *auth,
985            const DBusString *command,
986            const DBusString *args)
987{
988  if (!_dbus_string_append (&auth->outgoing,
989                            "BEGIN\r\n"))
990    return FALSE;
991
992  auth->authenticated_pending_output = TRUE;
993
994  return TRUE;
995}
996
997
998static dbus_bool_t
999process_data_client (DBusAuth         *auth,
1000                     const DBusString *command,
1001                     const DBusString *args)
1002{
1003  if (auth->mech != NULL)
1004    {
1005      DBusString decoded;
1006
1007      if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
1008        return FALSE;
1009
1010      if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
1011        {
1012          _dbus_string_free (&decoded);
1013          return FALSE;
1014        }
1015
1016      if (!(* auth->mech->client_data_func) (auth, &decoded))
1017        {
1018          _dbus_string_free (&decoded);
1019          return FALSE;
1020        }
1021
1022      _dbus_string_free (&decoded);
1023    }
1024  else
1025    {
1026      if (!_dbus_string_append (&auth->outgoing,
1027                                "ERROR \"Got DATA when not in an auth exchange\"\r\n"))
1028        return FALSE;
1029    }
1030
1031  return TRUE;
1032}
1033
1034static dbus_bool_t
1035process_error_client (DBusAuth         *auth,
1036                      const DBusString *command,
1037                      const DBusString *args)
1038{
1039  return TRUE;
1040}
1041
1042static dbus_bool_t
1043process_unknown (DBusAuth         *auth,
1044                 const DBusString *command,
1045                 const DBusString *args)
1046{
1047  if (!_dbus_string_append (&auth->outgoing,
1048                            "ERROR \"Unknown command\"\r\n"))
1049    return FALSE;
1050
1051  return TRUE;
1052}
1053
1054/* returns whether to call it again right away */
1055static dbus_bool_t
1056process_command (DBusAuth *auth)
1057{
1058  DBusString command;
1059  DBusString args;
1060  int eol;
1061  int i, j;
1062  dbus_bool_t retval;
1063
1064  retval = FALSE;
1065
1066  eol = 0;
1067  if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
1068    return FALSE;
1069
1070  if (!_dbus_string_init (&command, _DBUS_INT_MAX))
1071    {
1072      auth->needed_memory = TRUE;
1073      return FALSE;
1074    }
1075
1076  if (!_dbus_string_init (&args, _DBUS_INT_MAX))
1077    {
1078      auth->needed_memory = TRUE;
1079      return FALSE;
1080    }
1081
1082  if (eol > _DBUS_ONE_MEGABYTE)
1083    {
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"))
1086        goto out;
1087      else
1088        goto next_command;
1089    }
1090
1091  if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0))
1092    goto out;
1093
1094  if (!_dbus_string_validate_ascii (&command, 0,
1095                                    _dbus_string_get_length (&command)))
1096    {
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"))
1099        goto out;
1100      else
1101        goto next_command;
1102    }
1103
1104  {
1105    const char *q;
1106    _dbus_string_get_const_data (&command, &q);
1107    _dbus_verbose ("got command \"%s\"\n", q);
1108  }
1109
1110  _dbus_string_find_blank (&command, 0, &i);
1111  _dbus_string_skip_blank (&command, i, &j);
1112
1113  if (j > i)
1114    _dbus_string_delete (&command, i, j - i);
1115
1116  if (!_dbus_string_move (&command, i, &args, 0))
1117    goto out;
1118
1119  i = 0;
1120  while (auth->handlers[i].command != NULL)
1121    {
1122      if (_dbus_string_equal_c_str (&command,
1123                                    auth->handlers[i].command))
1124        {
1125          _dbus_verbose ("Processing auth command %s\n",
1126                         auth->handlers[i].command);
1127
1128          if (!(* auth->handlers[i].func) (auth, &command, &args))
1129            goto out;
1130
1131          break;
1132        }
1133      ++i;
1134    }
1135
1136  if (auth->handlers[i].command == NULL)
1137    {
1138      if (!process_unknown (auth, &command, &args))
1139        goto out;
1140    }
1141
1142 next_command:
1143
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.
1146   */
1147
1148  _dbus_string_delete (&auth->incoming, 0, eol);
1149
1150  /* kill the \r\n */
1151  _dbus_string_delete (&auth->incoming, 0, 2);
1152
1153  retval = TRUE;
1154
1155 out:
1156  _dbus_string_free (&args);
1157  _dbus_string_free (&command);
1158
1159  if (!retval)
1160    auth->needed_memory = TRUE;
1161  else
1162    auth->needed_memory = FALSE;
1163
1164  return retval;
1165}
1166
1167
1168/** @} */
1169
1170/**
1171 * @addtogroup DBusAuth
1172 * @{
1173 */
1174
1175/**
1176 * Creates a new auth conversation object for the server side.
1177 * See doc/dbus-sasl-profile.txt for full details on what
1178 * this object does.
1179 *
1180 * @returns the new object or #NULL if no memory
1181 */
1182DBusAuth*
1183_dbus_auth_server_new (void)
1184{
1185  DBusAuth *auth;
1186  DBusAuthServer *server_auth;
1187
1188  auth = _dbus_auth_new (sizeof (DBusAuthServer));
1189  if (auth == NULL)
1190    return NULL;
1191
1192  auth->handlers = server_handlers;
1193
1194  server_auth = DBUS_AUTH_SERVER (auth);
1195
1196  /* perhaps this should be per-mechanism with a lower
1197   * max
1198   */
1199  server_auth->failures = 0;
1200  server_auth->max_failures = 6;
1201
1202  return auth;
1203}
1204
1205/**
1206 * Creates a new auth conversation object for the client side.
1207 * See doc/dbus-sasl-profile.txt for full details on what
1208 * this object does.
1209 *
1210 * @returns the new object or #NULL if no memory
1211 */
1212DBusAuth*
1213_dbus_auth_client_new (void)
1214{
1215  DBusAuth *auth;
1216
1217  auth = _dbus_auth_new (sizeof (DBusAuthClient));
1218  if (auth == NULL)
1219    return NULL;
1220
1221  auth->handlers = client_handlers;
1222
1223  /* Add a default mechanism to try */
1224  if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1225                          (void*) &all_mechanisms[0]))
1226    {
1227      _dbus_auth_unref (auth);
1228      return NULL;
1229    }
1230
1231  /* Now try the mechanism we just added */
1232  if (!client_try_next_mechanism (auth))
1233    {
1234      _dbus_auth_unref (auth);
1235      return NULL;
1236    }
1237
1238  return auth;
1239}
1240
1241/**
1242 * Increments the refcount of an auth object.
1243 *
1244 * @param auth the auth conversation
1245 */
1246void
1247_dbus_auth_ref (DBusAuth *auth)
1248{
1249  _dbus_assert (auth != NULL);
1250
1251  auth->refcount += 1;
1252}
1253
1254/**
1255 * Decrements the refcount of an auth object.
1256 *
1257 * @param auth the auth conversation
1258 */
1259void
1260_dbus_auth_unref (DBusAuth *auth)
1261{
1262  _dbus_assert (auth != NULL);
1263  _dbus_assert (auth->refcount > 0);
1264
1265  auth->refcount -= 1;
1266  if (auth->refcount == 0)
1267    {
1268      shutdown_mech (auth);
1269
1270      if (DBUS_AUTH_IS_CLIENT (auth))
1271        {
1272          _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1273        }
1274
1275      _dbus_string_free (&auth->identity);
1276      _dbus_string_free (&auth->incoming);
1277      _dbus_string_free (&auth->outgoing);
1278      dbus_free (auth);
1279    }
1280}
1281
1282/**
1283 * @param auth the auth conversation object
1284 * @returns #TRUE if we're in a final state
1285 */
1286#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->need_disconnect || (auth)->authenticated)
1287
1288/**
1289 * Analyzes buffered input and moves the auth conversation forward,
1290 * returning the new state of the auth conversation.
1291 *
1292 * @param auth the auth conversation
1293 * @returns the new state
1294 */
1295DBusAuthState
1296_dbus_auth_do_work (DBusAuth *auth)
1297{
1298  if (DBUS_AUTH_IN_END_STATE (auth))
1299    return get_state (auth);
1300
1301  auth->needed_memory = FALSE;
1302
1303  /* Max amount we'll buffer up before deciding someone's on crack */
1304#define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE)
1305
1306  do
1307    {
1308      if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
1309          _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
1310        {
1311          auth->need_disconnect = TRUE;
1312          _dbus_verbose ("Disconnecting due to excessive data buffered in auth phase\n");
1313          break;
1314        }
1315
1316      if (auth->mech == NULL &&
1317          auth->already_got_mechanisms &&
1318          DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
1319        {
1320          auth->need_disconnect = TRUE;
1321          _dbus_verbose ("Disconnecting because we are out of mechanisms to try using\n");
1322          break;
1323        }
1324    }
1325  while (process_command (auth));
1326
1327  return get_state (auth);
1328}
1329
1330/**
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.
1334 *
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
1338 */
1339dbus_bool_t
1340_dbus_auth_get_bytes_to_send (DBusAuth          *auth,
1341                              const DBusString **str)
1342{
1343  _dbus_assert (auth != NULL);
1344  _dbus_assert (str != NULL);
1345
1346  *str = NULL;
1347
1348  if (DBUS_AUTH_IN_END_STATE (auth))
1349    return FALSE;
1350
1351  if (_dbus_string_get_length (&auth->outgoing) == 0)
1352    return FALSE;
1353
1354  *str = &auth->outgoing;
1355
1356  return TRUE;
1357}
1358
1359/**
1360 * Notifies the auth conversation object that
1361 * the given number of bytes of the outgoing buffer
1362 * have been written out.
1363 *
1364 * @param auth the auth conversation
1365 * @param bytes_sent number of bytes written out
1366 */
1367void
1368_dbus_auth_bytes_sent (DBusAuth *auth,
1369                       int       bytes_sent)
1370{
1371  _dbus_string_delete (&auth->outgoing,
1372                       0, bytes_sent);
1373
1374  if (auth->authenticated_pending_output &&
1375      _dbus_string_get_length (&auth->outgoing) == 0)
1376    auth->authenticated = TRUE;
1377}
1378
1379/**
1380 * Stores bytes received from the peer we're conversing with.
1381 *
1382 * @param auth the auth conversation
1383 * @param str the received bytes.
1384 * @returns #FALSE if not enough memory to store the bytes.
1385 */
1386dbus_bool_t
1387_dbus_auth_bytes_received (DBusAuth   *auth,
1388                           const DBusString *str)
1389{
1390  _dbus_assert (auth != NULL);
1391  _dbus_assert (str != NULL);
1392
1393  if (DBUS_AUTH_IN_END_STATE (auth))
1394    return FALSE;
1395
1396  auth->needed_memory = FALSE;
1397
1398  if (!_dbus_string_copy (str, 0,
1399                          &auth->incoming,
1400                          _dbus_string_get_length (&auth->incoming)))
1401    {
1402      auth->needed_memory = TRUE;
1403      return FALSE;
1404    }
1405
1406  _dbus_auth_do_work (auth);
1407
1408  return TRUE;
1409}
1410
1411/**
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
1415 * succeeded.
1416 *
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
1420 */
1421dbus_bool_t
1422_dbus_auth_get_unused_bytes (DBusAuth   *auth,
1423                             DBusString *str)
1424{
1425  if (!DBUS_AUTH_IN_END_STATE (auth))
1426    return FALSE;
1427
1428  if (!_dbus_string_move (&auth->incoming,
1429                          0, str,
1430                          _dbus_string_get_length (str)))
1431    return FALSE;
1432
1433  return TRUE;
1434}
1435
1436/**
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.
1440 *
1441 * @param auth the auth conversation
1442 * @returns #TRUE if we need to encode the stream
1443 */
1444dbus_bool_t
1445_dbus_auth_needs_encoding (DBusAuth *auth)
1446{
1447  if (!auth->authenticated)
1448    return FALSE;
1449
1450  if (auth->mech != NULL)
1451    {
1452      if (DBUS_AUTH_IS_CLIENT (auth))
1453        return auth->mech->client_encode_func != NULL;
1454      else
1455        return auth->mech->server_encode_func != NULL;
1456    }
1457  else
1458    return FALSE;
1459}
1460
1461/**
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()).
1465 *
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
1470 */
1471dbus_bool_t
1472_dbus_auth_encode_data (DBusAuth         *auth,
1473                        const DBusString *plaintext,
1474                        DBusString       *encoded)
1475{
1476  _dbus_assert (plaintext != encoded);
1477
1478  if (!auth->authenticated)
1479    return FALSE;
1480
1481  if (_dbus_auth_needs_encoding (auth))
1482    {
1483      if (DBUS_AUTH_IS_CLIENT (auth))
1484        return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
1485      else
1486        return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
1487    }
1488  else
1489    {
1490      return _dbus_string_copy (plaintext, 0, encoded,
1491                                _dbus_string_get_length (encoded));
1492    }
1493}
1494
1495/**
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.
1499 *
1500 * @param auth the auth conversation
1501 * @returns #TRUE if we need to encode the stream
1502 */
1503dbus_bool_t
1504_dbus_auth_needs_decoding (DBusAuth *auth)
1505{
1506  if (!auth->authenticated)
1507    return FALSE;
1508
1509  if (auth->mech != NULL)
1510    {
1511      if (DBUS_AUTH_IS_CLIENT (auth))
1512        return auth->mech->client_decode_func != NULL;
1513      else
1514        return auth->mech->server_decode_func != NULL;
1515    }
1516  else
1517    return FALSE;
1518}
1519
1520
1521/**
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()).
1525 *
1526 * @todo We need to be able to distinguish "out of memory" error
1527 * from "the data is hosed" error.
1528 *
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
1533 */
1534dbus_bool_t
1535_dbus_auth_decode_data (DBusAuth         *auth,
1536                        const DBusString *encoded,
1537                        DBusString       *plaintext)
1538{
1539  _dbus_assert (plaintext != encoded);
1540
1541  if (!auth->authenticated)
1542    return FALSE;
1543
1544  if (_dbus_auth_needs_decoding (auth))
1545    {
1546      if (DBUS_AUTH_IS_CLIENT (auth))
1547        return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
1548      else
1549        return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
1550    }
1551  else
1552    {
1553      return _dbus_string_copy (encoded, 0, plaintext,
1554                                _dbus_string_get_length (plaintext));
1555    }
1556}
1557
1558/**
1559 * Sets credentials received via reliable means from the operating
1560 * system.
1561 *
1562 * @param auth the auth conversation
1563 * @param credentials the credentials received
1564 */
1565void
1566_dbus_auth_set_credentials (DBusAuth               *auth,
1567                            const DBusCredentials  *credentials)
1568{
1569  auth->credentials = *credentials;
1570}
1571
1572/**
1573 * Gets the identity we authorized the client as.  Apps may have
1574 * different policies as to what identities they allow.
1575 *
1576 * @param auth the auth conversation
1577 * @param credentials the credentials we've authorized
1578 */
1579void
1580_dbus_auth_get_identity (DBusAuth               *auth,
1581                         DBusCredentials        *credentials)
1582{
1583  if (auth->authenticated)
1584    {
1585      *credentials = auth->authorized_identity;
1586    }
1587  else
1588    {
1589      credentials->pid = -1;
1590      credentials->uid = -1;
1591      credentials->gid = -1;
1592    }
1593}
1594
1595/** @} */
1596
1597#ifdef DBUS_BUILD_TESTS
1598#include "dbus-test.h"
1599#include <stdio.h>
1600
1601dbus_bool_t
1602_dbus_auth_test (const char *test_data_dir)
1603{
1604
1605
1606  return TRUE;
1607}
1608
1609#endif /* DBUS_BUILD_TESTS */
1610