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