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