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