dbus-auth.c revision 5baf2f856a9c6625993234855b07680da1c8916f
1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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          goto_state (auth, &server_state_waiting_for_data);
1043          return TRUE;
1044        }
1045      else
1046        return FALSE;
1047    }
1048
1049  _dbus_credentials_clear (auth->desired_identity);
1050
1051  /* If auth->identity is still empty here, then client
1052   * responded with an empty string after we poked it for
1053   * an initial response. This means to try to auth the
1054   * identity provided in the credentials.
1055   */
1056  if (_dbus_string_get_length (&auth->identity) == 0)
1057    {
1058      if (!_dbus_credentials_add_credentials (auth->desired_identity,
1059                                              auth->credentials))
1060        {
1061          return FALSE; /* OOM */
1062        }
1063    }
1064  else
1065    {
1066      if (!_dbus_credentials_add_from_user (auth->desired_identity,
1067                                            &auth->identity))
1068        {
1069          _dbus_verbose ("%s: could not get credentials from uid string\n",
1070                         DBUS_AUTH_NAME (auth));
1071          return send_rejected (auth);
1072        }
1073    }
1074
1075  if (_dbus_credentials_are_anonymous (auth->desired_identity))
1076    {
1077      _dbus_verbose ("%s: desired user %s is no good\n",
1078                     DBUS_AUTH_NAME (auth),
1079                     _dbus_string_get_const_data (&auth->identity));
1080      return send_rejected (auth);
1081    }
1082
1083  if (_dbus_credentials_are_superset (auth->credentials,
1084                                      auth->desired_identity))
1085    {
1086      /* client has authenticated */
1087      if (!_dbus_credentials_add_credentials (auth->authorized_identity,
1088                                              auth->desired_identity))
1089        return FALSE;
1090
1091      /* also copy process ID from the socket credentials
1092       */
1093      if (!_dbus_credentials_add_credential (auth->authorized_identity,
1094                                             DBUS_CREDENTIAL_UNIX_PROCESS_ID,
1095                                             auth->credentials))
1096        return FALSE;
1097
1098      /* also copy audit data from the socket credentials
1099       */
1100      if (!_dbus_credentials_add_credential (auth->authorized_identity,
1101                                             DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
1102                                             auth->credentials))
1103        return FALSE;
1104
1105      if (!send_ok (auth))
1106        return FALSE;
1107
1108      _dbus_verbose ("%s: authenticated client based on socket credentials\n",
1109                     DBUS_AUTH_NAME (auth));
1110
1111      return TRUE;
1112    }
1113  else
1114    {
1115      _dbus_verbose ("%s: desired identity not found in socket credentials\n",
1116                     DBUS_AUTH_NAME (auth));
1117      return send_rejected (auth);
1118    }
1119}
1120
1121static void
1122handle_server_shutdown_external_mech (DBusAuth *auth)
1123{
1124
1125}
1126
1127static dbus_bool_t
1128handle_client_initial_response_external_mech (DBusAuth         *auth,
1129                                              DBusString       *response)
1130{
1131  /* We always append our UID as an initial response, so the server
1132   * doesn't have to send back an empty challenge to check whether we
1133   * want to specify an identity. i.e. this avoids a round trip that
1134   * the spec for the EXTERNAL mechanism otherwise requires.
1135   */
1136  DBusString plaintext;
1137
1138  if (!_dbus_string_init (&plaintext))
1139    return FALSE;
1140
1141  if (!_dbus_append_user_from_current_process (&plaintext))
1142    goto failed;
1143
1144  if (!_dbus_string_hex_encode (&plaintext, 0,
1145				response,
1146				_dbus_string_get_length (response)))
1147    goto failed;
1148
1149  _dbus_string_free (&plaintext);
1150
1151  return TRUE;
1152
1153 failed:
1154  _dbus_string_free (&plaintext);
1155  return FALSE;
1156}
1157
1158static dbus_bool_t
1159handle_client_data_external_mech (DBusAuth         *auth,
1160                                  const DBusString *data)
1161{
1162
1163  return TRUE;
1164}
1165
1166static void
1167handle_client_shutdown_external_mech (DBusAuth *auth)
1168{
1169
1170}
1171
1172/*
1173 * ANONYMOUS mechanism
1174 */
1175
1176static dbus_bool_t
1177handle_server_data_anonymous_mech (DBusAuth         *auth,
1178                                   const DBusString *data)
1179{
1180  if (_dbus_string_get_length (data) > 0)
1181    {
1182      /* Client is allowed to send "trace" data, the only defined
1183       * meaning is that if it contains '@' it is an email address,
1184       * and otherwise it is anything else, and it's supposed to be
1185       * UTF-8
1186       */
1187      if (!_dbus_string_validate_utf8 (data, 0, _dbus_string_get_length (data)))
1188        {
1189          _dbus_verbose ("%s: Received invalid UTF-8 trace data from ANONYMOUS client\n",
1190                         DBUS_AUTH_NAME (auth));
1191
1192          {
1193            DBusString plaintext;
1194            DBusString encoded;
1195            _dbus_string_init_const (&plaintext, "D-Bus " VERSION);
1196            _dbus_string_init (&encoded);
1197            _dbus_string_hex_encode (&plaintext, 0,
1198                                     &encoded,
1199                                     0);
1200              _dbus_verbose ("%s: try '%s'\n",
1201                             DBUS_AUTH_NAME (auth), _dbus_string_get_const_data (&encoded));
1202          }
1203          return send_rejected (auth);
1204        }
1205
1206      _dbus_verbose ("%s: ANONYMOUS client sent trace string: '%s'\n",
1207                     DBUS_AUTH_NAME (auth),
1208                     _dbus_string_get_const_data (data));
1209    }
1210
1211  /* We want to be anonymous (clear in case some other protocol got midway through I guess) */
1212  _dbus_credentials_clear (auth->desired_identity);
1213
1214  /* Copy process ID from the socket credentials
1215   */
1216  if (!_dbus_credentials_add_credential (auth->authorized_identity,
1217                                         DBUS_CREDENTIAL_UNIX_PROCESS_ID,
1218                                         auth->credentials))
1219    return FALSE;
1220
1221  /* Anonymous is always allowed */
1222  if (!send_ok (auth))
1223    return FALSE;
1224
1225  _dbus_verbose ("%s: authenticated client as anonymous\n",
1226                 DBUS_AUTH_NAME (auth));
1227
1228  return TRUE;
1229}
1230
1231static void
1232handle_server_shutdown_anonymous_mech (DBusAuth *auth)
1233{
1234
1235}
1236
1237static dbus_bool_t
1238handle_client_initial_response_anonymous_mech (DBusAuth         *auth,
1239                                               DBusString       *response)
1240{
1241  /* Our initial response is a "trace" string which must be valid UTF-8
1242   * and must be an email address if it contains '@'.
1243   * We just send the dbus implementation info, like a user-agent or
1244   * something, because... why not. There's nothing guaranteed here
1245   * though, we could change it later.
1246   */
1247  DBusString plaintext;
1248
1249  if (!_dbus_string_init (&plaintext))
1250    return FALSE;
1251
1252  if (!_dbus_string_append (&plaintext,
1253                            "libdbus " VERSION))
1254    goto failed;
1255
1256  if (!_dbus_string_hex_encode (&plaintext, 0,
1257				response,
1258				_dbus_string_get_length (response)))
1259    goto failed;
1260
1261  _dbus_string_free (&plaintext);
1262
1263  return TRUE;
1264
1265 failed:
1266  _dbus_string_free (&plaintext);
1267  return FALSE;
1268}
1269
1270static dbus_bool_t
1271handle_client_data_anonymous_mech (DBusAuth         *auth,
1272                                  const DBusString *data)
1273{
1274
1275  return TRUE;
1276}
1277
1278static void
1279handle_client_shutdown_anonymous_mech (DBusAuth *auth)
1280{
1281
1282}
1283
1284/* Put mechanisms here in order of preference.
1285 * Right now we have:
1286 *
1287 * - EXTERNAL checks socket credentials (or in the future, other info from the OS)
1288 * - DBUS_COOKIE_SHA1 uses a cookie in the home directory, like xauth or ICE
1289 * - ANONYMOUS checks nothing but doesn't auth the person as a user
1290 *
1291 * We might ideally add a mechanism to chain to Cyrus SASL so we can
1292 * use its mechanisms as well.
1293 *
1294 */
1295static const DBusAuthMechanismHandler
1296all_mechanisms[] = {
1297  { "EXTERNAL",
1298    handle_server_data_external_mech,
1299    NULL, NULL,
1300    handle_server_shutdown_external_mech,
1301    handle_client_initial_response_external_mech,
1302    handle_client_data_external_mech,
1303    NULL, NULL,
1304    handle_client_shutdown_external_mech },
1305  { "DBUS_COOKIE_SHA1",
1306    handle_server_data_cookie_sha1_mech,
1307    NULL, NULL,
1308    handle_server_shutdown_cookie_sha1_mech,
1309    handle_client_initial_response_cookie_sha1_mech,
1310    handle_client_data_cookie_sha1_mech,
1311    NULL, NULL,
1312    handle_client_shutdown_cookie_sha1_mech },
1313  { "ANONYMOUS",
1314    handle_server_data_anonymous_mech,
1315    NULL, NULL,
1316    handle_server_shutdown_anonymous_mech,
1317    handle_client_initial_response_anonymous_mech,
1318    handle_client_data_anonymous_mech,
1319    NULL, NULL,
1320    handle_client_shutdown_anonymous_mech },
1321  { NULL, NULL }
1322};
1323
1324static const DBusAuthMechanismHandler*
1325find_mech (const DBusString  *name,
1326           char             **allowed_mechs)
1327{
1328  int i;
1329
1330  if (allowed_mechs != NULL &&
1331      !_dbus_string_array_contains ((const char**) allowed_mechs,
1332                                    _dbus_string_get_const_data (name)))
1333    return NULL;
1334
1335  i = 0;
1336  while (all_mechanisms[i].mechanism != NULL)
1337    {
1338      if (_dbus_string_equal_c_str (name,
1339                                    all_mechanisms[i].mechanism))
1340
1341        return &all_mechanisms[i];
1342
1343      ++i;
1344    }
1345
1346  return NULL;
1347}
1348
1349static dbus_bool_t
1350send_auth (DBusAuth *auth, const DBusAuthMechanismHandler *mech)
1351{
1352  DBusString auth_command;
1353
1354  if (!_dbus_string_init (&auth_command))
1355    return FALSE;
1356
1357  if (!_dbus_string_append (&auth_command,
1358                            "AUTH "))
1359    {
1360      _dbus_string_free (&auth_command);
1361      return FALSE;
1362    }
1363
1364  if (!_dbus_string_append (&auth_command,
1365                            mech->mechanism))
1366    {
1367      _dbus_string_free (&auth_command);
1368      return FALSE;
1369    }
1370
1371  if (mech->client_initial_response_func != NULL)
1372    {
1373      if (!_dbus_string_append (&auth_command, " "))
1374        {
1375          _dbus_string_free (&auth_command);
1376          return FALSE;
1377        }
1378
1379      if (!(* mech->client_initial_response_func) (auth, &auth_command))
1380        {
1381          _dbus_string_free (&auth_command);
1382          return FALSE;
1383        }
1384    }
1385
1386  if (!_dbus_string_append (&auth_command,
1387                            "\r\n"))
1388    {
1389      _dbus_string_free (&auth_command);
1390      return FALSE;
1391    }
1392
1393  if (!_dbus_string_copy (&auth_command, 0,
1394                          &auth->outgoing,
1395                          _dbus_string_get_length (&auth->outgoing)))
1396    {
1397      _dbus_string_free (&auth_command);
1398      return FALSE;
1399    }
1400
1401  _dbus_string_free (&auth_command);
1402  shutdown_mech (auth);
1403  auth->mech = mech;
1404  goto_state (auth, &client_state_waiting_for_data);
1405
1406  return TRUE;
1407}
1408
1409static dbus_bool_t
1410send_data (DBusAuth *auth, DBusString *data)
1411{
1412  int old_len;
1413
1414  if (data == NULL || _dbus_string_get_length (data) == 0)
1415    return _dbus_string_append (&auth->outgoing, "DATA\r\n");
1416  else
1417    {
1418      old_len = _dbus_string_get_length (&auth->outgoing);
1419      if (!_dbus_string_append (&auth->outgoing, "DATA "))
1420        goto out;
1421
1422      if (!_dbus_string_hex_encode (data, 0, &auth->outgoing,
1423                                    _dbus_string_get_length (&auth->outgoing)))
1424        goto out;
1425
1426      if (!_dbus_string_append (&auth->outgoing, "\r\n"))
1427        goto out;
1428
1429      return TRUE;
1430
1431    out:
1432      _dbus_string_set_length (&auth->outgoing, old_len);
1433
1434      return FALSE;
1435    }
1436}
1437
1438static dbus_bool_t
1439send_rejected (DBusAuth *auth)
1440{
1441  DBusString command;
1442  DBusAuthServer *server_auth;
1443  int i;
1444
1445  if (!_dbus_string_init (&command))
1446    return FALSE;
1447
1448  if (!_dbus_string_append (&command,
1449                            "REJECTED"))
1450    goto nomem;
1451
1452  i = 0;
1453  while (all_mechanisms[i].mechanism != NULL)
1454    {
1455      if (!_dbus_string_append (&command,
1456                                " "))
1457        goto nomem;
1458
1459      if (!_dbus_string_append (&command,
1460                                all_mechanisms[i].mechanism))
1461        goto nomem;
1462
1463      ++i;
1464    }
1465
1466  if (!_dbus_string_append (&command, "\r\n"))
1467    goto nomem;
1468
1469  if (!_dbus_string_copy (&command, 0, &auth->outgoing,
1470                          _dbus_string_get_length (&auth->outgoing)))
1471    goto nomem;
1472
1473  shutdown_mech (auth);
1474
1475  _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
1476  server_auth = DBUS_AUTH_SERVER (auth);
1477  server_auth->failures += 1;
1478
1479  if (server_auth->failures >= server_auth->max_failures)
1480    goto_state (auth, &common_state_need_disconnect);
1481  else
1482    goto_state (auth, &server_state_waiting_for_auth);
1483
1484  _dbus_string_free (&command);
1485
1486  return TRUE;
1487
1488 nomem:
1489  _dbus_string_free (&command);
1490  return FALSE;
1491}
1492
1493static dbus_bool_t
1494send_error (DBusAuth *auth, const char *message)
1495{
1496  return _dbus_string_append_printf (&auth->outgoing,
1497                                     "ERROR \"%s\"\r\n", message);
1498}
1499
1500static dbus_bool_t
1501send_ok (DBusAuth *auth)
1502{
1503  int orig_len;
1504
1505  orig_len = _dbus_string_get_length (&auth->outgoing);
1506
1507  if (_dbus_string_append (&auth->outgoing, "OK ") &&
1508      _dbus_string_copy (& DBUS_AUTH_SERVER (auth)->guid,
1509                         0,
1510                         &auth->outgoing,
1511                         _dbus_string_get_length (&auth->outgoing)) &&
1512      _dbus_string_append (&auth->outgoing, "\r\n"))
1513    {
1514      goto_state (auth, &server_state_waiting_for_begin);
1515      return TRUE;
1516    }
1517  else
1518    {
1519      _dbus_string_set_length (&auth->outgoing, orig_len);
1520      return FALSE;
1521    }
1522}
1523
1524static dbus_bool_t
1525send_begin (DBusAuth         *auth,
1526            const DBusString *args_from_ok)
1527{
1528  int end_of_hex;
1529
1530  /* "args_from_ok" should be the GUID, whitespace already pulled off the front */
1531  _dbus_assert (_dbus_string_get_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server) == 0);
1532
1533  /* We decode the hex string to binary, using guid_from_server as scratch... */
1534
1535  end_of_hex = 0;
1536  if (!_dbus_string_hex_decode (args_from_ok, 0, &end_of_hex,
1537                                & DBUS_AUTH_CLIENT (auth)->guid_from_server, 0))
1538    return FALSE;
1539
1540  /* now clear out the scratch */
1541  _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
1542
1543  if (end_of_hex != _dbus_string_get_length (args_from_ok) ||
1544      end_of_hex == 0)
1545    {
1546      _dbus_verbose ("Bad GUID from server, parsed %d bytes and had %d bytes from server\n",
1547                     end_of_hex, _dbus_string_get_length (args_from_ok));
1548      goto_state (auth, &common_state_need_disconnect);
1549      return TRUE;
1550    }
1551
1552  if (_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0) &&
1553      _dbus_string_append (&auth->outgoing, "BEGIN\r\n"))
1554    {
1555      _dbus_verbose ("Got GUID '%s' from the server\n",
1556                     _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server));
1557
1558      goto_state (auth, &common_state_authenticated);
1559      return TRUE;
1560    }
1561  else
1562    {
1563      _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
1564      return FALSE;
1565    }
1566}
1567
1568static dbus_bool_t
1569send_cancel (DBusAuth *auth)
1570{
1571  if (_dbus_string_append (&auth->outgoing, "CANCEL\r\n"))
1572    {
1573      goto_state (auth, &client_state_waiting_for_reject);
1574      return TRUE;
1575    }
1576  else
1577    return FALSE;
1578}
1579
1580static dbus_bool_t
1581process_data (DBusAuth             *auth,
1582              const DBusString     *args,
1583              DBusAuthDataFunction  data_func)
1584{
1585  int end;
1586  DBusString decoded;
1587
1588  if (!_dbus_string_init (&decoded))
1589    return FALSE;
1590
1591  if (!_dbus_string_hex_decode (args, 0, &end, &decoded, 0))
1592    {
1593      _dbus_string_free (&decoded);
1594      return FALSE;
1595    }
1596
1597  if (_dbus_string_get_length (args) != end)
1598    {
1599      _dbus_string_free (&decoded);
1600      if (!send_error (auth, "Invalid hex encoding"))
1601        return FALSE;
1602
1603      return TRUE;
1604    }
1605
1606#ifdef DBUS_ENABLE_VERBOSE_MODE
1607  if (_dbus_string_validate_ascii (&decoded, 0,
1608                                   _dbus_string_get_length (&decoded)))
1609    _dbus_verbose ("%s: data: '%s'\n",
1610                   DBUS_AUTH_NAME (auth),
1611                   _dbus_string_get_const_data (&decoded));
1612#endif
1613
1614  if (!(* data_func) (auth, &decoded))
1615    {
1616      _dbus_string_free (&decoded);
1617      return FALSE;
1618    }
1619
1620  _dbus_string_free (&decoded);
1621  return TRUE;
1622}
1623
1624static dbus_bool_t
1625handle_auth (DBusAuth *auth, const DBusString *args)
1626{
1627  if (_dbus_string_get_length (args) == 0)
1628    {
1629      /* No args to the auth, send mechanisms */
1630      if (!send_rejected (auth))
1631        return FALSE;
1632
1633      return TRUE;
1634    }
1635  else
1636    {
1637      int i;
1638      DBusString mech;
1639      DBusString hex_response;
1640
1641      _dbus_string_find_blank (args, 0, &i);
1642
1643      if (!_dbus_string_init (&mech))
1644        return FALSE;
1645
1646      if (!_dbus_string_init (&hex_response))
1647        {
1648          _dbus_string_free (&mech);
1649          return FALSE;
1650        }
1651
1652      if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
1653        goto failed;
1654
1655      _dbus_string_skip_blank (args, i, &i);
1656      if (!_dbus_string_copy (args, i, &hex_response, 0))
1657        goto failed;
1658
1659      auth->mech = find_mech (&mech, auth->allowed_mechs);
1660      if (auth->mech != NULL)
1661        {
1662          _dbus_verbose ("%s: Trying mechanism %s\n",
1663                         DBUS_AUTH_NAME (auth),
1664                         auth->mech->mechanism);
1665
1666          if (!process_data (auth, &hex_response,
1667                             auth->mech->server_data_func))
1668            goto failed;
1669        }
1670      else
1671        {
1672          /* Unsupported mechanism */
1673          _dbus_verbose ("%s: Unsupported mechanism %s\n",
1674                         DBUS_AUTH_NAME (auth),
1675                         _dbus_string_get_const_data (&mech));
1676
1677          if (!send_rejected (auth))
1678            goto failed;
1679        }
1680
1681      _dbus_string_free (&mech);
1682      _dbus_string_free (&hex_response);
1683
1684      return TRUE;
1685
1686    failed:
1687      auth->mech = NULL;
1688      _dbus_string_free (&mech);
1689      _dbus_string_free (&hex_response);
1690      return FALSE;
1691    }
1692}
1693
1694static dbus_bool_t
1695handle_server_state_waiting_for_auth  (DBusAuth         *auth,
1696                                       DBusAuthCommand   command,
1697                                       const DBusString *args)
1698{
1699  switch (command)
1700    {
1701    case DBUS_AUTH_COMMAND_AUTH:
1702      return handle_auth (auth, args);
1703
1704    case DBUS_AUTH_COMMAND_CANCEL:
1705    case DBUS_AUTH_COMMAND_DATA:
1706      return send_error (auth, "Not currently in an auth conversation");
1707
1708    case DBUS_AUTH_COMMAND_BEGIN:
1709      goto_state (auth, &common_state_need_disconnect);
1710      return TRUE;
1711
1712    case DBUS_AUTH_COMMAND_ERROR:
1713      return send_rejected (auth);
1714
1715    case DBUS_AUTH_COMMAND_REJECTED:
1716    case DBUS_AUTH_COMMAND_OK:
1717    case DBUS_AUTH_COMMAND_UNKNOWN:
1718    default:
1719      return send_error (auth, "Unknown command");
1720    }
1721}
1722
1723static dbus_bool_t
1724handle_server_state_waiting_for_data  (DBusAuth         *auth,
1725                                       DBusAuthCommand   command,
1726                                       const DBusString *args)
1727{
1728  switch (command)
1729    {
1730    case DBUS_AUTH_COMMAND_AUTH:
1731      return send_error (auth, "Sent AUTH while another AUTH in progress");
1732
1733    case DBUS_AUTH_COMMAND_CANCEL:
1734    case DBUS_AUTH_COMMAND_ERROR:
1735      return send_rejected (auth);
1736
1737    case DBUS_AUTH_COMMAND_DATA:
1738      return process_data (auth, args, auth->mech->server_data_func);
1739
1740    case DBUS_AUTH_COMMAND_BEGIN:
1741      goto_state (auth, &common_state_need_disconnect);
1742      return TRUE;
1743
1744    case DBUS_AUTH_COMMAND_REJECTED:
1745    case DBUS_AUTH_COMMAND_OK:
1746    case DBUS_AUTH_COMMAND_UNKNOWN:
1747    default:
1748      return send_error (auth, "Unknown command");
1749    }
1750}
1751
1752static dbus_bool_t
1753handle_server_state_waiting_for_begin (DBusAuth         *auth,
1754                                       DBusAuthCommand   command,
1755                                       const DBusString *args)
1756{
1757  switch (command)
1758    {
1759    case DBUS_AUTH_COMMAND_AUTH:
1760      return send_error (auth, "Sent AUTH while expecting BEGIN");
1761
1762    case DBUS_AUTH_COMMAND_DATA:
1763      return send_error (auth, "Sent DATA while expecting BEGIN");
1764
1765    case DBUS_AUTH_COMMAND_BEGIN:
1766      goto_state (auth, &common_state_authenticated);
1767      return TRUE;
1768
1769    case DBUS_AUTH_COMMAND_REJECTED:
1770    case DBUS_AUTH_COMMAND_OK:
1771    case DBUS_AUTH_COMMAND_UNKNOWN:
1772    default:
1773      return send_error (auth, "Unknown command");
1774
1775    case DBUS_AUTH_COMMAND_CANCEL:
1776    case DBUS_AUTH_COMMAND_ERROR:
1777      return send_rejected (auth);
1778    }
1779}
1780
1781/* return FALSE if no memory, TRUE if all OK */
1782static dbus_bool_t
1783get_word (const DBusString *str,
1784          int              *start,
1785          DBusString       *word)
1786{
1787  int i;
1788
1789  _dbus_string_skip_blank (str, *start, start);
1790  _dbus_string_find_blank (str, *start, &i);
1791
1792  if (i > *start)
1793    {
1794      if (!_dbus_string_copy_len (str, *start, i - *start, word, 0))
1795        return FALSE;
1796
1797      *start = i;
1798    }
1799
1800  return TRUE;
1801}
1802
1803static dbus_bool_t
1804record_mechanisms (DBusAuth         *auth,
1805                   const DBusString *args)
1806{
1807  int next;
1808  int len;
1809
1810  if (auth->already_got_mechanisms)
1811    return TRUE;
1812
1813  len = _dbus_string_get_length (args);
1814
1815  next = 0;
1816  while (next < len)
1817    {
1818      DBusString m;
1819      const DBusAuthMechanismHandler *mech;
1820
1821      if (!_dbus_string_init (&m))
1822        goto nomem;
1823
1824      if (!get_word (args, &next, &m))
1825        {
1826          _dbus_string_free (&m);
1827          goto nomem;
1828        }
1829
1830      mech = find_mech (&m, auth->allowed_mechs);
1831
1832      if (mech != NULL)
1833        {
1834          /* FIXME right now we try mechanisms in the order
1835           * the server lists them; should we do them in
1836           * some more deterministic order?
1837           *
1838           * Probably in all_mechanisms order, our order of
1839           * preference. Of course when the server is us,
1840           * it lists things in that order anyhow.
1841           */
1842
1843          if (mech != &all_mechanisms[0])
1844            {
1845              _dbus_verbose ("%s: Adding mechanism %s to list we will try\n",
1846                             DBUS_AUTH_NAME (auth), mech->mechanism);
1847
1848              if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1849                                      (void*) mech))
1850                {
1851                  _dbus_string_free (&m);
1852                  goto nomem;
1853                }
1854            }
1855          else
1856            {
1857              _dbus_verbose ("%s: Already tried mechanism %s; not adding to list we will try\n",
1858                             DBUS_AUTH_NAME (auth), mech->mechanism);
1859            }
1860        }
1861      else
1862        {
1863          _dbus_verbose ("%s: Server offered mechanism \"%s\" that we don't know how to use\n",
1864                         DBUS_AUTH_NAME (auth),
1865                         _dbus_string_get_const_data (&m));
1866        }
1867
1868      _dbus_string_free (&m);
1869    }
1870
1871  auth->already_got_mechanisms = TRUE;
1872
1873  return TRUE;
1874
1875 nomem:
1876  _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1877
1878  return FALSE;
1879}
1880
1881static dbus_bool_t
1882process_rejected (DBusAuth *auth, const DBusString *args)
1883{
1884  const DBusAuthMechanismHandler *mech;
1885  DBusAuthClient *client;
1886
1887  client = DBUS_AUTH_CLIENT (auth);
1888
1889  if (!auth->already_got_mechanisms)
1890    {
1891      if (!record_mechanisms (auth, args))
1892        return FALSE;
1893    }
1894
1895  if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
1896    {
1897      mech = client->mechs_to_try->data;
1898
1899      if (!send_auth (auth, mech))
1900        return FALSE;
1901
1902      _dbus_list_pop_first (&client->mechs_to_try);
1903
1904      _dbus_verbose ("%s: Trying mechanism %s\n",
1905                     DBUS_AUTH_NAME (auth),
1906                     mech->mechanism);
1907    }
1908  else
1909    {
1910      /* Give up */
1911      _dbus_verbose ("%s: Disconnecting because we are out of mechanisms to try using\n",
1912                     DBUS_AUTH_NAME (auth));
1913      goto_state (auth, &common_state_need_disconnect);
1914    }
1915
1916  return TRUE;
1917}
1918
1919
1920static dbus_bool_t
1921handle_client_state_waiting_for_data (DBusAuth         *auth,
1922                                      DBusAuthCommand   command,
1923                                      const DBusString *args)
1924{
1925  _dbus_assert (auth->mech != NULL);
1926
1927  switch (command)
1928    {
1929    case DBUS_AUTH_COMMAND_DATA:
1930      return process_data (auth, args, auth->mech->client_data_func);
1931
1932    case DBUS_AUTH_COMMAND_REJECTED:
1933      return process_rejected (auth, args);
1934
1935    case DBUS_AUTH_COMMAND_OK:
1936      return send_begin (auth, args);
1937
1938    case DBUS_AUTH_COMMAND_ERROR:
1939      return send_cancel (auth);
1940
1941    case DBUS_AUTH_COMMAND_AUTH:
1942    case DBUS_AUTH_COMMAND_CANCEL:
1943    case DBUS_AUTH_COMMAND_BEGIN:
1944    case DBUS_AUTH_COMMAND_UNKNOWN:
1945    default:
1946      return send_error (auth, "Unknown command");
1947    }
1948}
1949
1950static dbus_bool_t
1951handle_client_state_waiting_for_ok (DBusAuth         *auth,
1952                                    DBusAuthCommand   command,
1953                                    const DBusString *args)
1954{
1955  switch (command)
1956    {
1957    case DBUS_AUTH_COMMAND_REJECTED:
1958      return process_rejected (auth, args);
1959
1960    case DBUS_AUTH_COMMAND_OK:
1961      return send_begin (auth, args);
1962
1963    case DBUS_AUTH_COMMAND_DATA:
1964    case DBUS_AUTH_COMMAND_ERROR:
1965      return send_cancel (auth);
1966
1967    case DBUS_AUTH_COMMAND_AUTH:
1968    case DBUS_AUTH_COMMAND_CANCEL:
1969    case DBUS_AUTH_COMMAND_BEGIN:
1970    case DBUS_AUTH_COMMAND_UNKNOWN:
1971    default:
1972      return send_error (auth, "Unknown command");
1973    }
1974}
1975
1976static dbus_bool_t
1977handle_client_state_waiting_for_reject (DBusAuth         *auth,
1978                                        DBusAuthCommand   command,
1979                                        const DBusString *args)
1980{
1981  switch (command)
1982    {
1983    case DBUS_AUTH_COMMAND_REJECTED:
1984      return process_rejected (auth, args);
1985
1986    case DBUS_AUTH_COMMAND_AUTH:
1987    case DBUS_AUTH_COMMAND_CANCEL:
1988    case DBUS_AUTH_COMMAND_DATA:
1989    case DBUS_AUTH_COMMAND_BEGIN:
1990    case DBUS_AUTH_COMMAND_OK:
1991    case DBUS_AUTH_COMMAND_ERROR:
1992    case DBUS_AUTH_COMMAND_UNKNOWN:
1993    default:
1994      goto_state (auth, &common_state_need_disconnect);
1995      return TRUE;
1996    }
1997}
1998
1999/**
2000 * Mapping from command name to enum
2001 */
2002typedef struct {
2003  const char *name;        /**< Name of the command */
2004  DBusAuthCommand command; /**< Corresponding enum */
2005} DBusAuthCommandName;
2006
2007static const DBusAuthCommandName auth_command_names[] = {
2008  { "AUTH",     DBUS_AUTH_COMMAND_AUTH },
2009  { "CANCEL",   DBUS_AUTH_COMMAND_CANCEL },
2010  { "DATA",     DBUS_AUTH_COMMAND_DATA },
2011  { "BEGIN",    DBUS_AUTH_COMMAND_BEGIN },
2012  { "REJECTED", DBUS_AUTH_COMMAND_REJECTED },
2013  { "OK",       DBUS_AUTH_COMMAND_OK },
2014  { "ERROR",    DBUS_AUTH_COMMAND_ERROR }
2015};
2016
2017static DBusAuthCommand
2018lookup_command_from_name (DBusString *command)
2019{
2020  int i;
2021
2022  for (i = 0; i < _DBUS_N_ELEMENTS (auth_command_names); i++)
2023    {
2024      if (_dbus_string_equal_c_str (command,
2025                                    auth_command_names[i].name))
2026        return auth_command_names[i].command;
2027    }
2028
2029  return DBUS_AUTH_COMMAND_UNKNOWN;
2030}
2031
2032static void
2033goto_state (DBusAuth *auth,
2034            const DBusAuthStateData *state)
2035{
2036  _dbus_verbose ("%s: going from state %s to state %s\n",
2037                 DBUS_AUTH_NAME (auth),
2038                 auth->state->name,
2039                 state->name);
2040
2041  auth->state = state;
2042}
2043
2044/* returns whether to call it again right away */
2045static dbus_bool_t
2046process_command (DBusAuth *auth)
2047{
2048  DBusAuthCommand command;
2049  DBusString line;
2050  DBusString args;
2051  int eol;
2052  int i, j;
2053  dbus_bool_t retval;
2054
2055  /* _dbus_verbose ("%s:   trying process_command()\n"); */
2056
2057  retval = FALSE;
2058
2059  eol = 0;
2060  if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
2061    return FALSE;
2062
2063  if (!_dbus_string_init (&line))
2064    {
2065      auth->needed_memory = TRUE;
2066      return FALSE;
2067    }
2068
2069  if (!_dbus_string_init (&args))
2070    {
2071      _dbus_string_free (&line);
2072      auth->needed_memory = TRUE;
2073      return FALSE;
2074    }
2075
2076  if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &line, 0))
2077    goto out;
2078
2079  if (!_dbus_string_validate_ascii (&line, 0,
2080                                    _dbus_string_get_length (&line)))
2081    {
2082      _dbus_verbose ("%s: Command contained non-ASCII chars or embedded nul\n",
2083                     DBUS_AUTH_NAME (auth));
2084      if (!send_error (auth, "Command contained non-ASCII"))
2085        goto out;
2086      else
2087        goto next_command;
2088    }
2089
2090  _dbus_verbose ("%s: got command \"%s\"\n",
2091                 DBUS_AUTH_NAME (auth),
2092                 _dbus_string_get_const_data (&line));
2093
2094  _dbus_string_find_blank (&line, 0, &i);
2095  _dbus_string_skip_blank (&line, i, &j);
2096
2097  if (j > i)
2098    _dbus_string_delete (&line, i, j - i);
2099
2100  if (!_dbus_string_move (&line, i, &args, 0))
2101    goto out;
2102
2103  /* FIXME 1.0 we should probably validate that only the allowed
2104   * chars are in the command name
2105   */
2106
2107  command = lookup_command_from_name (&line);
2108  if (!(* auth->state->handler) (auth, command, &args))
2109    goto out;
2110
2111 next_command:
2112
2113  /* We've succeeded in processing the whole command so drop it out
2114   * of the incoming buffer and return TRUE to try another command.
2115   */
2116
2117  _dbus_string_delete (&auth->incoming, 0, eol);
2118
2119  /* kill the \r\n */
2120  _dbus_string_delete (&auth->incoming, 0, 2);
2121
2122  retval = TRUE;
2123
2124 out:
2125  _dbus_string_free (&args);
2126  _dbus_string_free (&line);
2127
2128  if (!retval)
2129    auth->needed_memory = TRUE;
2130  else
2131    auth->needed_memory = FALSE;
2132
2133  return retval;
2134}
2135
2136
2137/** @} */
2138
2139/**
2140 * @addtogroup DBusAuth
2141 * @{
2142 */
2143
2144/**
2145 * Creates a new auth conversation object for the server side.
2146 * See doc/dbus-sasl-profile.txt for full details on what
2147 * this object does.
2148 *
2149 * @returns the new object or #NULL if no memory
2150 */
2151DBusAuth*
2152_dbus_auth_server_new (const DBusString *guid)
2153{
2154  DBusAuth *auth;
2155  DBusAuthServer *server_auth;
2156  DBusString guid_copy;
2157
2158  if (!_dbus_string_init (&guid_copy))
2159    return NULL;
2160
2161  if (!_dbus_string_copy (guid, 0, &guid_copy, 0))
2162    {
2163      _dbus_string_free (&guid_copy);
2164      return NULL;
2165    }
2166
2167  auth = _dbus_auth_new (sizeof (DBusAuthServer));
2168  if (auth == NULL)
2169    {
2170      _dbus_string_free (&guid_copy);
2171      return NULL;
2172    }
2173
2174  auth->side = auth_side_server;
2175  auth->state = &server_state_waiting_for_auth;
2176
2177  server_auth = DBUS_AUTH_SERVER (auth);
2178
2179  server_auth->guid = guid_copy;
2180
2181  /* perhaps this should be per-mechanism with a lower
2182   * max
2183   */
2184  server_auth->failures = 0;
2185  server_auth->max_failures = 6;
2186
2187  return auth;
2188}
2189
2190/**
2191 * Creates a new auth conversation object for the client side.
2192 * See doc/dbus-sasl-profile.txt for full details on what
2193 * this object does.
2194 *
2195 * @returns the new object or #NULL if no memory
2196 */
2197DBusAuth*
2198_dbus_auth_client_new (void)
2199{
2200  DBusAuth *auth;
2201  DBusString guid_str;
2202
2203  if (!_dbus_string_init (&guid_str))
2204    return NULL;
2205
2206  auth = _dbus_auth_new (sizeof (DBusAuthClient));
2207  if (auth == NULL)
2208    {
2209      _dbus_string_free (&guid_str);
2210      return NULL;
2211    }
2212
2213  DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str;
2214
2215  auth->side = auth_side_client;
2216  auth->state = &client_state_need_send_auth;
2217
2218  /* Start the auth conversation by sending AUTH for our default
2219   * mechanism */
2220  if (!send_auth (auth, &all_mechanisms[0]))
2221    {
2222      _dbus_auth_unref (auth);
2223      return NULL;
2224    }
2225
2226  return auth;
2227}
2228
2229/**
2230 * Increments the refcount of an auth object.
2231 *
2232 * @param auth the auth conversation
2233 * @returns the auth conversation
2234 */
2235DBusAuth *
2236_dbus_auth_ref (DBusAuth *auth)
2237{
2238  _dbus_assert (auth != NULL);
2239
2240  auth->refcount += 1;
2241
2242  return auth;
2243}
2244
2245/**
2246 * Decrements the refcount of an auth object.
2247 *
2248 * @param auth the auth conversation
2249 */
2250void
2251_dbus_auth_unref (DBusAuth *auth)
2252{
2253  _dbus_assert (auth != NULL);
2254  _dbus_assert (auth->refcount > 0);
2255
2256  auth->refcount -= 1;
2257  if (auth->refcount == 0)
2258    {
2259      shutdown_mech (auth);
2260
2261      if (DBUS_AUTH_IS_CLIENT (auth))
2262        {
2263          _dbus_string_free (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
2264          _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
2265        }
2266      else
2267        {
2268          _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
2269
2270          _dbus_string_free (& DBUS_AUTH_SERVER (auth)->guid);
2271        }
2272
2273      if (auth->keyring)
2274        _dbus_keyring_unref (auth->keyring);
2275
2276      _dbus_string_free (&auth->context);
2277      _dbus_string_free (&auth->challenge);
2278      _dbus_string_free (&auth->identity);
2279      _dbus_string_free (&auth->incoming);
2280      _dbus_string_free (&auth->outgoing);
2281
2282      dbus_free_string_array (auth->allowed_mechs);
2283
2284      _dbus_credentials_unref (auth->credentials);
2285      _dbus_credentials_unref (auth->authorized_identity);
2286      _dbus_credentials_unref (auth->desired_identity);
2287
2288      dbus_free (auth);
2289    }
2290}
2291
2292/**
2293 * Sets an array of authentication mechanism names
2294 * that we are willing to use.
2295 *
2296 * @param auth the auth conversation
2297 * @param mechanisms #NULL-terminated array of mechanism names
2298 * @returns #FALSE if no memory
2299 */
2300dbus_bool_t
2301_dbus_auth_set_mechanisms (DBusAuth    *auth,
2302                           const char **mechanisms)
2303{
2304  char **copy;
2305
2306  if (mechanisms != NULL)
2307    {
2308      copy = _dbus_dup_string_array (mechanisms);
2309      if (copy == NULL)
2310        return FALSE;
2311    }
2312  else
2313    copy = NULL;
2314
2315  dbus_free_string_array (auth->allowed_mechs);
2316
2317  auth->allowed_mechs = copy;
2318
2319  return TRUE;
2320}
2321
2322/**
2323 * @param auth the auth conversation object
2324 * @returns #TRUE if we're in a final state
2325 */
2326#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->state->handler == NULL)
2327
2328/**
2329 * Analyzes buffered input and moves the auth conversation forward,
2330 * returning the new state of the auth conversation.
2331 *
2332 * @param auth the auth conversation
2333 * @returns the new state
2334 */
2335DBusAuthState
2336_dbus_auth_do_work (DBusAuth *auth)
2337{
2338  auth->needed_memory = FALSE;
2339
2340  /* Max amount we'll buffer up before deciding someone's on crack */
2341#define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE)
2342
2343  do
2344    {
2345      if (DBUS_AUTH_IN_END_STATE (auth))
2346        break;
2347
2348      if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
2349          _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
2350        {
2351          goto_state (auth, &common_state_need_disconnect);
2352          _dbus_verbose ("%s: Disconnecting due to excessive data buffered in auth phase\n",
2353                         DBUS_AUTH_NAME (auth));
2354          break;
2355        }
2356    }
2357  while (process_command (auth));
2358
2359  if (auth->needed_memory)
2360    return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
2361  else if (_dbus_string_get_length (&auth->outgoing) > 0)
2362    return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
2363  else if (auth->state == &common_state_need_disconnect)
2364    return DBUS_AUTH_STATE_NEED_DISCONNECT;
2365  else if (auth->state == &common_state_authenticated)
2366    return DBUS_AUTH_STATE_AUTHENTICATED;
2367  else return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
2368}
2369
2370/**
2371 * Gets bytes that need to be sent to the peer we're conversing with.
2372 * After writing some bytes, _dbus_auth_bytes_sent() must be called
2373 * to notify the auth object that they were written.
2374 *
2375 * @param auth the auth conversation
2376 * @param str return location for a ref to the buffer to send
2377 * @returns #FALSE if nothing to send
2378 */
2379dbus_bool_t
2380_dbus_auth_get_bytes_to_send (DBusAuth          *auth,
2381                              const DBusString **str)
2382{
2383  _dbus_assert (auth != NULL);
2384  _dbus_assert (str != NULL);
2385
2386  *str = NULL;
2387
2388  if (_dbus_string_get_length (&auth->outgoing) == 0)
2389    return FALSE;
2390
2391  *str = &auth->outgoing;
2392
2393  return TRUE;
2394}
2395
2396/**
2397 * Notifies the auth conversation object that
2398 * the given number of bytes of the outgoing buffer
2399 * have been written out.
2400 *
2401 * @param auth the auth conversation
2402 * @param bytes_sent number of bytes written out
2403 */
2404void
2405_dbus_auth_bytes_sent (DBusAuth *auth,
2406                       int       bytes_sent)
2407{
2408  _dbus_verbose ("%s: Sent %d bytes of: %s\n",
2409                 DBUS_AUTH_NAME (auth),
2410                 bytes_sent,
2411                 _dbus_string_get_const_data (&auth->outgoing));
2412
2413  _dbus_string_delete (&auth->outgoing,
2414                       0, bytes_sent);
2415}
2416
2417/**
2418 * Get a buffer to be used for reading bytes from the peer we're conversing
2419 * with. Bytes should be appended to this buffer.
2420 *
2421 * @param auth the auth conversation
2422 * @param buffer return location for buffer to append bytes to
2423 */
2424void
2425_dbus_auth_get_buffer (DBusAuth     *auth,
2426                       DBusString **buffer)
2427{
2428  _dbus_assert (auth != NULL);
2429  _dbus_assert (!auth->buffer_outstanding);
2430
2431  *buffer = &auth->incoming;
2432
2433  auth->buffer_outstanding = TRUE;
2434}
2435
2436/**
2437 * Returns a buffer with new data read into it.
2438 *
2439 * @param auth the auth conversation
2440 * @param buffer the buffer being returned
2441 * @param bytes_read number of new bytes added
2442 */
2443void
2444_dbus_auth_return_buffer (DBusAuth               *auth,
2445                          DBusString             *buffer,
2446                          int                     bytes_read)
2447{
2448  _dbus_assert (buffer == &auth->incoming);
2449  _dbus_assert (auth->buffer_outstanding);
2450
2451  auth->buffer_outstanding = FALSE;
2452}
2453
2454/**
2455 * Returns leftover bytes that were not used as part of the auth
2456 * conversation.  These bytes will be part of the message stream
2457 * instead. This function may not be called until authentication has
2458 * succeeded.
2459 *
2460 * @param auth the auth conversation
2461 * @param str return location for pointer to string of unused bytes
2462 */
2463void
2464_dbus_auth_get_unused_bytes (DBusAuth           *auth,
2465                             const DBusString **str)
2466{
2467  if (!DBUS_AUTH_IN_END_STATE (auth))
2468    return;
2469
2470  *str = &auth->incoming;
2471}
2472
2473
2474/**
2475 * Gets rid of unused bytes returned by _dbus_auth_get_unused_bytes()
2476 * after we've gotten them and successfully moved them elsewhere.
2477 *
2478 * @param auth the auth conversation
2479 */
2480void
2481_dbus_auth_delete_unused_bytes (DBusAuth *auth)
2482{
2483  if (!DBUS_AUTH_IN_END_STATE (auth))
2484    return;
2485
2486  _dbus_string_set_length (&auth->incoming, 0);
2487}
2488
2489/**
2490 * Called post-authentication, indicates whether we need to encode
2491 * the message stream with _dbus_auth_encode_data() prior to
2492 * sending it to the peer.
2493 *
2494 * @param auth the auth conversation
2495 * @returns #TRUE if we need to encode the stream
2496 */
2497dbus_bool_t
2498_dbus_auth_needs_encoding (DBusAuth *auth)
2499{
2500  if (auth->state != &common_state_authenticated)
2501    return FALSE;
2502
2503  if (auth->mech != NULL)
2504    {
2505      if (DBUS_AUTH_IS_CLIENT (auth))
2506        return auth->mech->client_encode_func != NULL;
2507      else
2508        return auth->mech->server_encode_func != NULL;
2509    }
2510  else
2511    return FALSE;
2512}
2513
2514/**
2515 * Called post-authentication, encodes a block of bytes for sending to
2516 * the peer. If no encoding was negotiated, just copies the bytes
2517 * (you can avoid this by checking _dbus_auth_needs_encoding()).
2518 *
2519 * @param auth the auth conversation
2520 * @param plaintext the plain text data
2521 * @param encoded initialized string to where encoded data is appended
2522 * @returns #TRUE if we had enough memory and successfully encoded
2523 */
2524dbus_bool_t
2525_dbus_auth_encode_data (DBusAuth         *auth,
2526                        const DBusString *plaintext,
2527                        DBusString       *encoded)
2528{
2529  _dbus_assert (plaintext != encoded);
2530
2531  if (auth->state != &common_state_authenticated)
2532    return FALSE;
2533
2534  if (_dbus_auth_needs_encoding (auth))
2535    {
2536      if (DBUS_AUTH_IS_CLIENT (auth))
2537        return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
2538      else
2539        return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
2540    }
2541  else
2542    {
2543      return _dbus_string_copy (plaintext, 0, encoded,
2544                                _dbus_string_get_length (encoded));
2545    }
2546}
2547
2548/**
2549 * Called post-authentication, indicates whether we need to decode
2550 * the message stream with _dbus_auth_decode_data() after
2551 * receiving it from the peer.
2552 *
2553 * @param auth the auth conversation
2554 * @returns #TRUE if we need to encode the stream
2555 */
2556dbus_bool_t
2557_dbus_auth_needs_decoding (DBusAuth *auth)
2558{
2559  if (auth->state != &common_state_authenticated)
2560    return FALSE;
2561
2562  if (auth->mech != NULL)
2563    {
2564      if (DBUS_AUTH_IS_CLIENT (auth))
2565        return auth->mech->client_decode_func != NULL;
2566      else
2567        return auth->mech->server_decode_func != NULL;
2568    }
2569  else
2570    return FALSE;
2571}
2572
2573
2574/**
2575 * Called post-authentication, decodes a block of bytes received from
2576 * the peer. If no encoding was negotiated, just copies the bytes (you
2577 * can avoid this by checking _dbus_auth_needs_decoding()).
2578 *
2579 * @todo 1.0? We need to be able to distinguish "out of memory" error
2580 * from "the data is hosed" error.
2581 *
2582 * @param auth the auth conversation
2583 * @param encoded the encoded data
2584 * @param plaintext initialized string where decoded data is appended
2585 * @returns #TRUE if we had enough memory and successfully decoded
2586 */
2587dbus_bool_t
2588_dbus_auth_decode_data (DBusAuth         *auth,
2589                        const DBusString *encoded,
2590                        DBusString       *plaintext)
2591{
2592  _dbus_assert (plaintext != encoded);
2593
2594  if (auth->state != &common_state_authenticated)
2595    return FALSE;
2596
2597  if (_dbus_auth_needs_decoding (auth))
2598    {
2599      if (DBUS_AUTH_IS_CLIENT (auth))
2600        return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
2601      else
2602        return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
2603    }
2604  else
2605    {
2606      return _dbus_string_copy (encoded, 0, plaintext,
2607                                _dbus_string_get_length (plaintext));
2608    }
2609}
2610
2611/**
2612 * Sets credentials received via reliable means from the operating
2613 * system.
2614 *
2615 * @param auth the auth conversation
2616 * @param credentials the credentials received
2617 * @returns #FALSE on OOM
2618 */
2619dbus_bool_t
2620_dbus_auth_set_credentials (DBusAuth               *auth,
2621                            DBusCredentials        *credentials)
2622{
2623  _dbus_credentials_clear (auth->credentials);
2624  return _dbus_credentials_add_credentials (auth->credentials,
2625                                            credentials);
2626}
2627
2628/**
2629 * Gets the identity we authorized the client as.  Apps may have
2630 * different policies as to what identities they allow.
2631 *
2632 * Returned credentials are not a copy and should not be modified
2633 *
2634 * @param auth the auth conversation
2635 * @returns the credentials we've authorized BY REFERENCE do not modify
2636 */
2637DBusCredentials*
2638_dbus_auth_get_identity (DBusAuth               *auth)
2639{
2640  if (auth->state == &common_state_authenticated)
2641    {
2642      return auth->authorized_identity;
2643    }
2644  else
2645    {
2646      /* FIXME instead of this, keep an empty credential around that
2647       * doesn't require allocation or something
2648       */
2649      /* return empty credentials */
2650      _dbus_assert (_dbus_credentials_are_empty (auth->authorized_identity));
2651      return auth->authorized_identity;
2652    }
2653}
2654
2655/**
2656 * Gets the GUID from the server if we've authenticated; gets
2657 * #NULL otherwise.
2658 * @param auth the auth object
2659 * @returns the GUID in ASCII hex format
2660 */
2661const char*
2662_dbus_auth_get_guid_from_server (DBusAuth *auth)
2663{
2664  _dbus_assert (DBUS_AUTH_IS_CLIENT (auth));
2665
2666  if (auth->state == &common_state_authenticated)
2667    return _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
2668  else
2669    return NULL;
2670}
2671
2672/**
2673 * Sets the "authentication context" which scopes cookies
2674 * with the DBUS_COOKIE_SHA1 auth mechanism for example.
2675 *
2676 * @param auth the auth conversation
2677 * @param context the context
2678 * @returns #FALSE if no memory
2679 */
2680dbus_bool_t
2681_dbus_auth_set_context (DBusAuth               *auth,
2682                        const DBusString       *context)
2683{
2684  return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context),
2685                                   &auth->context, 0, _dbus_string_get_length (context));
2686}
2687
2688/** @} */
2689
2690/* tests in dbus-auth-util.c */
2691