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