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