dbus-auth.c revision e7c0d217795f4e8eb618f82b9b3e52807436c8f1
1/* -*- mode: C; c-file-style: "gnu" -*- */
2/* dbus-auth.c Authentication
3 *
4 * Copyright (C) 2002, 2003, 2004 Red Hat Inc.
5 *
6 * Licensed under the Academic Free License version 2.1
7 *
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.
12 *
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.
17 *
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
21 *
22 */
23#include "dbus-auth.h"
24#include "dbus-string.h"
25#include "dbus-list.h"
26#include "dbus-internals.h"
27#include "dbus-keyring.h"
28#include "dbus-sha.h"
29#include "dbus-protocol.h"
30#include "dbus-credentials.h"
31
32/**
33 * @defgroup DBusAuth Authentication
34 * @ingroup  DBusInternals
35 * @brief DBusAuth object
36 *
37 * DBusAuth manages the authentication negotiation when a connection
38 * is first established, and also manage any encryption used over a
39 * connection.
40 *
41 * @todo some SASL profiles require sending the empty string as a
42 * challenge/response, but we don't currently allow that in our
43 * protocol.
44 *
45 * @todo right now sometimes both ends will block waiting for input
46 * from the other end, e.g. if there's an error during
47 * DBUS_COOKIE_SHA1.
48 *
49 * @todo the cookie keyring needs to be cached globally not just
50 * per-auth (which raises threadsafety issues too)
51 *
52 * @todo grep FIXME in dbus-auth.c
53 */
54
55/**
56 * @defgroup DBusAuthInternals Authentication implementation details
57 * @ingroup  DBusInternals
58 * @brief DBusAuth implementation details
59 *
60 * Private details of authentication code.
61 *
62 * @{
63 */
64
65/**
66 * This function appends an initial client response to the given string
67 */
68typedef dbus_bool_t (* DBusInitialResponseFunction)  (DBusAuth         *auth,
69                                                      DBusString       *response);
70
71/**
72 * This function processes a block of data received from the peer.
73 * i.e. handles a DATA command.
74 */
75typedef dbus_bool_t (* DBusAuthDataFunction)     (DBusAuth         *auth,
76                                                  const DBusString *data);
77
78/**
79 * This function encodes a block of data from the peer.
80 */
81typedef dbus_bool_t (* DBusAuthEncodeFunction)   (DBusAuth         *auth,
82                                                  const DBusString *data,
83                                                  DBusString       *encoded);
84
85/**
86 * This function decodes a block of data from the peer.
87 */
88typedef dbus_bool_t (* DBusAuthDecodeFunction)   (DBusAuth         *auth,
89                                                  const DBusString *data,
90                                                  DBusString       *decoded);
91
92/**
93 * This function is called when the mechanism is abandoned.
94 */
95typedef void        (* DBusAuthShutdownFunction) (DBusAuth       *auth);
96
97/**
98 * Virtual table representing a particular auth mechanism.
99 */
100typedef struct
101{
102  const char *mechanism; /**< Name of the mechanism */
103  DBusAuthDataFunction server_data_func; /**< Function on server side for DATA */
104  DBusAuthEncodeFunction server_encode_func; /**< Function on server side to encode */
105  DBusAuthDecodeFunction server_decode_func; /**< Function on server side to decode */
106  DBusAuthShutdownFunction server_shutdown_func; /**< Function on server side to shut down */
107  DBusInitialResponseFunction client_initial_response_func; /**< Function on client side to handle initial response */
108  DBusAuthDataFunction client_data_func; /**< Function on client side for DATA */
109  DBusAuthEncodeFunction client_encode_func; /**< Function on client side for encode */
110  DBusAuthDecodeFunction client_decode_func; /**< Function on client side for decode */
111  DBusAuthShutdownFunction client_shutdown_func; /**< Function on client side for shutdown */
112} DBusAuthMechanismHandler;
113
114/**
115 * Enumeration for the known authentication commands.
116 */
117typedef enum {
118  DBUS_AUTH_COMMAND_AUTH,
119  DBUS_AUTH_COMMAND_CANCEL,
120  DBUS_AUTH_COMMAND_DATA,
121  DBUS_AUTH_COMMAND_BEGIN,
122  DBUS_AUTH_COMMAND_REJECTED,
123  DBUS_AUTH_COMMAND_OK,
124  DBUS_AUTH_COMMAND_ERROR,
125  DBUS_AUTH_COMMAND_UNKNOWN
126} DBusAuthCommand;
127
128/**
129 * Auth state function, determines the reaction to incoming events for
130 * a particular state. Returns whether we had enough memory to
131 * complete the operation.
132 */
133typedef dbus_bool_t (* DBusAuthStateFunction) (DBusAuth         *auth,
134                                               DBusAuthCommand   command,
135                                               const DBusString *args);
136
137/**
138 * Information about a auth state.
139 */
140typedef struct
141{
142  const char *name;               /**< Name of the state */
143  DBusAuthStateFunction handler;  /**< State function for this state */
144} DBusAuthStateData;
145
146/**
147 * Internal members of DBusAuth.
148 */
149struct DBusAuth
150{
151  int refcount;           /**< reference count */
152  const char *side;       /**< Client or server */
153
154  DBusString incoming;    /**< Incoming data buffer */
155  DBusString outgoing;    /**< Outgoing data buffer */
156
157  const DBusAuthStateData *state;         /**< Current protocol state */
158
159  const DBusAuthMechanismHandler *mech;   /**< Current auth mechanism */
160
161  DBusString identity;                   /**< Current identity we're authorizing
162                                          *   as.
163                                          */
164
165  DBusCredentials *credentials;          /**< Credentials read from socket
166                                          */
167
168  DBusCredentials *authorized_identity; /**< Credentials that are authorized */
169
170  DBusCredentials *desired_identity;    /**< Identity client has requested */
171
172  DBusString context;               /**< Cookie scope */
173  DBusKeyring *keyring;             /**< Keyring for cookie mechanism. */
174  int cookie_id;                    /**< ID of cookie to use */
175  DBusString challenge;             /**< Challenge sent to client */
176
177  char **allowed_mechs;             /**< Mechanisms we're allowed to use,
178                                     * or #NULL if we can use any
179                                     */
180
181  unsigned int needed_memory : 1;   /**< We needed memory to continue since last
182                                     * successful getting something done
183                                     */
184  unsigned int already_got_mechanisms : 1;       /**< Client already got mech list */
185  unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */
186  unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */
187};
188
189/**
190 * "Subclass" of DBusAuth for client side
191 */
192typedef struct
193{
194  DBusAuth base;    /**< Parent class */
195
196  DBusList *mechs_to_try; /**< Mechanisms we got from the server that we're going to try using */
197
198  DBusString guid_from_server; /**< GUID received from server */
199
200} DBusAuthClient;
201
202/**
203 * "Subclass" of DBusAuth for server side.
204 */
205typedef struct
206{
207  DBusAuth base;    /**< Parent class */
208
209  int failures;     /**< Number of times client has been rejected */
210  int max_failures; /**< Number of times we reject before disconnect */
211
212  DBusString guid;  /**< Our globally unique ID in hex encoding */
213
214} DBusAuthServer;
215
216static void        goto_state                (DBusAuth                       *auth,
217                                              const DBusAuthStateData        *new_state);
218static dbus_bool_t send_auth                 (DBusAuth *auth,
219                                              const DBusAuthMechanismHandler *mech);
220static dbus_bool_t send_data                 (DBusAuth *auth,
221                                              DBusString *data);
222static dbus_bool_t send_rejected             (DBusAuth *auth);
223static dbus_bool_t send_error                (DBusAuth *auth,
224                                              const char *message);
225static dbus_bool_t send_ok                   (DBusAuth *auth);
226static dbus_bool_t send_begin                (DBusAuth *auth,
227                                              const DBusString *args_from_ok);
228static dbus_bool_t send_cancel               (DBusAuth *auth);
229
230/**
231 * Client states
232 */
233
234static dbus_bool_t handle_server_state_waiting_for_auth  (DBusAuth         *auth,
235                                                          DBusAuthCommand   command,
236                                                          const DBusString *args);
237static dbus_bool_t handle_server_state_waiting_for_data  (DBusAuth         *auth,
238                                                          DBusAuthCommand   command,
239                                                          const DBusString *args);
240static dbus_bool_t handle_server_state_waiting_for_begin (DBusAuth         *auth,
241                                                          DBusAuthCommand   command,
242                                                          const DBusString *args);
243
244static const DBusAuthStateData server_state_waiting_for_auth = {
245  "WaitingForAuth", handle_server_state_waiting_for_auth
246};
247static const DBusAuthStateData server_state_waiting_for_data = {
248  "WaitingForData", handle_server_state_waiting_for_data
249};
250static const DBusAuthStateData server_state_waiting_for_begin = {
251  "WaitingForBegin", handle_server_state_waiting_for_begin
252};
253
254/**
255 * Client states
256 */
257
258static dbus_bool_t handle_client_state_waiting_for_data   (DBusAuth         *auth,
259                                                           DBusAuthCommand   command,
260                                                           const DBusString *args);
261static dbus_bool_t handle_client_state_waiting_for_ok     (DBusAuth         *auth,
262                                                           DBusAuthCommand   command,
263                                                           const DBusString *args);
264static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth         *auth,
265                                                           DBusAuthCommand   command,
266                                                           const DBusString *args);
267
268static const DBusAuthStateData client_state_need_send_auth = {
269  "NeedSendAuth", NULL
270};
271static const DBusAuthStateData client_state_waiting_for_data = {
272  "WaitingForData", handle_client_state_waiting_for_data
273};
274static const DBusAuthStateData client_state_waiting_for_ok = {
275  "WaitingForOK", handle_client_state_waiting_for_ok
276};
277static const DBusAuthStateData client_state_waiting_for_reject = {
278  "WaitingForReject", handle_client_state_waiting_for_reject
279};
280
281/**
282 * Common terminal states.  Terminal states have handler == NULL.
283 */
284
285static const DBusAuthStateData common_state_authenticated = {
286  "Authenticated",  NULL
287};
288
289static const DBusAuthStateData common_state_need_disconnect = {
290  "NeedDisconnect",  NULL
291};
292
293static const char auth_side_client[] = "client";
294static const char auth_side_server[] = "server";
295/**
296 * @param auth the auth conversation
297 * @returns #TRUE if the conversation is the server side
298 */
299#define DBUS_AUTH_IS_SERVER(auth) ((auth)->side == auth_side_server)
300/**
301 * @param auth the auth conversation
302 * @returns #TRUE if the conversation is the client side
303 */
304#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->side == auth_side_client)
305/**
306 * @param auth the auth conversation
307 * @returns auth cast to DBusAuthClient
308 */
309#define DBUS_AUTH_CLIENT(auth)    ((DBusAuthClient*)(auth))
310/**
311 * @param auth the auth conversation
312 * @returns auth cast to DBusAuthServer
313 */
314#define DBUS_AUTH_SERVER(auth)    ((DBusAuthServer*)(auth))
315
316/**
317 * The name of the auth ("client" or "server")
318 * @param auth the auth conversation
319 * @returns a string
320 */
321#define DBUS_AUTH_NAME(auth)      ((auth)->side)
322
323static DBusAuth*
324_dbus_auth_new (int size)
325{
326  DBusAuth *auth;
327
328  auth = dbus_malloc0 (size);
329  if (auth == NULL)
330    return NULL;
331
332  auth->refcount = 1;
333
334  auth->keyring = NULL;
335  auth->cookie_id = -1;
336
337  /* note that we don't use the max string length feature,
338   * because you can't use that feature if you're going to
339   * try to recover from out-of-memory (it creates
340   * what looks like unrecoverable inability to alloc
341   * more space in the string). But we do handle
342   * overlong buffers in _dbus_auth_do_work().
343   */
344
345  if (!_dbus_string_init (&auth->incoming))
346    goto enomem_0;
347
348  if (!_dbus_string_init (&auth->outgoing))
349    goto enomem_1;
350
351  if (!_dbus_string_init (&auth->identity))
352    goto enomem_2;
353
354  if (!_dbus_string_init (&auth->context))
355    goto enomem_3;
356
357  if (!_dbus_string_init (&auth->challenge))
358    goto enomem_4;
359
360  /* default context if none is specified */
361  if (!_dbus_string_append (&auth->context, "org_freedesktop_general"))
362    goto enomem_5;
363
364  auth->credentials = _dbus_credentials_new ();
365  if (auth->credentials == NULL)
366    goto enomem_6;
367
368  auth->authorized_identity = _dbus_credentials_new ();
369  if (auth->authorized_identity == NULL)
370    goto enomem_7;
371
372  auth->desired_identity = _dbus_credentials_new ();
373  if (auth->desired_identity == NULL)
374    goto enomem_8;
375
376  return auth;
377
378#if 0
379 enomem_9:
380  _dbus_credentials_unref (auth->desired_identity);
381#endif
382 enomem_8:
383  _dbus_credentials_unref (auth->authorized_identity);
384 enomem_7:
385  _dbus_credentials_unref (auth->credentials);
386 enomem_6:
387 /* last alloc was an append to context, which is freed already below */ ;
388 enomem_5:
389  _dbus_string_free (&auth->challenge);
390 enomem_4:
391  _dbus_string_free (&auth->context);
392 enomem_3:
393  _dbus_string_free (&auth->identity);
394 enomem_2:
395  _dbus_string_free (&auth->outgoing);
396 enomem_1:
397  _dbus_string_free (&auth->incoming);
398 enomem_0:
399  dbus_free (auth);
400  return NULL;
401}
402
403static void
404shutdown_mech (DBusAuth *auth)
405{
406  /* Cancel any auth */
407  auth->already_asked_for_initial_response = FALSE;
408  _dbus_string_set_length (&auth->identity, 0);
409
410  _dbus_credentials_clear (auth->authorized_identity);
411  _dbus_credentials_clear (auth->desired_identity);
412
413  if (auth->mech != NULL)
414    {
415      _dbus_verbose ("%s: Shutting down mechanism %s\n",
416                     DBUS_AUTH_NAME (auth), auth->mech->mechanism);
417
418      if (DBUS_AUTH_IS_CLIENT (auth))
419        (* auth->mech->client_shutdown_func) (auth);
420      else
421        (* auth->mech->server_shutdown_func) (auth);
422
423      auth->mech = NULL;
424    }
425}
426
427/*
428 * DBUS_COOKIE_SHA1 mechanism
429 */
430
431/* Returns TRUE but with an empty string hash if the
432 * cookie_id isn't known. As with all this code
433 * TRUE just means we had enough memory.
434 */
435static dbus_bool_t
436sha1_compute_hash (DBusAuth         *auth,
437                   int               cookie_id,
438                   const DBusString *server_challenge,
439                   const DBusString *client_challenge,
440                   DBusString       *hash)
441{
442  DBusString cookie;
443  DBusString to_hash;
444  dbus_bool_t retval;
445
446  _dbus_assert (auth->keyring != NULL);
447
448  retval = FALSE;
449
450  if (!_dbus_string_init (&cookie))
451    return FALSE;
452
453  if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id,
454                                  &cookie))
455    goto out_0;
456
457  if (_dbus_string_get_length (&cookie) == 0)
458    {
459      retval = TRUE;
460      goto out_0;
461    }
462
463  if (!_dbus_string_init (&to_hash))
464    goto out_0;
465
466  if (!_dbus_string_copy (server_challenge, 0,
467                          &to_hash, _dbus_string_get_length (&to_hash)))
468    goto out_1;
469
470  if (!_dbus_string_append (&to_hash, ":"))
471    goto out_1;
472
473  if (!_dbus_string_copy (client_challenge, 0,
474                          &to_hash, _dbus_string_get_length (&to_hash)))
475    goto out_1;
476
477  if (!_dbus_string_append (&to_hash, ":"))
478    goto out_1;
479
480  if (!_dbus_string_copy (&cookie, 0,
481                          &to_hash, _dbus_string_get_length (&to_hash)))
482    goto out_1;
483
484  if (!_dbus_sha_compute (&to_hash, hash))
485    goto out_1;
486
487  retval = TRUE;
488
489 out_1:
490  _dbus_string_zero (&to_hash);
491  _dbus_string_free (&to_hash);
492 out_0:
493  _dbus_string_zero (&cookie);
494  _dbus_string_free (&cookie);
495  return retval;
496}
497
498/** http://www.ietf.org/rfc/rfc2831.txt suggests at least 64 bits of
499 * entropy, we use 128. This is the number of bytes in the random
500 * challenge.
501 */
502#define N_CHALLENGE_BYTES (128/8)
503
504static dbus_bool_t
505sha1_handle_first_client_response (DBusAuth         *auth,
506                                   const DBusString *data)
507{
508  /* We haven't sent a challenge yet, we're expecting a desired
509   * username from the client.
510   */
511  DBusString tmp;
512  DBusString tmp2;
513  dbus_bool_t retval;
514  DBusError error;
515
516  retval = FALSE;
517
518  _dbus_string_set_length (&auth->challenge, 0);
519
520  if (_dbus_string_get_length (data) > 0)
521    {
522      if (_dbus_string_get_length (&auth->identity) > 0)
523        {
524          /* Tried to send two auth identities, wtf */
525          _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
526                         DBUS_AUTH_NAME (auth));
527          return send_rejected (auth);
528        }
529      else
530        {
531          /* this is our auth identity */
532          if (!_dbus_string_copy (data, 0, &auth->identity, 0))
533            return FALSE;
534        }
535    }
536
537  if (!_dbus_credentials_add_from_user (auth->desired_identity, data))
538    {
539      _dbus_verbose ("%s: Did not get a valid username from client\n",
540                     DBUS_AUTH_NAME (auth));
541      return send_rejected (auth);
542    }
543
544  if (!_dbus_string_init (&tmp))
545    return FALSE;
546
547  if (!_dbus_string_init (&tmp2))
548    {
549      _dbus_string_free (&tmp);
550      return FALSE;
551    }
552
553  /* we cache the keyring for speed, so here we drop it if it's the
554   * wrong one. FIXME caching the keyring here is useless since we use
555   * a different DBusAuth for every connection.
556   */
557  if (auth->keyring &&
558      !_dbus_keyring_is_for_credentials (auth->keyring,
559                                         auth->desired_identity))
560    {
561      _dbus_keyring_unref (auth->keyring);
562      auth->keyring = NULL;
563    }
564
565  if (auth->keyring == NULL)
566    {
567      dbus_error_init (&error);
568      auth->keyring = _dbus_keyring_new_for_credentials (auth->desired_identity,
569                                                         &auth->context,
570                                                         &error);
571
572      if (auth->keyring == NULL)
573        {
574          if (dbus_error_has_name (&error,
575                                   DBUS_ERROR_NO_MEMORY))
576            {
577              dbus_error_free (&error);
578              goto out;
579            }
580          else
581            {
582              _DBUS_ASSERT_ERROR_IS_SET (&error);
583              _dbus_verbose ("%s: Error loading keyring: %s\n",
584                             DBUS_AUTH_NAME (auth), error.message);
585              if (send_rejected (auth))
586                retval = TRUE; /* retval is only about mem */
587              dbus_error_free (&error);
588              goto out;
589            }
590        }
591      else
592        {
593          _dbus_assert (!dbus_error_is_set (&error));
594        }
595    }
596
597  _dbus_assert (auth->keyring != NULL);
598
599  dbus_error_init (&error);
600  auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error);
601  if (auth->cookie_id < 0)
602    {
603      _DBUS_ASSERT_ERROR_IS_SET (&error);
604      _dbus_verbose ("%s: Could not get a cookie ID to send to client: %s\n",
605                     DBUS_AUTH_NAME (auth), error.message);
606      if (send_rejected (auth))
607        retval = TRUE;
608      dbus_error_free (&error);
609      goto out;
610    }
611  else
612    {
613      _dbus_assert (!dbus_error_is_set (&error));
614    }
615
616  if (!_dbus_string_copy (&auth->context, 0,
617                          &tmp2, _dbus_string_get_length (&tmp2)))
618    goto out;
619
620  if (!_dbus_string_append (&tmp2, " "))
621    goto out;
622
623  if (!_dbus_string_append_int (&tmp2, auth->cookie_id))
624    goto out;
625
626  if (!_dbus_string_append (&tmp2, " "))
627    goto out;
628
629  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
630    goto out;
631
632  _dbus_string_set_length (&auth->challenge, 0);
633  if (!_dbus_string_hex_encode (&tmp, 0, &auth->challenge, 0))
634    goto out;
635
636  if (!_dbus_string_hex_encode (&tmp, 0, &tmp2,
637                                _dbus_string_get_length (&tmp2)))
638    goto out;
639
640  if (!send_data (auth, &tmp2))
641    goto out;
642
643  goto_state (auth, &server_state_waiting_for_data);
644  retval = TRUE;
645
646 out:
647  _dbus_string_zero (&tmp);
648  _dbus_string_free (&tmp);
649  _dbus_string_zero (&tmp2);
650  _dbus_string_free (&tmp2);
651
652  return retval;
653}
654
655static dbus_bool_t
656sha1_handle_second_client_response (DBusAuth         *auth,
657                                    const DBusString *data)
658{
659  /* We are expecting a response which is the hex-encoded client
660   * challenge, space, then SHA-1 hash of the concatenation of our
661   * challenge, ":", client challenge, ":", secret key, all
662   * hex-encoded.
663   */
664  int i;
665  DBusString client_challenge;
666  DBusString client_hash;
667  dbus_bool_t retval;
668  DBusString correct_hash;
669
670  retval = FALSE;
671
672  if (!_dbus_string_find_blank (data, 0, &i))
673    {
674      _dbus_verbose ("%s: no space separator in client response\n",
675                     DBUS_AUTH_NAME (auth));
676      return send_rejected (auth);
677    }
678
679  if (!_dbus_string_init (&client_challenge))
680    goto out_0;
681
682  if (!_dbus_string_init (&client_hash))
683    goto out_1;
684
685  if (!_dbus_string_copy_len (data, 0, i, &client_challenge,
686                              0))
687    goto out_2;
688
689  _dbus_string_skip_blank (data, i, &i);
690
691  if (!_dbus_string_copy_len (data, i,
692                              _dbus_string_get_length (data) - i,
693                              &client_hash,
694                              0))
695    goto out_2;
696
697  if (_dbus_string_get_length (&client_challenge) == 0 ||
698      _dbus_string_get_length (&client_hash) == 0)
699    {
700      _dbus_verbose ("%s: zero-length client challenge or hash\n",
701                     DBUS_AUTH_NAME (auth));
702      if (send_rejected (auth))
703        retval = TRUE;
704      goto out_2;
705    }
706
707  if (!_dbus_string_init (&correct_hash))
708    goto out_2;
709
710  if (!sha1_compute_hash (auth, auth->cookie_id,
711                          &auth->challenge,
712                          &client_challenge,
713                          &correct_hash))
714    goto out_3;
715
716  /* if cookie_id was invalid, then we get an empty hash */
717  if (_dbus_string_get_length (&correct_hash) == 0)
718    {
719      if (send_rejected (auth))
720        retval = TRUE;
721      goto out_3;
722    }
723
724  if (!_dbus_string_equal (&client_hash, &correct_hash))
725    {
726      if (send_rejected (auth))
727        retval = TRUE;
728      goto out_3;
729    }
730
731  if (!_dbus_credentials_add_credentials (auth->authorized_identity,
732                                          auth->desired_identity))
733    goto out_3;
734
735  /* Copy process ID from the socket credentials if it's there
736   */
737  if (!_dbus_credentials_add_credential (auth->authorized_identity,
738                                         DBUS_CREDENTIAL_UNIX_PROCESS_ID,
739                                         auth->credentials))
740    goto out_3;
741
742  if (!send_ok (auth))
743    goto out_3;
744
745  _dbus_verbose ("%s: authenticated client using DBUS_COOKIE_SHA1\n",
746                 DBUS_AUTH_NAME (auth));
747
748  retval = TRUE;
749
750 out_3:
751  _dbus_string_zero (&correct_hash);
752  _dbus_string_free (&correct_hash);
753 out_2:
754  _dbus_string_zero (&client_hash);
755  _dbus_string_free (&client_hash);
756 out_1:
757  _dbus_string_free (&client_challenge);
758 out_0:
759  return retval;
760}
761
762static dbus_bool_t
763handle_server_data_cookie_sha1_mech (DBusAuth         *auth,
764                                     const DBusString *data)
765{
766  if (auth->cookie_id < 0)
767    return sha1_handle_first_client_response (auth, data);
768  else
769    return sha1_handle_second_client_response (auth, data);
770}
771
772static void
773handle_server_shutdown_cookie_sha1_mech (DBusAuth *auth)
774{
775  auth->cookie_id = -1;
776  _dbus_string_set_length (&auth->challenge, 0);
777}
778
779static dbus_bool_t
780handle_client_initial_response_cookie_sha1_mech (DBusAuth   *auth,
781                                                 DBusString *response)
782{
783  DBusString username;
784  dbus_bool_t retval;
785
786  retval = FALSE;
787
788  if (!_dbus_string_init (&username))
789    return FALSE;
790
791  if (!_dbus_append_user_from_current_process (&username))
792    goto out_0;
793
794  if (!_dbus_string_hex_encode (&username, 0,
795				response,
796				_dbus_string_get_length (response)))
797    goto out_0;
798
799  retval = TRUE;
800
801 out_0:
802  _dbus_string_free (&username);
803
804  return retval;
805}
806
807static dbus_bool_t
808handle_client_data_cookie_sha1_mech (DBusAuth         *auth,
809                                     const DBusString *data)
810{
811  /* The data we get from the server should be the cookie context
812   * name, the cookie ID, and the server challenge, separated by
813   * spaces. We send back our challenge string and the correct hash.
814   */
815  dbus_bool_t retval;
816  DBusString context;
817  DBusString cookie_id_str;
818  DBusString server_challenge;
819  DBusString client_challenge;
820  DBusString correct_hash;
821  DBusString tmp;
822  int i, j;
823  long val;
824
825  retval = FALSE;
826
827  if (!_dbus_string_find_blank (data, 0, &i))
828    {
829      if (send_error (auth,
830                      "Server did not send context/ID/challenge properly"))
831        retval = TRUE;
832      goto out_0;
833    }
834
835  if (!_dbus_string_init (&context))
836    goto out_0;
837
838  if (!_dbus_string_copy_len (data, 0, i,
839                              &context, 0))
840    goto out_1;
841
842  _dbus_string_skip_blank (data, i, &i);
843  if (!_dbus_string_find_blank (data, i, &j))
844    {
845      if (send_error (auth,
846                      "Server did not send context/ID/challenge properly"))
847        retval = TRUE;
848      goto out_1;
849    }
850
851  if (!_dbus_string_init (&cookie_id_str))
852    goto out_1;
853
854  if (!_dbus_string_copy_len (data, i, j - i,
855                              &cookie_id_str, 0))
856    goto out_2;
857
858  if (!_dbus_string_init (&server_challenge))
859    goto out_2;
860
861  i = j;
862  _dbus_string_skip_blank (data, i, &i);
863  j = _dbus_string_get_length (data);
864
865  if (!_dbus_string_copy_len (data, i, j - i,
866                              &server_challenge, 0))
867    goto out_3;
868
869  if (!_dbus_keyring_validate_context (&context))
870    {
871      if (send_error (auth, "Server sent invalid cookie context"))
872        retval = TRUE;
873      goto out_3;
874    }
875
876  if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL))
877    {
878      if (send_error (auth, "Could not parse cookie ID as an integer"))
879        retval = TRUE;
880      goto out_3;
881    }
882
883  if (_dbus_string_get_length (&server_challenge) == 0)
884    {
885      if (send_error (auth, "Empty server challenge string"))
886        retval = TRUE;
887      goto out_3;
888    }
889
890  if (auth->keyring == NULL)
891    {
892      DBusError error;
893
894      dbus_error_init (&error);
895      auth->keyring = _dbus_keyring_new_for_credentials (NULL,
896                                                         &context,
897                                                         &error);
898
899      if (auth->keyring == NULL)
900        {
901          if (dbus_error_has_name (&error,
902                                   DBUS_ERROR_NO_MEMORY))
903            {
904              dbus_error_free (&error);
905              goto out_3;
906            }
907          else
908            {
909              _DBUS_ASSERT_ERROR_IS_SET (&error);
910
911              _dbus_verbose ("%s: Error loading keyring: %s\n",
912                             DBUS_AUTH_NAME (auth), error.message);
913
914              if (send_error (auth, "Could not load cookie file"))
915                retval = TRUE; /* retval is only about mem */
916
917              dbus_error_free (&error);
918              goto out_3;
919            }
920        }
921      else
922        {
923          _dbus_assert (!dbus_error_is_set (&error));
924        }
925    }
926
927  _dbus_assert (auth->keyring != NULL);
928
929  if (!_dbus_string_init (&tmp))
930    goto out_3;
931
932  if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
933    goto out_4;
934
935  if (!_dbus_string_init (&client_challenge))
936    goto out_4;
937
938  if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0))
939    goto out_5;
940
941  if (!_dbus_string_init (&correct_hash))
942    goto out_5;
943
944  if (!sha1_compute_hash (auth, val,
945                          &server_challenge,
946                          &client_challenge,
947                          &correct_hash))
948    goto out_6;
949
950  if (_dbus_string_get_length (&correct_hash) == 0)
951    {
952      /* couldn't find the cookie ID or something */
953      if (send_error (auth, "Don't have the requested cookie ID"))
954        retval = TRUE;
955      goto out_6;
956    }
957
958  _dbus_string_set_length (&tmp, 0);
959
960  if (!_dbus_string_copy (&client_challenge, 0, &tmp,
961                          _dbus_string_get_length (&tmp)))
962    goto out_6;
963
964  if (!_dbus_string_append (&tmp, " "))
965    goto out_6;
966
967  if (!_dbus_string_copy (&correct_hash, 0, &tmp,
968                          _dbus_string_get_length (&tmp)))
969    goto out_6;
970
971  if (!send_data (auth, &tmp))
972    goto out_6;
973
974  retval = TRUE;
975
976 out_6:
977  _dbus_string_zero (&correct_hash);
978  _dbus_string_free (&correct_hash);
979 out_5:
980  _dbus_string_free (&client_challenge);
981 out_4:
982  _dbus_string_zero (&tmp);
983  _dbus_string_free (&tmp);
984 out_3:
985  _dbus_string_free (&server_challenge);
986 out_2:
987  _dbus_string_free (&cookie_id_str);
988 out_1:
989  _dbus_string_free (&context);
990 out_0:
991  return retval;
992}
993
994static void
995handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth)
996{
997  auth->cookie_id = -1;
998  _dbus_string_set_length (&auth->challenge, 0);
999}
1000
1001/*
1002 * EXTERNAL mechanism
1003 */
1004
1005static dbus_bool_t
1006handle_server_data_external_mech (DBusAuth         *auth,
1007                                  const DBusString *data)
1008{
1009  if (_dbus_credentials_are_anonymous (auth->credentials))
1010    {
1011      _dbus_verbose ("%s: no credentials, mechanism EXTERNAL can't authenticate\n",
1012                     DBUS_AUTH_NAME (auth));
1013      return send_rejected (auth);
1014    }
1015
1016  if (_dbus_string_get_length (data) > 0)
1017    {
1018      if (_dbus_string_get_length (&auth->identity) > 0)
1019        {
1020          /* Tried to send two auth identities, wtf */
1021          _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
1022                         DBUS_AUTH_NAME (auth));
1023          return send_rejected (auth);
1024        }
1025      else
1026        {
1027          /* this is our auth identity */
1028          if (!_dbus_string_copy (data, 0, &auth->identity, 0))
1029            return FALSE;
1030        }
1031    }
1032
1033  /* Poke client for an auth identity, if none given */
1034  if (_dbus_string_get_length (&auth->identity) == 0 &&
1035      !auth->already_asked_for_initial_response)
1036    {
1037      if (send_data (auth, NULL))
1038        {
1039          _dbus_verbose ("%s: sending empty challenge asking client for auth identity\n",
1040                         DBUS_AUTH_NAME (auth));
1041          auth->already_asked_for_initial_response = TRUE;
1042          return TRUE;
1043        }
1044      else
1045        return FALSE;
1046    }
1047
1048  _dbus_credentials_clear (auth->desired_identity);
1049
1050  /* If auth->identity is still empty here, then client
1051   * responded with an empty string after we poked it for
1052   * an initial response. This means to try to auth the
1053   * identity provided in the credentials.
1054   */
1055  if (_dbus_string_get_length (&auth->identity) == 0)
1056    {
1057      if (!_dbus_credentials_add_credentials (auth->desired_identity,
1058                                              auth->credentials))
1059        {
1060          return FALSE; /* OOM */
1061        }
1062    }
1063  else
1064    {
1065      if (!_dbus_credentials_add_from_user (auth->desired_identity,
1066                                            &auth->identity))
1067        {
1068          _dbus_verbose ("%s: could not get credentials from uid string\n",
1069                         DBUS_AUTH_NAME (auth));
1070          return send_rejected (auth);
1071        }
1072    }
1073
1074  if (_dbus_credentials_are_anonymous (auth->desired_identity))
1075    {
1076      _dbus_verbose ("%s: desired user %s is no good\n",
1077                     DBUS_AUTH_NAME (auth),
1078                     _dbus_string_get_const_data (&auth->identity));
1079      return send_rejected (auth);
1080    }
1081
1082  if (_dbus_credentials_are_superset (auth->credentials,
1083                                      auth->desired_identity))
1084    {
1085      /* client has authenticated */
1086      if (!_dbus_credentials_add_credentials (auth->authorized_identity,
1087                                              auth->desired_identity))
1088        return FALSE;
1089
1090      /* also copy process ID from the socket credentials
1091       */
1092      if (!_dbus_credentials_add_credential (auth->authorized_identity,
1093                                             DBUS_CREDENTIAL_UNIX_PROCESS_ID,
1094                                             auth->credentials))
1095        return FALSE;
1096
1097      if (!send_ok (auth))
1098        return FALSE;
1099
1100      _dbus_verbose ("%s: authenticated client based on socket credentials\n",
1101                     DBUS_AUTH_NAME (auth));
1102
1103      return TRUE;
1104    }
1105  else
1106    {
1107      _dbus_verbose ("%s: desired identity not found in socket credentials\n",
1108                     DBUS_AUTH_NAME (auth));
1109      return send_rejected (auth);
1110    }
1111}
1112
1113static void
1114handle_server_shutdown_external_mech (DBusAuth *auth)
1115{
1116
1117}
1118
1119static dbus_bool_t
1120handle_client_initial_response_external_mech (DBusAuth         *auth,
1121                                              DBusString       *response)
1122{
1123  /* We always append our UID as an initial response, so the server
1124   * doesn't have to send back an empty challenge to check whether we
1125   * want to specify an identity. i.e. this avoids a round trip that
1126   * the spec for the EXTERNAL mechanism otherwise requires.
1127   */
1128  DBusString plaintext;
1129
1130  if (!_dbus_string_init (&plaintext))
1131    return FALSE;
1132
1133  if (!_dbus_append_user_from_current_process (&plaintext))
1134    goto failed;
1135
1136  if (!_dbus_string_hex_encode (&plaintext, 0,
1137				response,
1138				_dbus_string_get_length (response)))
1139    goto failed;
1140
1141  _dbus_string_free (&plaintext);
1142
1143  return TRUE;
1144
1145 failed:
1146  _dbus_string_free (&plaintext);
1147  return FALSE;
1148}
1149
1150static dbus_bool_t
1151handle_client_data_external_mech (DBusAuth         *auth,
1152                                  const DBusString *data)
1153{
1154
1155  return TRUE;
1156}
1157
1158static void
1159handle_client_shutdown_external_mech (DBusAuth *auth)
1160{
1161
1162}
1163
1164/*
1165 * ANONYMOUS mechanism
1166 */
1167
1168static dbus_bool_t
1169handle_server_data_anonymous_mech (DBusAuth         *auth,
1170                                   const DBusString *data)
1171{
1172  if (_dbus_string_get_length (data) > 0)
1173    {
1174      /* Client is allowed to send "trace" data, the only defined
1175       * meaning is that if it contains '@' it is an email address,
1176       * and otherwise it is anything else, and it's supposed to be
1177       * UTF-8
1178       */
1179      if (!_dbus_string_validate_utf8 (data, 0, _dbus_string_get_length (data)))
1180        {
1181          _dbus_verbose ("%s: Received invalid UTF-8 trace data from ANONYMOUS client\n",
1182                         DBUS_AUTH_NAME (auth));
1183
1184          {
1185            DBusString plaintext;
1186            DBusString encoded;
1187            _dbus_string_init_const (&plaintext, "D-Bus " VERSION);
1188            _dbus_string_init (&encoded);
1189            _dbus_string_hex_encode (&plaintext, 0,
1190                                     &encoded,
1191                                     0);
1192              _dbus_verbose ("%s: try '%s'\n",
1193                             DBUS_AUTH_NAME (auth), _dbus_string_get_const_data (&encoded));
1194          }
1195          return send_rejected (auth);
1196        }
1197
1198      _dbus_verbose ("%s: ANONYMOUS client sent trace string: '%s'\n",
1199                     DBUS_AUTH_NAME (auth),
1200                     _dbus_string_get_const_data (data));
1201    }
1202
1203  /* We want to be anonymous (clear in case some other protocol got midway through I guess) */
1204  _dbus_credentials_clear (auth->desired_identity);
1205
1206  /* Copy process ID from the socket credentials
1207   */
1208  if (!_dbus_credentials_add_credential (auth->authorized_identity,
1209                                         DBUS_CREDENTIAL_UNIX_PROCESS_ID,
1210                                         auth->credentials))
1211    return FALSE;
1212
1213  /* Anonymous is always allowed */
1214  if (!send_ok (auth))
1215    return FALSE;
1216
1217  _dbus_verbose ("%s: authenticated client as anonymous\n",
1218                 DBUS_AUTH_NAME (auth));
1219
1220  return TRUE;
1221}
1222
1223static void
1224handle_server_shutdown_anonymous_mech (DBusAuth *auth)
1225{
1226
1227}
1228
1229static dbus_bool_t
1230handle_client_initial_response_anonymous_mech (DBusAuth         *auth,
1231                                               DBusString       *response)
1232{
1233  /* Our initial response is a "trace" string which must be valid UTF-8
1234   * and must be an email address if it contains '@'.
1235   * We just send the dbus implementation info, like a user-agent or
1236   * something, because... why not. There's nothing guaranteed here
1237   * though, we could change it later.
1238   */
1239  DBusString plaintext;
1240
1241  if (!_dbus_string_init (&plaintext))
1242    return FALSE;
1243
1244  if (!_dbus_string_append (&plaintext,
1245                            "libdbus " VERSION))
1246    goto failed;
1247
1248  if (!_dbus_string_hex_encode (&plaintext, 0,
1249				response,
1250				_dbus_string_get_length (response)))
1251    goto failed;
1252
1253  _dbus_string_free (&plaintext);
1254
1255  return TRUE;
1256
1257 failed:
1258  _dbus_string_free (&plaintext);
1259  return FALSE;
1260}
1261
1262static dbus_bool_t
1263handle_client_data_anonymous_mech (DBusAuth         *auth,
1264                                  const DBusString *data)
1265{
1266
1267  return TRUE;
1268}
1269
1270static void
1271handle_client_shutdown_anonymous_mech (DBusAuth *auth)
1272{
1273
1274}
1275
1276/* Put mechanisms here in order of preference.
1277 * Right now we have:
1278 *
1279 * - EXTERNAL checks socket credentials (or in the future, other info from the OS)
1280 * - DBUS_COOKIE_SHA1 uses a cookie in the home directory, like xauth or ICE
1281 * - ANONYMOUS checks nothing but doesn't auth the person as a user
1282 *
1283 * We might ideally add a mechanism to chain to Cyrus SASL so we can
1284 * use its mechanisms as well.
1285 *
1286 */
1287static const DBusAuthMechanismHandler
1288all_mechanisms[] = {
1289  { "EXTERNAL",
1290    handle_server_data_external_mech,
1291    NULL, NULL,
1292    handle_server_shutdown_external_mech,
1293    handle_client_initial_response_external_mech,
1294    handle_client_data_external_mech,
1295    NULL, NULL,
1296    handle_client_shutdown_external_mech },
1297  { "DBUS_COOKIE_SHA1",
1298    handle_server_data_cookie_sha1_mech,
1299    NULL, NULL,
1300    handle_server_shutdown_cookie_sha1_mech,
1301    handle_client_initial_response_cookie_sha1_mech,
1302    handle_client_data_cookie_sha1_mech,
1303    NULL, NULL,
1304    handle_client_shutdown_cookie_sha1_mech },
1305  { "ANONYMOUS",
1306    handle_server_data_anonymous_mech,
1307    NULL, NULL,
1308    handle_server_shutdown_anonymous_mech,
1309    handle_client_initial_response_anonymous_mech,
1310    handle_client_data_anonymous_mech,
1311    NULL, NULL,
1312    handle_client_shutdown_anonymous_mech },
1313  { NULL, NULL }
1314};
1315
1316static const DBusAuthMechanismHandler*
1317find_mech (const DBusString  *name,
1318           char             **allowed_mechs)
1319{
1320  int i;
1321
1322  if (allowed_mechs != NULL &&
1323      !_dbus_string_array_contains ((const char**) allowed_mechs,
1324                                    _dbus_string_get_const_data (name)))
1325    return NULL;
1326
1327  i = 0;
1328  while (all_mechanisms[i].mechanism != NULL)
1329    {
1330      if (_dbus_string_equal_c_str (name,
1331                                    all_mechanisms[i].mechanism))
1332
1333        return &all_mechanisms[i];
1334
1335      ++i;
1336    }
1337
1338  return NULL;
1339}
1340
1341static dbus_bool_t
1342send_auth (DBusAuth *auth, const DBusAuthMechanismHandler *mech)
1343{
1344  DBusString auth_command;
1345
1346  if (!_dbus_string_init (&auth_command))
1347    return FALSE;
1348
1349  if (!_dbus_string_append (&auth_command,
1350                            "AUTH "))
1351    {
1352      _dbus_string_free (&auth_command);
1353      return FALSE;
1354    }
1355
1356  if (!_dbus_string_append (&auth_command,
1357                            mech->mechanism))
1358    {
1359      _dbus_string_free (&auth_command);
1360      return FALSE;
1361    }
1362
1363  if (mech->client_initial_response_func != NULL)
1364    {
1365      if (!_dbus_string_append (&auth_command, " "))
1366        {
1367          _dbus_string_free (&auth_command);
1368          return FALSE;
1369        }
1370
1371      if (!(* mech->client_initial_response_func) (auth, &auth_command))
1372        {
1373          _dbus_string_free (&auth_command);
1374          return FALSE;
1375        }
1376    }
1377
1378  if (!_dbus_string_append (&auth_command,
1379                            "\r\n"))
1380    {
1381      _dbus_string_free (&auth_command);
1382      return FALSE;
1383    }
1384
1385  if (!_dbus_string_copy (&auth_command, 0,
1386                          &auth->outgoing,
1387                          _dbus_string_get_length (&auth->outgoing)))
1388    {
1389      _dbus_string_free (&auth_command);
1390      return FALSE;
1391    }
1392
1393  _dbus_string_free (&auth_command);
1394  shutdown_mech (auth);
1395  auth->mech = mech;
1396  goto_state (auth, &client_state_waiting_for_data);
1397
1398  return TRUE;
1399}
1400
1401static dbus_bool_t
1402send_data (DBusAuth *auth, DBusString *data)
1403{
1404  int old_len;
1405
1406  if (data == NULL || _dbus_string_get_length (data) == 0)
1407    return _dbus_string_append (&auth->outgoing, "DATA\r\n");
1408  else
1409    {
1410      old_len = _dbus_string_get_length (&auth->outgoing);
1411      if (!_dbus_string_append (&auth->outgoing, "DATA "))
1412        goto out;
1413
1414      if (!_dbus_string_hex_encode (data, 0, &auth->outgoing,
1415                                    _dbus_string_get_length (&auth->outgoing)))
1416        goto out;
1417
1418      if (!_dbus_string_append (&auth->outgoing, "\r\n"))
1419        goto out;
1420
1421      return TRUE;
1422
1423    out:
1424      _dbus_string_set_length (&auth->outgoing, old_len);
1425
1426      return FALSE;
1427    }
1428}
1429
1430static dbus_bool_t
1431send_rejected (DBusAuth *auth)
1432{
1433  DBusString command;
1434  DBusAuthServer *server_auth;
1435  int i;
1436
1437  if (!_dbus_string_init (&command))
1438    return FALSE;
1439
1440  if (!_dbus_string_append (&command,
1441                            "REJECTED"))
1442    goto nomem;
1443
1444  i = 0;
1445  while (all_mechanisms[i].mechanism != NULL)
1446    {
1447      if (!_dbus_string_append (&command,
1448                                " "))
1449        goto nomem;
1450
1451      if (!_dbus_string_append (&command,
1452                                all_mechanisms[i].mechanism))
1453        goto nomem;
1454
1455      ++i;
1456    }
1457
1458  if (!_dbus_string_append (&command, "\r\n"))
1459    goto nomem;
1460
1461  if (!_dbus_string_copy (&command, 0, &auth->outgoing,
1462                          _dbus_string_get_length (&auth->outgoing)))
1463    goto nomem;
1464
1465  shutdown_mech (auth);
1466
1467  _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
1468  server_auth = DBUS_AUTH_SERVER (auth);
1469  server_auth->failures += 1;
1470
1471  if (server_auth->failures >= server_auth->max_failures)
1472    goto_state (auth, &common_state_need_disconnect);
1473  else
1474    goto_state (auth, &server_state_waiting_for_auth);
1475
1476  _dbus_string_free (&command);
1477
1478  return TRUE;
1479
1480 nomem:
1481  _dbus_string_free (&command);
1482  return FALSE;
1483}
1484
1485static dbus_bool_t
1486send_error (DBusAuth *auth, const char *message)
1487{
1488  return _dbus_string_append_printf (&auth->outgoing,
1489                                     "ERROR \"%s\"\r\n", message);
1490}
1491
1492static dbus_bool_t
1493send_ok (DBusAuth *auth)
1494{
1495  int orig_len;
1496
1497  orig_len = _dbus_string_get_length (&auth->outgoing);
1498
1499  if (_dbus_string_append (&auth->outgoing, "OK ") &&
1500      _dbus_string_copy (& DBUS_AUTH_SERVER (auth)->guid,
1501                         0,
1502                         &auth->outgoing,
1503                         _dbus_string_get_length (&auth->outgoing)) &&
1504      _dbus_string_append (&auth->outgoing, "\r\n"))
1505    {
1506      goto_state (auth, &server_state_waiting_for_begin);
1507      return TRUE;
1508    }
1509  else
1510    {
1511      _dbus_string_set_length (&auth->outgoing, orig_len);
1512      return FALSE;
1513    }
1514}
1515
1516static dbus_bool_t
1517send_begin (DBusAuth         *auth,
1518            const DBusString *args_from_ok)
1519{
1520  int end_of_hex;
1521
1522  /* "args_from_ok" should be the GUID, whitespace already pulled off the front */
1523  _dbus_assert (_dbus_string_get_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server) == 0);
1524
1525  /* We decode the hex string to binary, using guid_from_server as scratch... */
1526
1527  end_of_hex = 0;
1528  if (!_dbus_string_hex_decode (args_from_ok, 0, &end_of_hex,
1529                                & DBUS_AUTH_CLIENT (auth)->guid_from_server, 0))
1530    return FALSE;
1531
1532  /* now clear out the scratch */
1533  _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
1534
1535  if (end_of_hex != _dbus_string_get_length (args_from_ok) ||
1536      end_of_hex == 0)
1537    {
1538      _dbus_verbose ("Bad GUID from server, parsed %d bytes and had %d bytes from server\n",
1539                     end_of_hex, _dbus_string_get_length (args_from_ok));
1540      goto_state (auth, &common_state_need_disconnect);
1541      return TRUE;
1542    }
1543
1544  if (_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0) &&
1545      _dbus_string_append (&auth->outgoing, "BEGIN\r\n"))
1546    {
1547      _dbus_verbose ("Got GUID '%s' from the server\n",
1548                     _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server));
1549
1550      goto_state (auth, &common_state_authenticated);
1551      return TRUE;
1552    }
1553  else
1554    {
1555      _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
1556      return FALSE;
1557    }
1558}
1559
1560static dbus_bool_t
1561send_cancel (DBusAuth *auth)
1562{
1563  if (_dbus_string_append (&auth->outgoing, "CANCEL\r\n"))
1564    {
1565      goto_state (auth, &client_state_waiting_for_reject);
1566      return TRUE;
1567    }
1568  else
1569    return FALSE;
1570}
1571
1572static dbus_bool_t
1573process_data (DBusAuth             *auth,
1574              const DBusString     *args,
1575              DBusAuthDataFunction  data_func)
1576{
1577  int end;
1578  DBusString decoded;
1579
1580  if (!_dbus_string_init (&decoded))
1581    return FALSE;
1582
1583  if (!_dbus_string_hex_decode (args, 0, &end, &decoded, 0))
1584    {
1585      _dbus_string_free (&decoded);
1586      return FALSE;
1587    }
1588
1589  if (_dbus_string_get_length (args) != end)
1590    {
1591      _dbus_string_free (&decoded);
1592      if (!send_error (auth, "Invalid hex encoding"))
1593        return FALSE;
1594
1595      return TRUE;
1596    }
1597
1598#ifdef DBUS_ENABLE_VERBOSE_MODE
1599  if (_dbus_string_validate_ascii (&decoded, 0,
1600                                   _dbus_string_get_length (&decoded)))
1601    _dbus_verbose ("%s: data: '%s'\n",
1602                   DBUS_AUTH_NAME (auth),
1603                   _dbus_string_get_const_data (&decoded));
1604#endif
1605
1606  if (!(* data_func) (auth, &decoded))
1607    {
1608      _dbus_string_free (&decoded);
1609      return FALSE;
1610    }
1611
1612  _dbus_string_free (&decoded);
1613  return TRUE;
1614}
1615
1616static dbus_bool_t
1617handle_auth (DBusAuth *auth, const DBusString *args)
1618{
1619  if (_dbus_string_get_length (args) == 0)
1620    {
1621      /* No args to the auth, send mechanisms */
1622      if (!send_rejected (auth))
1623        return FALSE;
1624
1625      return TRUE;
1626    }
1627  else
1628    {
1629      int i;
1630      DBusString mech;
1631      DBusString hex_response;
1632
1633      _dbus_string_find_blank (args, 0, &i);
1634
1635      if (!_dbus_string_init (&mech))
1636        return FALSE;
1637
1638      if (!_dbus_string_init (&hex_response))
1639        {
1640          _dbus_string_free (&mech);
1641          return FALSE;
1642        }
1643
1644      if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
1645        goto failed;
1646
1647      _dbus_string_skip_blank (args, i, &i);
1648      if (!_dbus_string_copy (args, i, &hex_response, 0))
1649        goto failed;
1650
1651      auth->mech = find_mech (&mech, auth->allowed_mechs);
1652      if (auth->mech != NULL)
1653        {
1654          _dbus_verbose ("%s: Trying mechanism %s\n",
1655                         DBUS_AUTH_NAME (auth),
1656                         auth->mech->mechanism);
1657
1658          if (!process_data (auth, &hex_response,
1659                             auth->mech->server_data_func))
1660            goto failed;
1661        }
1662      else
1663        {
1664          /* Unsupported mechanism */
1665          _dbus_verbose ("%s: Unsupported mechanism %s\n",
1666                         DBUS_AUTH_NAME (auth),
1667                         _dbus_string_get_const_data (&mech));
1668
1669          if (!send_rejected (auth))
1670            goto failed;
1671        }
1672
1673      _dbus_string_free (&mech);
1674      _dbus_string_free (&hex_response);
1675
1676      return TRUE;
1677
1678    failed:
1679      auth->mech = NULL;
1680      _dbus_string_free (&mech);
1681      _dbus_string_free (&hex_response);
1682      return FALSE;
1683    }
1684}
1685
1686static dbus_bool_t
1687handle_server_state_waiting_for_auth  (DBusAuth         *auth,
1688                                       DBusAuthCommand   command,
1689                                       const DBusString *args)
1690{
1691  switch (command)
1692    {
1693    case DBUS_AUTH_COMMAND_AUTH:
1694      return handle_auth (auth, args);
1695
1696    case DBUS_AUTH_COMMAND_CANCEL:
1697    case DBUS_AUTH_COMMAND_DATA:
1698      return send_error (auth, "Not currently in an auth conversation");
1699
1700    case DBUS_AUTH_COMMAND_BEGIN:
1701      goto_state (auth, &common_state_need_disconnect);
1702      return TRUE;
1703
1704    case DBUS_AUTH_COMMAND_ERROR:
1705      return send_rejected (auth);
1706
1707    case DBUS_AUTH_COMMAND_REJECTED:
1708    case DBUS_AUTH_COMMAND_OK:
1709    case DBUS_AUTH_COMMAND_UNKNOWN:
1710    default:
1711      return send_error (auth, "Unknown command");
1712    }
1713}
1714
1715static dbus_bool_t
1716handle_server_state_waiting_for_data  (DBusAuth         *auth,
1717                                       DBusAuthCommand   command,
1718                                       const DBusString *args)
1719{
1720  switch (command)
1721    {
1722    case DBUS_AUTH_COMMAND_AUTH:
1723      return send_error (auth, "Sent AUTH while another AUTH in progress");
1724
1725    case DBUS_AUTH_COMMAND_CANCEL:
1726    case DBUS_AUTH_COMMAND_ERROR:
1727      return send_rejected (auth);
1728
1729    case DBUS_AUTH_COMMAND_DATA:
1730      return process_data (auth, args, auth->mech->server_data_func);
1731
1732    case DBUS_AUTH_COMMAND_BEGIN:
1733      goto_state (auth, &common_state_need_disconnect);
1734      return TRUE;
1735
1736    case DBUS_AUTH_COMMAND_REJECTED:
1737    case DBUS_AUTH_COMMAND_OK:
1738    case DBUS_AUTH_COMMAND_UNKNOWN:
1739    default:
1740      return send_error (auth, "Unknown command");
1741    }
1742}
1743
1744static dbus_bool_t
1745handle_server_state_waiting_for_begin (DBusAuth         *auth,
1746                                       DBusAuthCommand   command,
1747                                       const DBusString *args)
1748{
1749  switch (command)
1750    {
1751    case DBUS_AUTH_COMMAND_AUTH:
1752      return send_error (auth, "Sent AUTH while expecting BEGIN");
1753
1754    case DBUS_AUTH_COMMAND_DATA:
1755      return send_error (auth, "Sent DATA while expecting BEGIN");
1756
1757    case DBUS_AUTH_COMMAND_BEGIN:
1758      goto_state (auth, &common_state_authenticated);
1759      return TRUE;
1760
1761    case DBUS_AUTH_COMMAND_REJECTED:
1762    case DBUS_AUTH_COMMAND_OK:
1763    case DBUS_AUTH_COMMAND_UNKNOWN:
1764    default:
1765      return send_error (auth, "Unknown command");
1766
1767    case DBUS_AUTH_COMMAND_CANCEL:
1768    case DBUS_AUTH_COMMAND_ERROR:
1769      return send_rejected (auth);
1770    }
1771}
1772
1773/* return FALSE if no memory, TRUE if all OK */
1774static dbus_bool_t
1775get_word (const DBusString *str,
1776          int              *start,
1777          DBusString       *word)
1778{
1779  int i;
1780
1781  _dbus_string_skip_blank (str, *start, start);
1782  _dbus_string_find_blank (str, *start, &i);
1783
1784  if (i > *start)
1785    {
1786      if (!_dbus_string_copy_len (str, *start, i - *start, word, 0))
1787        return FALSE;
1788
1789      *start = i;
1790    }
1791
1792  return TRUE;
1793}
1794
1795static dbus_bool_t
1796record_mechanisms (DBusAuth         *auth,
1797                   const DBusString *args)
1798{
1799  int next;
1800  int len;
1801
1802  if (auth->already_got_mechanisms)
1803    return TRUE;
1804
1805  len = _dbus_string_get_length (args);
1806
1807  next = 0;
1808  while (next < len)
1809    {
1810      DBusString m;
1811      const DBusAuthMechanismHandler *mech;
1812
1813      if (!_dbus_string_init (&m))
1814        goto nomem;
1815
1816      if (!get_word (args, &next, &m))
1817        {
1818          _dbus_string_free (&m);
1819          goto nomem;
1820        }
1821
1822      mech = find_mech (&m, auth->allowed_mechs);
1823
1824      if (mech != NULL)
1825        {
1826          /* FIXME right now we try mechanisms in the order
1827           * the server lists them; should we do them in
1828           * some more deterministic order?
1829           *
1830           * Probably in all_mechanisms order, our order of
1831           * preference. Of course when the server is us,
1832           * it lists things in that order anyhow.
1833           */
1834
1835          if (mech != &all_mechanisms[0])
1836            {
1837              _dbus_verbose ("%s: Adding mechanism %s to list we will try\n",
1838                             DBUS_AUTH_NAME (auth), mech->mechanism);
1839
1840              if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1841                                      (void*) mech))
1842                {
1843                  _dbus_string_free (&m);
1844                  goto nomem;
1845                }
1846            }
1847          else
1848            {
1849              _dbus_verbose ("%s: Already tried mechanism %s; not adding to list we will try\n",
1850                             DBUS_AUTH_NAME (auth), mech->mechanism);
1851            }
1852        }
1853      else
1854        {
1855          _dbus_verbose ("%s: Server offered mechanism \"%s\" that we don't know how to use\n",
1856                         DBUS_AUTH_NAME (auth),
1857                         _dbus_string_get_const_data (&m));
1858        }
1859
1860      _dbus_string_free (&m);
1861    }
1862
1863  auth->already_got_mechanisms = TRUE;
1864
1865  return TRUE;
1866
1867 nomem:
1868  _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1869
1870  return FALSE;
1871}
1872
1873static dbus_bool_t
1874process_rejected (DBusAuth *auth, const DBusString *args)
1875{
1876  const DBusAuthMechanismHandler *mech;
1877  DBusAuthClient *client;
1878
1879  client = DBUS_AUTH_CLIENT (auth);
1880
1881  if (!auth->already_got_mechanisms)
1882    {
1883      if (!record_mechanisms (auth, args))
1884        return FALSE;
1885    }
1886
1887  if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
1888    {
1889      mech = client->mechs_to_try->data;
1890
1891      if (!send_auth (auth, mech))
1892        return FALSE;
1893
1894      _dbus_list_pop_first (&client->mechs_to_try);
1895
1896      _dbus_verbose ("%s: Trying mechanism %s\n",
1897                     DBUS_AUTH_NAME (auth),
1898                     mech->mechanism);
1899    }
1900  else
1901    {
1902      /* Give up */
1903      _dbus_verbose ("%s: Disconnecting because we are out of mechanisms to try using\n",
1904                     DBUS_AUTH_NAME (auth));
1905      goto_state (auth, &common_state_need_disconnect);
1906    }
1907
1908  return TRUE;
1909}
1910
1911
1912static dbus_bool_t
1913handle_client_state_waiting_for_data (DBusAuth         *auth,
1914                                      DBusAuthCommand   command,
1915                                      const DBusString *args)
1916{
1917  _dbus_assert (auth->mech != NULL);
1918
1919  switch (command)
1920    {
1921    case DBUS_AUTH_COMMAND_DATA:
1922      return process_data (auth, args, auth->mech->client_data_func);
1923
1924    case DBUS_AUTH_COMMAND_REJECTED:
1925      return process_rejected (auth, args);
1926
1927    case DBUS_AUTH_COMMAND_OK:
1928      return send_begin (auth, args);
1929
1930    case DBUS_AUTH_COMMAND_ERROR:
1931      return send_cancel (auth);
1932
1933    case DBUS_AUTH_COMMAND_AUTH:
1934    case DBUS_AUTH_COMMAND_CANCEL:
1935    case DBUS_AUTH_COMMAND_BEGIN:
1936    case DBUS_AUTH_COMMAND_UNKNOWN:
1937    default:
1938      return send_error (auth, "Unknown command");
1939    }
1940}
1941
1942static dbus_bool_t
1943handle_client_state_waiting_for_ok (DBusAuth         *auth,
1944                                    DBusAuthCommand   command,
1945                                    const DBusString *args)
1946{
1947  switch (command)
1948    {
1949    case DBUS_AUTH_COMMAND_REJECTED:
1950      return process_rejected (auth, args);
1951
1952    case DBUS_AUTH_COMMAND_OK:
1953      return send_begin (auth, args);
1954
1955    case DBUS_AUTH_COMMAND_DATA:
1956    case DBUS_AUTH_COMMAND_ERROR:
1957      return send_cancel (auth);
1958
1959    case DBUS_AUTH_COMMAND_AUTH:
1960    case DBUS_AUTH_COMMAND_CANCEL:
1961    case DBUS_AUTH_COMMAND_BEGIN:
1962    case DBUS_AUTH_COMMAND_UNKNOWN:
1963    default:
1964      return send_error (auth, "Unknown command");
1965    }
1966}
1967
1968static dbus_bool_t
1969handle_client_state_waiting_for_reject (DBusAuth         *auth,
1970                                        DBusAuthCommand   command,
1971                                        const DBusString *args)
1972{
1973  switch (command)
1974    {
1975    case DBUS_AUTH_COMMAND_REJECTED:
1976      return process_rejected (auth, args);
1977
1978    case DBUS_AUTH_COMMAND_AUTH:
1979    case DBUS_AUTH_COMMAND_CANCEL:
1980    case DBUS_AUTH_COMMAND_DATA:
1981    case DBUS_AUTH_COMMAND_BEGIN:
1982    case DBUS_AUTH_COMMAND_OK:
1983    case DBUS_AUTH_COMMAND_ERROR:
1984    case DBUS_AUTH_COMMAND_UNKNOWN:
1985    default:
1986      goto_state (auth, &common_state_need_disconnect);
1987      return TRUE;
1988    }
1989}
1990
1991/**
1992 * Mapping from command name to enum
1993 */
1994typedef struct {
1995  const char *name;        /**< Name of the command */
1996  DBusAuthCommand command; /**< Corresponding enum */
1997} DBusAuthCommandName;
1998
1999static const DBusAuthCommandName auth_command_names[] = {
2000  { "AUTH",     DBUS_AUTH_COMMAND_AUTH },
2001  { "CANCEL",   DBUS_AUTH_COMMAND_CANCEL },
2002  { "DATA",     DBUS_AUTH_COMMAND_DATA },
2003  { "BEGIN",    DBUS_AUTH_COMMAND_BEGIN },
2004  { "REJECTED", DBUS_AUTH_COMMAND_REJECTED },
2005  { "OK",       DBUS_AUTH_COMMAND_OK },
2006  { "ERROR",    DBUS_AUTH_COMMAND_ERROR }
2007};
2008
2009static DBusAuthCommand
2010lookup_command_from_name (DBusString *command)
2011{
2012  int i;
2013
2014  for (i = 0; i < _DBUS_N_ELEMENTS (auth_command_names); i++)
2015    {
2016      if (_dbus_string_equal_c_str (command,
2017                                    auth_command_names[i].name))
2018        return auth_command_names[i].command;
2019    }
2020
2021  return DBUS_AUTH_COMMAND_UNKNOWN;
2022}
2023
2024static void
2025goto_state (DBusAuth *auth,
2026            const DBusAuthStateData *state)
2027{
2028  _dbus_verbose ("%s: going from state %s to state %s\n",
2029                 DBUS_AUTH_NAME (auth),
2030                 auth->state->name,
2031                 state->name);
2032
2033  auth->state = state;
2034}
2035
2036/* returns whether to call it again right away */
2037static dbus_bool_t
2038process_command (DBusAuth *auth)
2039{
2040  DBusAuthCommand command;
2041  DBusString line;
2042  DBusString args;
2043  int eol;
2044  int i, j;
2045  dbus_bool_t retval;
2046
2047  /* _dbus_verbose ("%s:   trying process_command()\n"); */
2048
2049  retval = FALSE;
2050
2051  eol = 0;
2052  if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
2053    return FALSE;
2054
2055  if (!_dbus_string_init (&line))
2056    {
2057      auth->needed_memory = TRUE;
2058      return FALSE;
2059    }
2060
2061  if (!_dbus_string_init (&args))
2062    {
2063      _dbus_string_free (&line);
2064      auth->needed_memory = TRUE;
2065      return FALSE;
2066    }
2067
2068  if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &line, 0))
2069    goto out;
2070
2071  if (!_dbus_string_validate_ascii (&line, 0,
2072                                    _dbus_string_get_length (&line)))
2073    {
2074      _dbus_verbose ("%s: Command contained non-ASCII chars or embedded nul\n",
2075                     DBUS_AUTH_NAME (auth));
2076      if (!send_error (auth, "Command contained non-ASCII"))
2077        goto out;
2078      else
2079        goto next_command;
2080    }
2081
2082  _dbus_verbose ("%s: got command \"%s\"\n",
2083                 DBUS_AUTH_NAME (auth),
2084                 _dbus_string_get_const_data (&line));
2085
2086  _dbus_string_find_blank (&line, 0, &i);
2087  _dbus_string_skip_blank (&line, i, &j);
2088
2089  if (j > i)
2090    _dbus_string_delete (&line, i, j - i);
2091
2092  if (!_dbus_string_move (&line, i, &args, 0))
2093    goto out;
2094
2095  /* FIXME 1.0 we should probably validate that only the allowed
2096   * chars are in the command name
2097   */
2098
2099  command = lookup_command_from_name (&line);
2100  if (!(* auth->state->handler) (auth, command, &args))
2101    goto out;
2102
2103 next_command:
2104
2105  /* We've succeeded in processing the whole command so drop it out
2106   * of the incoming buffer and return TRUE to try another command.
2107   */
2108
2109  _dbus_string_delete (&auth->incoming, 0, eol);
2110
2111  /* kill the \r\n */
2112  _dbus_string_delete (&auth->incoming, 0, 2);
2113
2114  retval = TRUE;
2115
2116 out:
2117  _dbus_string_free (&args);
2118  _dbus_string_free (&line);
2119
2120  if (!retval)
2121    auth->needed_memory = TRUE;
2122  else
2123    auth->needed_memory = FALSE;
2124
2125  return retval;
2126}
2127
2128
2129/** @} */
2130
2131/**
2132 * @addtogroup DBusAuth
2133 * @{
2134 */
2135
2136/**
2137 * Creates a new auth conversation object for the server side.
2138 * See doc/dbus-sasl-profile.txt for full details on what
2139 * this object does.
2140 *
2141 * @returns the new object or #NULL if no memory
2142 */
2143DBusAuth*
2144_dbus_auth_server_new (const DBusString *guid)
2145{
2146  DBusAuth *auth;
2147  DBusAuthServer *server_auth;
2148  DBusString guid_copy;
2149
2150  if (!_dbus_string_init (&guid_copy))
2151    return NULL;
2152
2153  if (!_dbus_string_copy (guid, 0, &guid_copy, 0))
2154    {
2155      _dbus_string_free (&guid_copy);
2156      return NULL;
2157    }
2158
2159  auth = _dbus_auth_new (sizeof (DBusAuthServer));
2160  if (auth == NULL)
2161    {
2162      _dbus_string_free (&guid_copy);
2163      return NULL;
2164    }
2165
2166  auth->side = auth_side_server;
2167  auth->state = &server_state_waiting_for_auth;
2168
2169  server_auth = DBUS_AUTH_SERVER (auth);
2170
2171  server_auth->guid = guid_copy;
2172
2173  /* perhaps this should be per-mechanism with a lower
2174   * max
2175   */
2176  server_auth->failures = 0;
2177  server_auth->max_failures = 6;
2178
2179  return auth;
2180}
2181
2182/**
2183 * Creates a new auth conversation object for the client side.
2184 * See doc/dbus-sasl-profile.txt for full details on what
2185 * this object does.
2186 *
2187 * @returns the new object or #NULL if no memory
2188 */
2189DBusAuth*
2190_dbus_auth_client_new (void)
2191{
2192  DBusAuth *auth;
2193  DBusString guid_str;
2194
2195  if (!_dbus_string_init (&guid_str))
2196    return NULL;
2197
2198  auth = _dbus_auth_new (sizeof (DBusAuthClient));
2199  if (auth == NULL)
2200    {
2201      _dbus_string_free (&guid_str);
2202      return NULL;
2203    }
2204
2205  DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str;
2206
2207  auth->side = auth_side_client;
2208  auth->state = &client_state_need_send_auth;
2209
2210  /* Start the auth conversation by sending AUTH for our default
2211   * mechanism */
2212  if (!send_auth (auth, &all_mechanisms[0]))
2213    {
2214      _dbus_auth_unref (auth);
2215      return NULL;
2216    }
2217
2218  return auth;
2219}
2220
2221/**
2222 * Increments the refcount of an auth object.
2223 *
2224 * @param auth the auth conversation
2225 * @returns the auth conversation
2226 */
2227DBusAuth *
2228_dbus_auth_ref (DBusAuth *auth)
2229{
2230  _dbus_assert (auth != NULL);
2231
2232  auth->refcount += 1;
2233
2234  return auth;
2235}
2236
2237/**
2238 * Decrements the refcount of an auth object.
2239 *
2240 * @param auth the auth conversation
2241 */
2242void
2243_dbus_auth_unref (DBusAuth *auth)
2244{
2245  _dbus_assert (auth != NULL);
2246  _dbus_assert (auth->refcount > 0);
2247
2248  auth->refcount -= 1;
2249  if (auth->refcount == 0)
2250    {
2251      shutdown_mech (auth);
2252
2253      if (DBUS_AUTH_IS_CLIENT (auth))
2254        {
2255          _dbus_string_free (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
2256          _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
2257        }
2258      else
2259        {
2260          _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
2261
2262          _dbus_string_free (& DBUS_AUTH_SERVER (auth)->guid);
2263        }
2264
2265      if (auth->keyring)
2266        _dbus_keyring_unref (auth->keyring);
2267
2268      _dbus_string_free (&auth->context);
2269      _dbus_string_free (&auth->challenge);
2270      _dbus_string_free (&auth->identity);
2271      _dbus_string_free (&auth->incoming);
2272      _dbus_string_free (&auth->outgoing);
2273
2274      dbus_free_string_array (auth->allowed_mechs);
2275
2276      _dbus_credentials_unref (auth->credentials);
2277      _dbus_credentials_unref (auth->authorized_identity);
2278      _dbus_credentials_unref (auth->desired_identity);
2279
2280      dbus_free (auth);
2281    }
2282}
2283
2284/**
2285 * Sets an array of authentication mechanism names
2286 * that we are willing to use.
2287 *
2288 * @param auth the auth conversation
2289 * @param mechanisms #NULL-terminated array of mechanism names
2290 * @returns #FALSE if no memory
2291 */
2292dbus_bool_t
2293_dbus_auth_set_mechanisms (DBusAuth    *auth,
2294                           const char **mechanisms)
2295{
2296  char **copy;
2297
2298  if (mechanisms != NULL)
2299    {
2300      copy = _dbus_dup_string_array (mechanisms);
2301      if (copy == NULL)
2302        return FALSE;
2303    }
2304  else
2305    copy = NULL;
2306
2307  dbus_free_string_array (auth->allowed_mechs);
2308
2309  auth->allowed_mechs = copy;
2310
2311  return TRUE;
2312}
2313
2314/**
2315 * @param auth the auth conversation object
2316 * @returns #TRUE if we're in a final state
2317 */
2318#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->state->handler == NULL)
2319
2320/**
2321 * Analyzes buffered input and moves the auth conversation forward,
2322 * returning the new state of the auth conversation.
2323 *
2324 * @param auth the auth conversation
2325 * @returns the new state
2326 */
2327DBusAuthState
2328_dbus_auth_do_work (DBusAuth *auth)
2329{
2330  auth->needed_memory = FALSE;
2331
2332  /* Max amount we'll buffer up before deciding someone's on crack */
2333#define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE)
2334
2335  do
2336    {
2337      if (DBUS_AUTH_IN_END_STATE (auth))
2338        break;
2339
2340      if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
2341          _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
2342        {
2343          goto_state (auth, &common_state_need_disconnect);
2344          _dbus_verbose ("%s: Disconnecting due to excessive data buffered in auth phase\n",
2345                         DBUS_AUTH_NAME (auth));
2346          break;
2347        }
2348    }
2349  while (process_command (auth));
2350
2351  if (auth->needed_memory)
2352    return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
2353  else if (_dbus_string_get_length (&auth->outgoing) > 0)
2354    return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
2355  else if (auth->state == &common_state_need_disconnect)
2356    return DBUS_AUTH_STATE_NEED_DISCONNECT;
2357  else if (auth->state == &common_state_authenticated)
2358    return DBUS_AUTH_STATE_AUTHENTICATED;
2359  else return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
2360}
2361
2362/**
2363 * Gets bytes that need to be sent to the peer we're conversing with.
2364 * After writing some bytes, _dbus_auth_bytes_sent() must be called
2365 * to notify the auth object that they were written.
2366 *
2367 * @param auth the auth conversation
2368 * @param str return location for a ref to the buffer to send
2369 * @returns #FALSE if nothing to send
2370 */
2371dbus_bool_t
2372_dbus_auth_get_bytes_to_send (DBusAuth          *auth,
2373                              const DBusString **str)
2374{
2375  _dbus_assert (auth != NULL);
2376  _dbus_assert (str != NULL);
2377
2378  *str = NULL;
2379
2380  if (_dbus_string_get_length (&auth->outgoing) == 0)
2381    return FALSE;
2382
2383  *str = &auth->outgoing;
2384
2385  return TRUE;
2386}
2387
2388/**
2389 * Notifies the auth conversation object that
2390 * the given number of bytes of the outgoing buffer
2391 * have been written out.
2392 *
2393 * @param auth the auth conversation
2394 * @param bytes_sent number of bytes written out
2395 */
2396void
2397_dbus_auth_bytes_sent (DBusAuth *auth,
2398                       int       bytes_sent)
2399{
2400  _dbus_verbose ("%s: Sent %d bytes of: %s\n",
2401                 DBUS_AUTH_NAME (auth),
2402                 bytes_sent,
2403                 _dbus_string_get_const_data (&auth->outgoing));
2404
2405  _dbus_string_delete (&auth->outgoing,
2406                       0, bytes_sent);
2407}
2408
2409/**
2410 * Get a buffer to be used for reading bytes from the peer we're conversing
2411 * with. Bytes should be appended to this buffer.
2412 *
2413 * @param auth the auth conversation
2414 * @param buffer return location for buffer to append bytes to
2415 */
2416void
2417_dbus_auth_get_buffer (DBusAuth     *auth,
2418                       DBusString **buffer)
2419{
2420  _dbus_assert (auth != NULL);
2421  _dbus_assert (!auth->buffer_outstanding);
2422
2423  *buffer = &auth->incoming;
2424
2425  auth->buffer_outstanding = TRUE;
2426}
2427
2428/**
2429 * Returns a buffer with new data read into it.
2430 *
2431 * @param auth the auth conversation
2432 * @param buffer the buffer being returned
2433 * @param bytes_read number of new bytes added
2434 */
2435void
2436_dbus_auth_return_buffer (DBusAuth               *auth,
2437                          DBusString             *buffer,
2438                          int                     bytes_read)
2439{
2440  _dbus_assert (buffer == &auth->incoming);
2441  _dbus_assert (auth->buffer_outstanding);
2442
2443  auth->buffer_outstanding = FALSE;
2444}
2445
2446/**
2447 * Returns leftover bytes that were not used as part of the auth
2448 * conversation.  These bytes will be part of the message stream
2449 * instead. This function may not be called until authentication has
2450 * succeeded.
2451 *
2452 * @param auth the auth conversation
2453 * @param str return location for pointer to string of unused bytes
2454 */
2455void
2456_dbus_auth_get_unused_bytes (DBusAuth           *auth,
2457                             const DBusString **str)
2458{
2459  if (!DBUS_AUTH_IN_END_STATE (auth))
2460    return;
2461
2462  *str = &auth->incoming;
2463}
2464
2465
2466/**
2467 * Gets rid of unused bytes returned by _dbus_auth_get_unused_bytes()
2468 * after we've gotten them and successfully moved them elsewhere.
2469 *
2470 * @param auth the auth conversation
2471 */
2472void
2473_dbus_auth_delete_unused_bytes (DBusAuth *auth)
2474{
2475  if (!DBUS_AUTH_IN_END_STATE (auth))
2476    return;
2477
2478  _dbus_string_set_length (&auth->incoming, 0);
2479}
2480
2481/**
2482 * Called post-authentication, indicates whether we need to encode
2483 * the message stream with _dbus_auth_encode_data() prior to
2484 * sending it to the peer.
2485 *
2486 * @param auth the auth conversation
2487 * @returns #TRUE if we need to encode the stream
2488 */
2489dbus_bool_t
2490_dbus_auth_needs_encoding (DBusAuth *auth)
2491{
2492  if (auth->state != &common_state_authenticated)
2493    return FALSE;
2494
2495  if (auth->mech != NULL)
2496    {
2497      if (DBUS_AUTH_IS_CLIENT (auth))
2498        return auth->mech->client_encode_func != NULL;
2499      else
2500        return auth->mech->server_encode_func != NULL;
2501    }
2502  else
2503    return FALSE;
2504}
2505
2506/**
2507 * Called post-authentication, encodes a block of bytes for sending to
2508 * the peer. If no encoding was negotiated, just copies the bytes
2509 * (you can avoid this by checking _dbus_auth_needs_encoding()).
2510 *
2511 * @param auth the auth conversation
2512 * @param plaintext the plain text data
2513 * @param encoded initialized string to where encoded data is appended
2514 * @returns #TRUE if we had enough memory and successfully encoded
2515 */
2516dbus_bool_t
2517_dbus_auth_encode_data (DBusAuth         *auth,
2518                        const DBusString *plaintext,
2519                        DBusString       *encoded)
2520{
2521  _dbus_assert (plaintext != encoded);
2522
2523  if (auth->state != &common_state_authenticated)
2524    return FALSE;
2525
2526  if (_dbus_auth_needs_encoding (auth))
2527    {
2528      if (DBUS_AUTH_IS_CLIENT (auth))
2529        return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
2530      else
2531        return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
2532    }
2533  else
2534    {
2535      return _dbus_string_copy (plaintext, 0, encoded,
2536                                _dbus_string_get_length (encoded));
2537    }
2538}
2539
2540/**
2541 * Called post-authentication, indicates whether we need to decode
2542 * the message stream with _dbus_auth_decode_data() after
2543 * receiving it from the peer.
2544 *
2545 * @param auth the auth conversation
2546 * @returns #TRUE if we need to encode the stream
2547 */
2548dbus_bool_t
2549_dbus_auth_needs_decoding (DBusAuth *auth)
2550{
2551  if (auth->state != &common_state_authenticated)
2552    return FALSE;
2553
2554  if (auth->mech != NULL)
2555    {
2556      if (DBUS_AUTH_IS_CLIENT (auth))
2557        return auth->mech->client_decode_func != NULL;
2558      else
2559        return auth->mech->server_decode_func != NULL;
2560    }
2561  else
2562    return FALSE;
2563}
2564
2565
2566/**
2567 * Called post-authentication, decodes a block of bytes received from
2568 * the peer. If no encoding was negotiated, just copies the bytes (you
2569 * can avoid this by checking _dbus_auth_needs_decoding()).
2570 *
2571 * @todo 1.0? We need to be able to distinguish "out of memory" error
2572 * from "the data is hosed" error.
2573 *
2574 * @param auth the auth conversation
2575 * @param encoded the encoded data
2576 * @param plaintext initialized string where decoded data is appended
2577 * @returns #TRUE if we had enough memory and successfully decoded
2578 */
2579dbus_bool_t
2580_dbus_auth_decode_data (DBusAuth         *auth,
2581                        const DBusString *encoded,
2582                        DBusString       *plaintext)
2583{
2584  _dbus_assert (plaintext != encoded);
2585
2586  if (auth->state != &common_state_authenticated)
2587    return FALSE;
2588
2589  if (_dbus_auth_needs_decoding (auth))
2590    {
2591      if (DBUS_AUTH_IS_CLIENT (auth))
2592        return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
2593      else
2594        return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
2595    }
2596  else
2597    {
2598      return _dbus_string_copy (encoded, 0, plaintext,
2599                                _dbus_string_get_length (plaintext));
2600    }
2601}
2602
2603/**
2604 * Sets credentials received via reliable means from the operating
2605 * system.
2606 *
2607 * @param auth the auth conversation
2608 * @param credentials the credentials received
2609 * @returns #FALSE on OOM
2610 */
2611dbus_bool_t
2612_dbus_auth_set_credentials (DBusAuth               *auth,
2613                            DBusCredentials        *credentials)
2614{
2615  _dbus_credentials_clear (auth->credentials);
2616  return _dbus_credentials_add_credentials (auth->credentials,
2617                                            credentials);
2618}
2619
2620/**
2621 * Gets the identity we authorized the client as.  Apps may have
2622 * different policies as to what identities they allow.
2623 *
2624 * Returned credentials are not a copy and should not be modified
2625 *
2626 * @param auth the auth conversation
2627 * @returns the credentials we've authorized BY REFERENCE do not modify
2628 */
2629DBusCredentials*
2630_dbus_auth_get_identity (DBusAuth               *auth)
2631{
2632  if (auth->state == &common_state_authenticated)
2633    {
2634      return auth->authorized_identity;
2635    }
2636  else
2637    {
2638      /* FIXME instead of this, keep an empty credential around that
2639       * doesn't require allocation or something
2640       */
2641      /* return empty credentials */
2642      _dbus_assert (_dbus_credentials_are_empty (auth->authorized_identity));
2643      return auth->authorized_identity;
2644    }
2645}
2646
2647/**
2648 * Gets the GUID from the server if we've authenticated; gets
2649 * #NULL otherwise.
2650 * @param auth the auth object
2651 * @returns the GUID in ASCII hex format
2652 */
2653const char*
2654_dbus_auth_get_guid_from_server (DBusAuth *auth)
2655{
2656  _dbus_assert (DBUS_AUTH_IS_CLIENT (auth));
2657
2658  if (auth->state == &common_state_authenticated)
2659    return _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
2660  else
2661    return NULL;
2662}
2663
2664/**
2665 * Sets the "authentication context" which scopes cookies
2666 * with the DBUS_COOKIE_SHA1 auth mechanism for example.
2667 *
2668 * @param auth the auth conversation
2669 * @param context the context
2670 * @returns #FALSE if no memory
2671 */
2672dbus_bool_t
2673_dbus_auth_set_context (DBusAuth               *auth,
2674                        const DBusString       *context)
2675{
2676  return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context),
2677                                   &auth->context, 0, _dbus_string_get_length (context));
2678}
2679
2680/** @} */
2681
2682/* tests in dbus-auth-util.c */
2683