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