added dbus support
[monky] / src / dbus / dbus-auth.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
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_user (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_credentials (auth->keyring,
559                                          auth->desired_identity))
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_for_credentials (auth->desired_identity,
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   DBusString username;
784   dbus_bool_t retval;
785
786   retval = FALSE;
787
788   if (!_dbus_string_init (&username))
789     return FALSE;
790   
791   if (!_dbus_append_user_from_current_process (&username))
792     goto out_0;
793
794   if (!_dbus_string_hex_encode (&username, 0,
795                                 response,
796                                 _dbus_string_get_length (response)))
797     goto out_0;
798
799   retval = TRUE;
800   
801  out_0:
802   _dbus_string_free (&username);
803   
804   return retval;
805 }
806
807 static dbus_bool_t
808 handle_client_data_cookie_sha1_mech (DBusAuth         *auth,
809                                      const DBusString *data)
810 {
811   /* The data we get from the server should be the cookie context
812    * name, the cookie ID, and the server challenge, separated by
813    * spaces. We send back our challenge string and the correct hash.
814    */
815   dbus_bool_t retval;
816   DBusString context;
817   DBusString cookie_id_str;
818   DBusString server_challenge;
819   DBusString client_challenge;
820   DBusString correct_hash;
821   DBusString tmp;
822   int i, j;
823   long val;
824   
825   retval = FALSE;                 
826   
827   if (!_dbus_string_find_blank (data, 0, &i))
828     {
829       if (send_error (auth,
830                       "Server did not send context/ID/challenge properly"))
831         retval = TRUE;
832       goto out_0;
833     }
834
835   if (!_dbus_string_init (&context))
836     goto out_0;
837
838   if (!_dbus_string_copy_len (data, 0, i,
839                               &context, 0))
840     goto out_1;
841   
842   _dbus_string_skip_blank (data, i, &i);
843   if (!_dbus_string_find_blank (data, i, &j))
844     {
845       if (send_error (auth,
846                       "Server did not send context/ID/challenge properly"))
847         retval = TRUE;
848       goto out_1;
849     }
850
851   if (!_dbus_string_init (&cookie_id_str))
852     goto out_1;
853   
854   if (!_dbus_string_copy_len (data, i, j - i,
855                               &cookie_id_str, 0))
856     goto out_2;  
857
858   if (!_dbus_string_init (&server_challenge))
859     goto out_2;
860
861   i = j;
862   _dbus_string_skip_blank (data, i, &i);
863   j = _dbus_string_get_length (data);
864
865   if (!_dbus_string_copy_len (data, i, j - i,
866                               &server_challenge, 0))
867     goto out_3;
868
869   if (!_dbus_keyring_validate_context (&context))
870     {
871       if (send_error (auth, "Server sent invalid cookie context"))
872         retval = TRUE;
873       goto out_3;
874     }
875
876   if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL))
877     {
878       if (send_error (auth, "Could not parse cookie ID as an integer"))
879         retval = TRUE;
880       goto out_3;
881     }
882
883   if (_dbus_string_get_length (&server_challenge) == 0)
884     {
885       if (send_error (auth, "Empty server challenge string"))
886         retval = TRUE;
887       goto out_3;
888     }
889
890   if (auth->keyring == NULL)
891     {
892       DBusError error;
893
894       dbus_error_init (&error);
895       auth->keyring = _dbus_keyring_new_for_credentials (NULL,
896                                                          &context,
897                                                          &error);
898
899       if (auth->keyring == NULL)
900         {
901           if (dbus_error_has_name (&error,
902                                    DBUS_ERROR_NO_MEMORY))
903             {
904               dbus_error_free (&error);
905               goto out_3;
906             }
907           else
908             {
909               _DBUS_ASSERT_ERROR_IS_SET (&error);
910
911               _dbus_verbose ("%s: Error loading keyring: %s\n",
912                              DBUS_AUTH_NAME (auth), error.message);
913               
914               if (send_error (auth, "Could not load cookie file"))
915                 retval = TRUE; /* retval is only about mem */
916               
917               dbus_error_free (&error);
918               goto out_3;
919             }
920         }
921       else
922         {
923           _dbus_assert (!dbus_error_is_set (&error));
924         }
925     }
926   
927   _dbus_assert (auth->keyring != NULL);
928   
929   if (!_dbus_string_init (&tmp))
930     goto out_3;
931   
932   if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
933     goto out_4;
934
935   if (!_dbus_string_init (&client_challenge))
936     goto out_4;
937
938   if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0))
939     goto out_5;
940
941   if (!_dbus_string_init (&correct_hash))
942     goto out_5;
943   
944   if (!sha1_compute_hash (auth, val,
945                           &server_challenge,
946                           &client_challenge,
947                           &correct_hash))
948     goto out_6;
949
950   if (_dbus_string_get_length (&correct_hash) == 0)
951     {
952       /* couldn't find the cookie ID or something */
953       if (send_error (auth, "Don't have the requested cookie ID"))
954         retval = TRUE;
955       goto out_6;
956     }
957   
958   _dbus_string_set_length (&tmp, 0);
959   
960   if (!_dbus_string_copy (&client_challenge, 0, &tmp,
961                           _dbus_string_get_length (&tmp)))
962     goto out_6;
963
964   if (!_dbus_string_append (&tmp, " "))
965     goto out_6;
966
967   if (!_dbus_string_copy (&correct_hash, 0, &tmp,
968                           _dbus_string_get_length (&tmp)))
969     goto out_6;
970
971   if (!send_data (auth, &tmp))
972     goto out_6;
973
974   retval = TRUE;
975
976  out_6:
977   _dbus_string_zero (&correct_hash);
978   _dbus_string_free (&correct_hash);
979  out_5:
980   _dbus_string_free (&client_challenge);
981  out_4:
982   _dbus_string_zero (&tmp);
983   _dbus_string_free (&tmp);
984  out_3:
985   _dbus_string_free (&server_challenge);
986  out_2:
987   _dbus_string_free (&cookie_id_str);
988  out_1:
989   _dbus_string_free (&context);
990  out_0:
991   return retval;
992 }
993
994 static void
995 handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth)
996 {
997   auth->cookie_id = -1;  
998   _dbus_string_set_length (&auth->challenge, 0);
999 }
1000
1001 /*
1002  * EXTERNAL mechanism
1003  */
1004
1005 static dbus_bool_t
1006 handle_server_data_external_mech (DBusAuth         *auth,
1007                                   const DBusString *data)
1008 {
1009   if (_dbus_credentials_are_anonymous (auth->credentials))
1010     {
1011       _dbus_verbose ("%s: no credentials, mechanism EXTERNAL can't authenticate\n",
1012                      DBUS_AUTH_NAME (auth));
1013       return send_rejected (auth);
1014     }
1015   
1016   if (_dbus_string_get_length (data) > 0)
1017     {
1018       if (_dbus_string_get_length (&auth->identity) > 0)
1019         {
1020           /* Tried to send two auth identities, wtf */
1021           _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
1022                          DBUS_AUTH_NAME (auth));
1023           return send_rejected (auth);
1024         }
1025       else
1026         {
1027           /* this is our auth identity */
1028           if (!_dbus_string_copy (data, 0, &auth->identity, 0))
1029             return FALSE;
1030         }
1031     }
1032
1033   /* Poke client for an auth identity, if none given */
1034   if (_dbus_string_get_length (&auth->identity) == 0 &&
1035       !auth->already_asked_for_initial_response)
1036     {
1037       if (send_data (auth, NULL))
1038         {
1039           _dbus_verbose ("%s: sending empty challenge asking client for auth identity\n",
1040                          DBUS_AUTH_NAME (auth));
1041           auth->already_asked_for_initial_response = TRUE;
1042           goto_state (auth, &server_state_waiting_for_data);
1043           return TRUE;
1044         }
1045       else
1046         return FALSE;
1047     }
1048
1049   _dbus_credentials_clear (auth->desired_identity);
1050   
1051   /* If auth->identity is still empty here, then client
1052    * responded with an empty string after we poked it for
1053    * an initial response. This means to try to auth the
1054    * identity provided in the credentials.
1055    */
1056   if (_dbus_string_get_length (&auth->identity) == 0)
1057     {
1058       if (!_dbus_credentials_add_credentials (auth->desired_identity,
1059                                               auth->credentials))
1060         {
1061           return FALSE; /* OOM */
1062         }
1063     }
1064   else
1065     {
1066       if (!_dbus_credentials_add_from_user (auth->desired_identity,
1067                                             &auth->identity))
1068         {
1069           _dbus_verbose ("%s: could not get credentials from uid string\n",
1070                          DBUS_AUTH_NAME (auth));
1071           return send_rejected (auth);
1072         }
1073     }
1074
1075   if (_dbus_credentials_are_anonymous (auth->desired_identity))
1076     {
1077       _dbus_verbose ("%s: desired user %s is no good\n",
1078                      DBUS_AUTH_NAME (auth),
1079                      _dbus_string_get_const_data (&auth->identity));
1080       return send_rejected (auth);
1081     }
1082   
1083   if (_dbus_credentials_are_superset (auth->credentials,
1084                                       auth->desired_identity))
1085     {
1086       /* client has authenticated */
1087       if (!_dbus_credentials_add_credentials (auth->authorized_identity,
1088                                               auth->desired_identity))
1089         return FALSE;
1090
1091       /* also copy process ID from the socket credentials
1092        */
1093       if (!_dbus_credentials_add_credential (auth->authorized_identity,
1094                                              DBUS_CREDENTIAL_UNIX_PROCESS_ID,
1095                                              auth->credentials))
1096         return FALSE;
1097
1098       /* also copy audit data from the socket credentials
1099        */
1100       if (!_dbus_credentials_add_credential (auth->authorized_identity,
1101                                              DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
1102                                              auth->credentials))
1103         return FALSE;
1104       
1105       if (!send_ok (auth))
1106         return FALSE;
1107
1108       _dbus_verbose ("%s: authenticated client based on socket credentials\n",
1109                      DBUS_AUTH_NAME (auth));
1110
1111       return TRUE;
1112     }
1113   else
1114     {
1115       _dbus_verbose ("%s: desired identity not found in socket credentials\n",
1116                      DBUS_AUTH_NAME (auth));
1117       return send_rejected (auth);
1118     }
1119 }
1120
1121 static void
1122 handle_server_shutdown_external_mech (DBusAuth *auth)
1123 {
1124
1125 }
1126
1127 static dbus_bool_t
1128 handle_client_initial_response_external_mech (DBusAuth         *auth,
1129                                               DBusString       *response)
1130 {
1131   /* We always append our UID as an initial response, so the server
1132    * doesn't have to send back an empty challenge to check whether we
1133    * want to specify an identity. i.e. this avoids a round trip that
1134    * the spec for the EXTERNAL mechanism otherwise requires.
1135    */
1136   DBusString plaintext;
1137
1138   if (!_dbus_string_init (&plaintext))
1139     return FALSE;
1140
1141   if (!_dbus_append_user_from_current_process (&plaintext))
1142     goto failed;
1143
1144   if (!_dbus_string_hex_encode (&plaintext, 0,
1145                                 response,
1146                                 _dbus_string_get_length (response)))
1147     goto failed;
1148
1149   _dbus_string_free (&plaintext);
1150   
1151   return TRUE;
1152
1153  failed:
1154   _dbus_string_free (&plaintext);
1155   return FALSE;  
1156 }
1157
1158 static dbus_bool_t
1159 handle_client_data_external_mech (DBusAuth         *auth,
1160                                   const DBusString *data)
1161 {
1162   
1163   return TRUE;
1164 }
1165
1166 static void
1167 handle_client_shutdown_external_mech (DBusAuth *auth)
1168 {
1169
1170 }
1171
1172 /*
1173  * ANONYMOUS mechanism
1174  */
1175
1176 static dbus_bool_t
1177 handle_server_data_anonymous_mech (DBusAuth         *auth,
1178                                    const DBusString *data)
1179 {  
1180   if (_dbus_string_get_length (data) > 0)
1181     {
1182       /* Client is allowed to send "trace" data, the only defined
1183        * meaning is that if it contains '@' it is an email address,
1184        * and otherwise it is anything else, and it's supposed to be
1185        * UTF-8
1186        */
1187       if (!_dbus_string_validate_utf8 (data, 0, _dbus_string_get_length (data)))
1188         {
1189           _dbus_verbose ("%s: Received invalid UTF-8 trace data from ANONYMOUS client\n",
1190                          DBUS_AUTH_NAME (auth));
1191
1192           {
1193             DBusString plaintext;
1194             DBusString encoded;
1195             _dbus_string_init_const (&plaintext, "D-Bus " VERSION);
1196             _dbus_string_init (&encoded);
1197             _dbus_string_hex_encode (&plaintext, 0,
1198                                      &encoded,
1199                                      0);
1200               _dbus_verbose ("%s: try '%s'\n",
1201                              DBUS_AUTH_NAME (auth), _dbus_string_get_const_data (&encoded));
1202           }
1203           return send_rejected (auth);
1204         }
1205       
1206       _dbus_verbose ("%s: ANONYMOUS client sent trace string: '%s'\n",
1207                      DBUS_AUTH_NAME (auth),
1208                      _dbus_string_get_const_data (data));
1209     }
1210
1211   /* We want to be anonymous (clear in case some other protocol got midway through I guess) */
1212   _dbus_credentials_clear (auth->desired_identity);
1213
1214   /* Copy process ID from the socket credentials
1215    */
1216   if (!_dbus_credentials_add_credential (auth->authorized_identity,
1217                                          DBUS_CREDENTIAL_UNIX_PROCESS_ID,
1218                                          auth->credentials))
1219     return FALSE;
1220   
1221   /* Anonymous is always allowed */
1222   if (!send_ok (auth))
1223     return FALSE;
1224
1225   _dbus_verbose ("%s: authenticated client as anonymous\n",
1226                  DBUS_AUTH_NAME (auth));
1227
1228   return TRUE;
1229 }
1230
1231 static void
1232 handle_server_shutdown_anonymous_mech (DBusAuth *auth)
1233 {
1234   
1235 }
1236
1237 static dbus_bool_t
1238 handle_client_initial_response_anonymous_mech (DBusAuth         *auth,
1239                                                DBusString       *response)
1240 {
1241   /* Our initial response is a "trace" string which must be valid UTF-8
1242    * and must be an email address if it contains '@'.
1243    * We just send the dbus implementation info, like a user-agent or
1244    * something, because... why not. There's nothing guaranteed here
1245    * though, we could change it later.
1246    */
1247   DBusString plaintext;
1248
1249   if (!_dbus_string_init (&plaintext))
1250     return FALSE;
1251
1252   if (!_dbus_string_append (&plaintext,
1253                             "libdbus " VERSION))
1254     goto failed;
1255
1256   if (!_dbus_string_hex_encode (&plaintext, 0,
1257                                 response,
1258                                 _dbus_string_get_length (response)))
1259     goto failed;
1260
1261   _dbus_string_free (&plaintext);
1262   
1263   return TRUE;
1264
1265  failed:
1266   _dbus_string_free (&plaintext);
1267   return FALSE;  
1268 }
1269
1270 static dbus_bool_t
1271 handle_client_data_anonymous_mech (DBusAuth         *auth,
1272                                   const DBusString *data)
1273 {
1274   
1275   return TRUE;
1276 }
1277
1278 static void
1279 handle_client_shutdown_anonymous_mech (DBusAuth *auth)
1280 {
1281   
1282 }
1283
1284 /* Put mechanisms here in order of preference.
1285  * Right now we have:
1286  *
1287  * - EXTERNAL checks socket credentials (or in the future, other info from the OS)
1288  * - DBUS_COOKIE_SHA1 uses a cookie in the home directory, like xauth or ICE
1289  * - ANONYMOUS checks nothing but doesn't auth the person as a user
1290  *
1291  * We might ideally add a mechanism to chain to Cyrus SASL so we can
1292  * use its mechanisms as well.
1293  * 
1294  */
1295 static const DBusAuthMechanismHandler
1296 all_mechanisms[] = {
1297   { "EXTERNAL",
1298     handle_server_data_external_mech,
1299     NULL, NULL,
1300     handle_server_shutdown_external_mech,
1301     handle_client_initial_response_external_mech,
1302     handle_client_data_external_mech,
1303     NULL, NULL,
1304     handle_client_shutdown_external_mech },
1305   { "DBUS_COOKIE_SHA1",
1306     handle_server_data_cookie_sha1_mech,
1307     NULL, NULL,
1308     handle_server_shutdown_cookie_sha1_mech,
1309     handle_client_initial_response_cookie_sha1_mech,
1310     handle_client_data_cookie_sha1_mech,
1311     NULL, NULL,
1312     handle_client_shutdown_cookie_sha1_mech },
1313   { "ANONYMOUS",
1314     handle_server_data_anonymous_mech,
1315     NULL, NULL,
1316     handle_server_shutdown_anonymous_mech,
1317     handle_client_initial_response_anonymous_mech,
1318     handle_client_data_anonymous_mech,
1319     NULL, NULL,
1320     handle_client_shutdown_anonymous_mech },  
1321   { NULL, NULL }
1322 };
1323
1324 static const DBusAuthMechanismHandler*
1325 find_mech (const DBusString  *name,
1326            char             **allowed_mechs)
1327 {
1328   int i;
1329   
1330   if (allowed_mechs != NULL &&
1331       !_dbus_string_array_contains ((const char**) allowed_mechs,
1332                                     _dbus_string_get_const_data (name)))
1333     return NULL;
1334   
1335   i = 0;
1336   while (all_mechanisms[i].mechanism != NULL)
1337     {      
1338       if (_dbus_string_equal_c_str (name,
1339                                     all_mechanisms[i].mechanism))
1340
1341         return &all_mechanisms[i];
1342       
1343       ++i;
1344     }
1345   
1346   return NULL;
1347 }
1348
1349 static dbus_bool_t
1350 send_auth (DBusAuth *auth, const DBusAuthMechanismHandler *mech)
1351 {
1352   DBusString auth_command;
1353
1354   if (!_dbus_string_init (&auth_command))
1355     return FALSE;
1356       
1357   if (!_dbus_string_append (&auth_command,
1358                             "AUTH "))
1359     {
1360       _dbus_string_free (&auth_command);
1361       return FALSE;
1362     }  
1363   
1364   if (!_dbus_string_append (&auth_command,
1365                             mech->mechanism))
1366     {
1367       _dbus_string_free (&auth_command);
1368       return FALSE;
1369     }
1370
1371   if (mech->client_initial_response_func != NULL)
1372     {
1373       if (!_dbus_string_append (&auth_command, " "))
1374         {
1375           _dbus_string_free (&auth_command);
1376           return FALSE;
1377         }
1378       
1379       if (!(* mech->client_initial_response_func) (auth, &auth_command))
1380         {
1381           _dbus_string_free (&auth_command);
1382           return FALSE;
1383         }
1384     }
1385   
1386   if (!_dbus_string_append (&auth_command,
1387                             "\r\n"))
1388     {
1389       _dbus_string_free (&auth_command);
1390       return FALSE;
1391     }
1392
1393   if (!_dbus_string_copy (&auth_command, 0,
1394                           &auth->outgoing,
1395                           _dbus_string_get_length (&auth->outgoing)))
1396     {
1397       _dbus_string_free (&auth_command);
1398       return FALSE;
1399     }
1400
1401   _dbus_string_free (&auth_command);
1402   shutdown_mech (auth);
1403   auth->mech = mech;      
1404   goto_state (auth, &client_state_waiting_for_data);
1405
1406   return TRUE;
1407 }
1408
1409 static dbus_bool_t
1410 send_data (DBusAuth *auth, DBusString *data)
1411 {
1412   int old_len;
1413
1414   if (data == NULL || _dbus_string_get_length (data) == 0)
1415     return _dbus_string_append (&auth->outgoing, "DATA\r\n");
1416   else
1417     {
1418       old_len = _dbus_string_get_length (&auth->outgoing);
1419       if (!_dbus_string_append (&auth->outgoing, "DATA "))
1420         goto out;
1421
1422       if (!_dbus_string_hex_encode (data, 0, &auth->outgoing,
1423                                     _dbus_string_get_length (&auth->outgoing)))
1424         goto out;
1425
1426       if (!_dbus_string_append (&auth->outgoing, "\r\n"))
1427         goto out;
1428
1429       return TRUE;
1430
1431     out:
1432       _dbus_string_set_length (&auth->outgoing, old_len);
1433
1434       return FALSE;
1435     }
1436 }
1437
1438 static dbus_bool_t
1439 send_rejected (DBusAuth *auth)
1440 {
1441   DBusString command;
1442   DBusAuthServer *server_auth;
1443   int i;
1444   
1445   if (!_dbus_string_init (&command))
1446     return FALSE;
1447   
1448   if (!_dbus_string_append (&command,
1449                             "REJECTED"))
1450     goto nomem;
1451
1452   i = 0;
1453   while (all_mechanisms[i].mechanism != NULL)
1454     {
1455       if (!_dbus_string_append (&command,
1456                                 " "))
1457         goto nomem;
1458
1459       if (!_dbus_string_append (&command,
1460                                 all_mechanisms[i].mechanism))
1461         goto nomem;
1462       
1463       ++i;
1464     }
1465   
1466   if (!_dbus_string_append (&command, "\r\n"))
1467     goto nomem;
1468
1469   if (!_dbus_string_copy (&command, 0, &auth->outgoing,
1470                           _dbus_string_get_length (&auth->outgoing)))
1471     goto nomem;
1472
1473   shutdown_mech (auth);
1474   
1475   _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
1476   server_auth = DBUS_AUTH_SERVER (auth);
1477   server_auth->failures += 1;
1478
1479   if (server_auth->failures >= server_auth->max_failures)
1480     goto_state (auth, &common_state_need_disconnect);
1481   else
1482     goto_state (auth, &server_state_waiting_for_auth);
1483
1484   _dbus_string_free (&command);
1485   
1486   return TRUE;
1487
1488  nomem:
1489   _dbus_string_free (&command);
1490   return FALSE;
1491 }
1492
1493 static dbus_bool_t
1494 send_error (DBusAuth *auth, const char *message)
1495 {
1496   return _dbus_string_append_printf (&auth->outgoing,
1497                                      "ERROR \"%s\"\r\n", message);
1498 }
1499
1500 static dbus_bool_t
1501 send_ok (DBusAuth *auth)
1502 {
1503   int orig_len;
1504
1505   orig_len = _dbus_string_get_length (&auth->outgoing);
1506   
1507   if (_dbus_string_append (&auth->outgoing, "OK ") &&
1508       _dbus_string_copy (& DBUS_AUTH_SERVER (auth)->guid,
1509                          0,
1510                          &auth->outgoing,
1511                          _dbus_string_get_length (&auth->outgoing)) &&
1512       _dbus_string_append (&auth->outgoing, "\r\n"))
1513     {
1514       goto_state (auth, &server_state_waiting_for_begin);
1515       return TRUE;
1516     }
1517   else
1518     {
1519       _dbus_string_set_length (&auth->outgoing, orig_len);
1520       return FALSE;
1521     }
1522 }
1523
1524 static dbus_bool_t
1525 send_begin (DBusAuth         *auth,
1526             const DBusString *args_from_ok)
1527 {
1528   int end_of_hex;
1529   
1530   /* "args_from_ok" should be the GUID, whitespace already pulled off the front */
1531   _dbus_assert (_dbus_string_get_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server) == 0);
1532
1533   /* We decode the hex string to binary, using guid_from_server as scratch... */
1534   
1535   end_of_hex = 0;
1536   if (!_dbus_string_hex_decode (args_from_ok, 0, &end_of_hex,
1537                                 & DBUS_AUTH_CLIENT (auth)->guid_from_server, 0))
1538     return FALSE;
1539
1540   /* now clear out the scratch */
1541   _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
1542   
1543   if (end_of_hex != _dbus_string_get_length (args_from_ok) ||
1544       end_of_hex == 0)
1545     {
1546       _dbus_verbose ("Bad GUID from server, parsed %d bytes and had %d bytes from server\n",
1547                      end_of_hex, _dbus_string_get_length (args_from_ok));
1548       goto_state (auth, &common_state_need_disconnect);
1549       return TRUE;
1550     }
1551
1552   if (_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0) &&
1553       _dbus_string_append (&auth->outgoing, "BEGIN\r\n"))
1554     {
1555       _dbus_verbose ("Got GUID '%s' from the server\n",
1556                      _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server));
1557       
1558       goto_state (auth, &common_state_authenticated);
1559       return TRUE;
1560     }
1561   else
1562     {
1563       _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
1564       return FALSE;
1565     }
1566 }
1567
1568 static dbus_bool_t
1569 send_cancel (DBusAuth *auth)
1570 {
1571   if (_dbus_string_append (&auth->outgoing, "CANCEL\r\n"))
1572     {
1573       goto_state (auth, &client_state_waiting_for_reject);
1574       return TRUE;
1575     }
1576   else
1577     return FALSE;
1578 }
1579
1580 static dbus_bool_t
1581 process_data (DBusAuth             *auth,
1582               const DBusString     *args,
1583               DBusAuthDataFunction  data_func)
1584 {
1585   int end;
1586   DBusString decoded;
1587
1588   if (!_dbus_string_init (&decoded))
1589     return FALSE;
1590
1591   if (!_dbus_string_hex_decode (args, 0, &end, &decoded, 0))
1592     {
1593       _dbus_string_free (&decoded);
1594       return FALSE;
1595     }
1596
1597   if (_dbus_string_get_length (args) != end)
1598     {
1599       _dbus_string_free (&decoded);
1600       if (!send_error (auth, "Invalid hex encoding"))
1601         return FALSE;
1602
1603       return TRUE;
1604     }
1605
1606 #ifdef DBUS_ENABLE_VERBOSE_MODE
1607   if (_dbus_string_validate_ascii (&decoded, 0,
1608                                    _dbus_string_get_length (&decoded)))
1609     _dbus_verbose ("%s: data: '%s'\n",
1610                    DBUS_AUTH_NAME (auth),
1611                    _dbus_string_get_const_data (&decoded));
1612 #endif
1613       
1614   if (!(* data_func) (auth, &decoded))
1615     {
1616       _dbus_string_free (&decoded);
1617       return FALSE;
1618     }
1619
1620   _dbus_string_free (&decoded);
1621   return TRUE;
1622 }
1623
1624 static dbus_bool_t
1625 handle_auth (DBusAuth *auth, const DBusString *args)
1626 {
1627   if (_dbus_string_get_length (args) == 0)
1628     {
1629       /* No args to the auth, send mechanisms */
1630       if (!send_rejected (auth))
1631         return FALSE;
1632
1633       return TRUE;
1634     }
1635   else
1636     {
1637       int i;
1638       DBusString mech;
1639       DBusString hex_response;
1640       
1641       _dbus_string_find_blank (args, 0, &i);
1642
1643       if (!_dbus_string_init (&mech))
1644         return FALSE;
1645
1646       if (!_dbus_string_init (&hex_response))
1647         {
1648           _dbus_string_free (&mech);
1649           return FALSE;
1650         }
1651       
1652       if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
1653         goto failed;
1654
1655       _dbus_string_skip_blank (args, i, &i);
1656       if (!_dbus_string_copy (args, i, &hex_response, 0))
1657         goto failed;
1658      
1659       auth->mech = find_mech (&mech, auth->allowed_mechs);
1660       if (auth->mech != NULL)
1661         {
1662           _dbus_verbose ("%s: Trying mechanism %s\n",
1663                          DBUS_AUTH_NAME (auth),
1664                          auth->mech->mechanism);
1665           
1666           if (!process_data (auth, &hex_response,
1667                              auth->mech->server_data_func))
1668             goto failed;
1669         }
1670       else
1671         {
1672           /* Unsupported mechanism */
1673           _dbus_verbose ("%s: Unsupported mechanism %s\n",
1674                          DBUS_AUTH_NAME (auth),
1675                          _dbus_string_get_const_data (&mech));
1676           
1677           if (!send_rejected (auth))
1678             goto failed;
1679         }
1680
1681       _dbus_string_free (&mech);      
1682       _dbus_string_free (&hex_response);
1683
1684       return TRUE;
1685       
1686     failed:
1687       auth->mech = NULL;
1688       _dbus_string_free (&mech);
1689       _dbus_string_free (&hex_response);
1690       return FALSE;
1691     }
1692 }
1693
1694 static dbus_bool_t
1695 handle_server_state_waiting_for_auth  (DBusAuth         *auth,
1696                                        DBusAuthCommand   command,
1697                                        const DBusString *args)
1698 {
1699   switch (command)
1700     {
1701     case DBUS_AUTH_COMMAND_AUTH:
1702       return handle_auth (auth, args);
1703
1704     case DBUS_AUTH_COMMAND_CANCEL:
1705     case DBUS_AUTH_COMMAND_DATA:
1706       return send_error (auth, "Not currently in an auth conversation");
1707
1708     case DBUS_AUTH_COMMAND_BEGIN:
1709       goto_state (auth, &common_state_need_disconnect);
1710       return TRUE;
1711
1712     case DBUS_AUTH_COMMAND_ERROR:
1713       return send_rejected (auth);
1714
1715     case DBUS_AUTH_COMMAND_REJECTED:
1716     case DBUS_AUTH_COMMAND_OK:
1717     case DBUS_AUTH_COMMAND_UNKNOWN:
1718     default:
1719       return send_error (auth, "Unknown command");
1720     }
1721 }
1722
1723 static dbus_bool_t
1724 handle_server_state_waiting_for_data  (DBusAuth         *auth,
1725                                        DBusAuthCommand   command,
1726                                        const DBusString *args)
1727 {
1728   switch (command)
1729     {
1730     case DBUS_AUTH_COMMAND_AUTH:
1731       return send_error (auth, "Sent AUTH while another AUTH in progress");
1732
1733     case DBUS_AUTH_COMMAND_CANCEL:
1734     case DBUS_AUTH_COMMAND_ERROR:
1735       return send_rejected (auth);
1736
1737     case DBUS_AUTH_COMMAND_DATA:
1738       return process_data (auth, args, auth->mech->server_data_func);
1739
1740     case DBUS_AUTH_COMMAND_BEGIN:
1741       goto_state (auth, &common_state_need_disconnect);
1742       return TRUE;
1743
1744     case DBUS_AUTH_COMMAND_REJECTED:
1745     case DBUS_AUTH_COMMAND_OK:
1746     case DBUS_AUTH_COMMAND_UNKNOWN:
1747     default:
1748       return send_error (auth, "Unknown command");
1749     }
1750 }
1751
1752 static dbus_bool_t
1753 handle_server_state_waiting_for_begin (DBusAuth         *auth,
1754                                        DBusAuthCommand   command,
1755                                        const DBusString *args)
1756 {
1757   switch (command)
1758     {
1759     case DBUS_AUTH_COMMAND_AUTH:
1760       return send_error (auth, "Sent AUTH while expecting BEGIN");
1761
1762     case DBUS_AUTH_COMMAND_DATA:
1763       return send_error (auth, "Sent DATA while expecting BEGIN");
1764
1765     case DBUS_AUTH_COMMAND_BEGIN:
1766       goto_state (auth, &common_state_authenticated);
1767       return TRUE;
1768
1769     case DBUS_AUTH_COMMAND_REJECTED:
1770     case DBUS_AUTH_COMMAND_OK:
1771     case DBUS_AUTH_COMMAND_UNKNOWN:
1772     default:
1773       return send_error (auth, "Unknown command");
1774
1775     case DBUS_AUTH_COMMAND_CANCEL:
1776     case DBUS_AUTH_COMMAND_ERROR:
1777       return send_rejected (auth);
1778     }
1779 }
1780
1781 /* return FALSE if no memory, TRUE if all OK */
1782 static dbus_bool_t
1783 get_word (const DBusString *str,
1784           int              *start,
1785           DBusString       *word)
1786 {
1787   int i;
1788
1789   _dbus_string_skip_blank (str, *start, start);
1790   _dbus_string_find_blank (str, *start, &i);
1791   
1792   if (i > *start)
1793     {
1794       if (!_dbus_string_copy_len (str, *start, i - *start, word, 0))
1795         return FALSE;
1796       
1797       *start = i;
1798     }
1799
1800   return TRUE;
1801 }
1802
1803 static dbus_bool_t
1804 record_mechanisms (DBusAuth         *auth,
1805                    const DBusString *args)
1806 {
1807   int next;
1808   int len;
1809
1810   if (auth->already_got_mechanisms)
1811     return TRUE;
1812   
1813   len = _dbus_string_get_length (args);
1814   
1815   next = 0;
1816   while (next < len)
1817     {
1818       DBusString m;
1819       const DBusAuthMechanismHandler *mech;
1820       
1821       if (!_dbus_string_init (&m))
1822         goto nomem;
1823       
1824       if (!get_word (args, &next, &m))
1825         {
1826           _dbus_string_free (&m);
1827           goto nomem;
1828         }
1829
1830       mech = find_mech (&m, auth->allowed_mechs);
1831
1832       if (mech != NULL)
1833         {
1834           /* FIXME right now we try mechanisms in the order
1835            * the server lists them; should we do them in
1836            * some more deterministic order?
1837            *
1838            * Probably in all_mechanisms order, our order of
1839            * preference. Of course when the server is us,
1840            * it lists things in that order anyhow.
1841            */
1842
1843           if (mech != &all_mechanisms[0])
1844             {
1845               _dbus_verbose ("%s: Adding mechanism %s to list we will try\n",
1846                              DBUS_AUTH_NAME (auth), mech->mechanism);
1847           
1848               if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1849                                       (void*) mech))
1850                 {
1851                   _dbus_string_free (&m);
1852                   goto nomem;
1853                 }
1854             }
1855           else
1856             {
1857               _dbus_verbose ("%s: Already tried mechanism %s; not adding to list we will try\n",
1858                              DBUS_AUTH_NAME (auth), mech->mechanism);
1859             }
1860         }
1861       else
1862         {
1863           _dbus_verbose ("%s: Server offered mechanism \"%s\" that we don't know how to use\n",
1864                          DBUS_AUTH_NAME (auth),
1865                          _dbus_string_get_const_data (&m));
1866         }
1867
1868       _dbus_string_free (&m);
1869     }
1870   
1871   auth->already_got_mechanisms = TRUE;
1872   
1873   return TRUE;
1874
1875  nomem:
1876   _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1877   
1878   return FALSE;
1879 }
1880
1881 static dbus_bool_t
1882 process_rejected (DBusAuth *auth, const DBusString *args)
1883 {
1884   const DBusAuthMechanismHandler *mech;
1885   DBusAuthClient *client;
1886
1887   client = DBUS_AUTH_CLIENT (auth);
1888
1889   if (!auth->already_got_mechanisms)
1890     {
1891       if (!record_mechanisms (auth, args))
1892         return FALSE;
1893     }
1894   
1895   if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
1896     {
1897       mech = client->mechs_to_try->data;
1898
1899       if (!send_auth (auth, mech))
1900         return FALSE;
1901
1902       _dbus_list_pop_first (&client->mechs_to_try);
1903
1904       _dbus_verbose ("%s: Trying mechanism %s\n",
1905                      DBUS_AUTH_NAME (auth),
1906                      mech->mechanism);
1907     }
1908   else
1909     {
1910       /* Give up */
1911       _dbus_verbose ("%s: Disconnecting because we are out of mechanisms to try using\n",
1912                      DBUS_AUTH_NAME (auth));
1913       goto_state (auth, &common_state_need_disconnect);
1914     }
1915   
1916   return TRUE;
1917 }
1918
1919
1920 static dbus_bool_t
1921 handle_client_state_waiting_for_data (DBusAuth         *auth,
1922                                       DBusAuthCommand   command,
1923                                       const DBusString *args)
1924 {
1925   _dbus_assert (auth->mech != NULL);
1926  
1927   switch (command)
1928     {
1929     case DBUS_AUTH_COMMAND_DATA:
1930       return process_data (auth, args, auth->mech->client_data_func);
1931
1932     case DBUS_AUTH_COMMAND_REJECTED:
1933       return process_rejected (auth, args);
1934
1935     case DBUS_AUTH_COMMAND_OK:
1936       return send_begin (auth, args);
1937
1938     case DBUS_AUTH_COMMAND_ERROR:
1939       return send_cancel (auth);
1940
1941     case DBUS_AUTH_COMMAND_AUTH:
1942     case DBUS_AUTH_COMMAND_CANCEL:
1943     case DBUS_AUTH_COMMAND_BEGIN:
1944     case DBUS_AUTH_COMMAND_UNKNOWN:
1945     default:
1946       return send_error (auth, "Unknown command");
1947     }
1948 }
1949
1950 static dbus_bool_t
1951 handle_client_state_waiting_for_ok (DBusAuth         *auth,
1952                                     DBusAuthCommand   command,
1953                                     const DBusString *args)
1954 {
1955   switch (command)
1956     {
1957     case DBUS_AUTH_COMMAND_REJECTED:
1958       return process_rejected (auth, args);
1959
1960     case DBUS_AUTH_COMMAND_OK:
1961       return send_begin (auth, args);
1962
1963     case DBUS_AUTH_COMMAND_DATA:
1964     case DBUS_AUTH_COMMAND_ERROR:
1965       return send_cancel (auth);
1966
1967     case DBUS_AUTH_COMMAND_AUTH:
1968     case DBUS_AUTH_COMMAND_CANCEL:
1969     case DBUS_AUTH_COMMAND_BEGIN:
1970     case DBUS_AUTH_COMMAND_UNKNOWN:
1971     default:
1972       return send_error (auth, "Unknown command");
1973     }
1974 }
1975
1976 static dbus_bool_t
1977 handle_client_state_waiting_for_reject (DBusAuth         *auth,
1978                                         DBusAuthCommand   command,
1979                                         const DBusString *args)
1980 {
1981   switch (command)
1982     {
1983     case DBUS_AUTH_COMMAND_REJECTED:
1984       return process_rejected (auth, args);
1985       
1986     case DBUS_AUTH_COMMAND_AUTH:
1987     case DBUS_AUTH_COMMAND_CANCEL:
1988     case DBUS_AUTH_COMMAND_DATA:
1989     case DBUS_AUTH_COMMAND_BEGIN:
1990     case DBUS_AUTH_COMMAND_OK:
1991     case DBUS_AUTH_COMMAND_ERROR:
1992     case DBUS_AUTH_COMMAND_UNKNOWN:
1993     default:
1994       goto_state (auth, &common_state_need_disconnect);
1995       return TRUE;
1996     }
1997 }
1998
1999 /**
2000  * Mapping from command name to enum
2001  */
2002 typedef struct {
2003   const char *name;        /**< Name of the command */
2004   DBusAuthCommand command; /**< Corresponding enum */
2005 } DBusAuthCommandName;
2006
2007 static const DBusAuthCommandName auth_command_names[] = {
2008   { "AUTH",     DBUS_AUTH_COMMAND_AUTH },
2009   { "CANCEL",   DBUS_AUTH_COMMAND_CANCEL },
2010   { "DATA",     DBUS_AUTH_COMMAND_DATA },
2011   { "BEGIN",    DBUS_AUTH_COMMAND_BEGIN },
2012   { "REJECTED", DBUS_AUTH_COMMAND_REJECTED },
2013   { "OK",       DBUS_AUTH_COMMAND_OK },
2014   { "ERROR",    DBUS_AUTH_COMMAND_ERROR }
2015 };
2016
2017 static DBusAuthCommand
2018 lookup_command_from_name (DBusString *command)
2019 {
2020   int i;
2021
2022   for (i = 0; i < _DBUS_N_ELEMENTS (auth_command_names); i++)
2023     {
2024       if (_dbus_string_equal_c_str (command,
2025                                     auth_command_names[i].name))
2026         return auth_command_names[i].command;
2027     }
2028
2029   return DBUS_AUTH_COMMAND_UNKNOWN;
2030 }
2031
2032 static void
2033 goto_state (DBusAuth *auth,
2034             const DBusAuthStateData *state)
2035 {
2036   _dbus_verbose ("%s: going from state %s to state %s\n",
2037                  DBUS_AUTH_NAME (auth),
2038                  auth->state->name,
2039                  state->name);
2040
2041   auth->state = state;
2042 }
2043
2044 /* returns whether to call it again right away */
2045 static dbus_bool_t
2046 process_command (DBusAuth *auth)
2047 {
2048   DBusAuthCommand command;
2049   DBusString line;
2050   DBusString args;
2051   int eol;
2052   int i, j;
2053   dbus_bool_t retval;
2054
2055   /* _dbus_verbose ("%s:   trying process_command()\n"); */
2056   
2057   retval = FALSE;
2058   
2059   eol = 0;
2060   if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
2061     return FALSE;
2062   
2063   if (!_dbus_string_init (&line))
2064     {
2065       auth->needed_memory = TRUE;
2066       return FALSE;
2067     }
2068
2069   if (!_dbus_string_init (&args))
2070     {
2071       _dbus_string_free (&line);
2072       auth->needed_memory = TRUE;
2073       return FALSE;
2074     }
2075   
2076   if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &line, 0))
2077     goto out;
2078
2079   if (!_dbus_string_validate_ascii (&line, 0,
2080                                     _dbus_string_get_length (&line)))
2081     {
2082       _dbus_verbose ("%s: Command contained non-ASCII chars or embedded nul\n",
2083                      DBUS_AUTH_NAME (auth));
2084       if (!send_error (auth, "Command contained non-ASCII"))
2085         goto out;
2086       else
2087         goto next_command;
2088     }
2089   
2090   _dbus_verbose ("%s: got command \"%s\"\n",
2091                  DBUS_AUTH_NAME (auth),
2092                  _dbus_string_get_const_data (&line));
2093   
2094   _dbus_string_find_blank (&line, 0, &i);
2095   _dbus_string_skip_blank (&line, i, &j);
2096
2097   if (j > i)
2098     _dbus_string_delete (&line, i, j - i);
2099   
2100   if (!_dbus_string_move (&line, i, &args, 0))
2101     goto out;
2102
2103   /* FIXME 1.0 we should probably validate that only the allowed
2104    * chars are in the command name
2105    */
2106   
2107   command = lookup_command_from_name (&line);
2108   if (!(* auth->state->handler) (auth, command, &args))
2109     goto out;
2110
2111  next_command:
2112   
2113   /* We've succeeded in processing the whole command so drop it out
2114    * of the incoming buffer and return TRUE to try another command.
2115    */
2116
2117   _dbus_string_delete (&auth->incoming, 0, eol);
2118   
2119   /* kill the \r\n */
2120   _dbus_string_delete (&auth->incoming, 0, 2);
2121
2122   retval = TRUE;
2123   
2124  out:
2125   _dbus_string_free (&args);
2126   _dbus_string_free (&line);
2127
2128   if (!retval)
2129     auth->needed_memory = TRUE;
2130   else
2131     auth->needed_memory = FALSE;
2132   
2133   return retval;
2134 }
2135
2136
2137 /** @} */
2138
2139 /**
2140  * @addtogroup DBusAuth
2141  * @{
2142  */
2143
2144 /**
2145  * Creates a new auth conversation object for the server side.
2146  * See doc/dbus-sasl-profile.txt for full details on what
2147  * this object does.
2148  *
2149  * @returns the new object or #NULL if no memory
2150  */
2151 DBusAuth*
2152 _dbus_auth_server_new (const DBusString *guid)
2153 {
2154   DBusAuth *auth;
2155   DBusAuthServer *server_auth;
2156   DBusString guid_copy;
2157
2158   if (!_dbus_string_init (&guid_copy))
2159     return NULL;
2160
2161   if (!_dbus_string_copy (guid, 0, &guid_copy, 0))
2162     {
2163       _dbus_string_free (&guid_copy);
2164       return NULL;
2165     }
2166
2167   auth = _dbus_auth_new (sizeof (DBusAuthServer));
2168   if (auth == NULL)
2169     {
2170       _dbus_string_free (&guid_copy);
2171       return NULL;
2172     }
2173   
2174   auth->side = auth_side_server;
2175   auth->state = &server_state_waiting_for_auth;
2176
2177   server_auth = DBUS_AUTH_SERVER (auth);
2178
2179   server_auth->guid = guid_copy;
2180   
2181   /* perhaps this should be per-mechanism with a lower
2182    * max
2183    */
2184   server_auth->failures = 0;
2185   server_auth->max_failures = 6;
2186   
2187   return auth;
2188 }
2189
2190 /**
2191  * Creates a new auth conversation object for the client side.
2192  * See doc/dbus-sasl-profile.txt for full details on what
2193  * this object does.
2194  *
2195  * @returns the new object or #NULL if no memory
2196  */
2197 DBusAuth*
2198 _dbus_auth_client_new (void)
2199 {
2200   DBusAuth *auth;
2201   DBusString guid_str;
2202
2203   if (!_dbus_string_init (&guid_str))
2204     return NULL;
2205
2206   auth = _dbus_auth_new (sizeof (DBusAuthClient));
2207   if (auth == NULL)
2208     {
2209       _dbus_string_free (&guid_str);
2210       return NULL;
2211     }
2212
2213   DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str;
2214
2215   auth->side = auth_side_client;
2216   auth->state = &client_state_need_send_auth;
2217
2218   /* Start the auth conversation by sending AUTH for our default
2219    * mechanism */
2220   if (!send_auth (auth, &all_mechanisms[0]))
2221     {
2222       _dbus_auth_unref (auth);
2223       return NULL;
2224     }
2225   
2226   return auth;
2227 }
2228
2229 /**
2230  * Increments the refcount of an auth object.
2231  *
2232  * @param auth the auth conversation
2233  * @returns the auth conversation
2234  */
2235 DBusAuth *
2236 _dbus_auth_ref (DBusAuth *auth)
2237 {
2238   _dbus_assert (auth != NULL);
2239   
2240   auth->refcount += 1;
2241   
2242   return auth;
2243 }
2244
2245 /**
2246  * Decrements the refcount of an auth object.
2247  *
2248  * @param auth the auth conversation
2249  */
2250 void
2251 _dbus_auth_unref (DBusAuth *auth)
2252 {
2253   _dbus_assert (auth != NULL);
2254   _dbus_assert (auth->refcount > 0);
2255
2256   auth->refcount -= 1;
2257   if (auth->refcount == 0)
2258     {
2259       shutdown_mech (auth);
2260
2261       if (DBUS_AUTH_IS_CLIENT (auth))
2262         {
2263           _dbus_string_free (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
2264           _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
2265         }
2266       else
2267         {
2268           _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
2269
2270           _dbus_string_free (& DBUS_AUTH_SERVER (auth)->guid);
2271         }
2272
2273       if (auth->keyring)
2274         _dbus_keyring_unref (auth->keyring);
2275
2276       _dbus_string_free (&auth->context);
2277       _dbus_string_free (&auth->challenge);
2278       _dbus_string_free (&auth->identity);
2279       _dbus_string_free (&auth->incoming);
2280       _dbus_string_free (&auth->outgoing);
2281
2282       dbus_free_string_array (auth->allowed_mechs);
2283
2284       _dbus_credentials_unref (auth->credentials);
2285       _dbus_credentials_unref (auth->authorized_identity);
2286       _dbus_credentials_unref (auth->desired_identity);
2287       
2288       dbus_free (auth);
2289     }
2290 }
2291
2292 /**
2293  * Sets an array of authentication mechanism names
2294  * that we are willing to use.
2295  *
2296  * @param auth the auth conversation
2297  * @param mechanisms #NULL-terminated array of mechanism names
2298  * @returns #FALSE if no memory
2299  */
2300 dbus_bool_t
2301 _dbus_auth_set_mechanisms (DBusAuth    *auth,
2302                            const char **mechanisms)
2303 {
2304   char **copy;
2305
2306   if (mechanisms != NULL)
2307     {
2308       copy = _dbus_dup_string_array (mechanisms);
2309       if (copy == NULL)
2310         return FALSE;
2311     }
2312   else
2313     copy = NULL;
2314   
2315   dbus_free_string_array (auth->allowed_mechs);
2316
2317   auth->allowed_mechs = copy;
2318
2319   return TRUE;
2320 }
2321
2322 /**
2323  * @param auth the auth conversation object
2324  * @returns #TRUE if we're in a final state
2325  */
2326 #define DBUS_AUTH_IN_END_STATE(auth) ((auth)->state->handler == NULL)
2327
2328 /**
2329  * Analyzes buffered input and moves the auth conversation forward,
2330  * returning the new state of the auth conversation.
2331  *
2332  * @param auth the auth conversation
2333  * @returns the new state
2334  */
2335 DBusAuthState
2336 _dbus_auth_do_work (DBusAuth *auth)
2337 {
2338   auth->needed_memory = FALSE;
2339
2340   /* Max amount we'll buffer up before deciding someone's on crack */
2341 #define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE)
2342
2343   do
2344     {
2345       if (DBUS_AUTH_IN_END_STATE (auth))
2346         break;
2347       
2348       if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
2349           _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
2350         {
2351           goto_state (auth, &common_state_need_disconnect);
2352           _dbus_verbose ("%s: Disconnecting due to excessive data buffered in auth phase\n",
2353                          DBUS_AUTH_NAME (auth));
2354           break;
2355         }
2356     }
2357   while (process_command (auth));
2358
2359   if (auth->needed_memory)
2360     return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
2361   else if (_dbus_string_get_length (&auth->outgoing) > 0)
2362     return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
2363   else if (auth->state == &common_state_need_disconnect)
2364     return DBUS_AUTH_STATE_NEED_DISCONNECT;
2365   else if (auth->state == &common_state_authenticated)
2366     return DBUS_AUTH_STATE_AUTHENTICATED;
2367   else return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
2368 }
2369
2370 /**
2371  * Gets bytes that need to be sent to the peer we're conversing with.
2372  * After writing some bytes, _dbus_auth_bytes_sent() must be called
2373  * to notify the auth object that they were written.
2374  *
2375  * @param auth the auth conversation
2376  * @param str return location for a ref to the buffer to send
2377  * @returns #FALSE if nothing to send
2378  */
2379 dbus_bool_t
2380 _dbus_auth_get_bytes_to_send (DBusAuth          *auth,
2381                               const DBusString **str)
2382 {
2383   _dbus_assert (auth != NULL);
2384   _dbus_assert (str != NULL);
2385
2386   *str = NULL;
2387   
2388   if (_dbus_string_get_length (&auth->outgoing) == 0)
2389     return FALSE;
2390
2391   *str = &auth->outgoing;
2392
2393   return TRUE;
2394 }
2395
2396 /**
2397  * Notifies the auth conversation object that
2398  * the given number of bytes of the outgoing buffer
2399  * have been written out.
2400  *
2401  * @param auth the auth conversation
2402  * @param bytes_sent number of bytes written out
2403  */
2404 void
2405 _dbus_auth_bytes_sent (DBusAuth *auth,
2406                        int       bytes_sent)
2407 {
2408   _dbus_verbose ("%s: Sent %d bytes of: %s\n",
2409                  DBUS_AUTH_NAME (auth),
2410                  bytes_sent,
2411                  _dbus_string_get_const_data (&auth->outgoing));
2412   
2413   _dbus_string_delete (&auth->outgoing,
2414                        0, bytes_sent);
2415 }
2416
2417 /**
2418  * Get a buffer to be used for reading bytes from the peer we're conversing
2419  * with. Bytes should be appended to this buffer.
2420  *
2421  * @param auth the auth conversation
2422  * @param buffer return location for buffer to append bytes to
2423  */
2424 void
2425 _dbus_auth_get_buffer (DBusAuth     *auth,
2426                        DBusString **buffer)
2427 {
2428   _dbus_assert (auth != NULL);
2429   _dbus_assert (!auth->buffer_outstanding);
2430   
2431   *buffer = &auth->incoming;
2432
2433   auth->buffer_outstanding = TRUE;
2434 }
2435
2436 /**
2437  * Returns a buffer with new data read into it.
2438  *
2439  * @param auth the auth conversation
2440  * @param buffer the buffer being returned
2441  * @param bytes_read number of new bytes added
2442  */
2443 void
2444 _dbus_auth_return_buffer (DBusAuth               *auth,
2445                           DBusString             *buffer,
2446                           int                     bytes_read)
2447 {
2448   _dbus_assert (buffer == &auth->incoming);
2449   _dbus_assert (auth->buffer_outstanding);
2450
2451   auth->buffer_outstanding = FALSE;
2452 }
2453
2454 /**
2455  * Returns leftover bytes that were not used as part of the auth
2456  * conversation.  These bytes will be part of the message stream
2457  * instead. This function may not be called until authentication has
2458  * succeeded.
2459  *
2460  * @param auth the auth conversation
2461  * @param str return location for pointer to string of unused bytes
2462  */
2463 void
2464 _dbus_auth_get_unused_bytes (DBusAuth           *auth,
2465                              const DBusString **str)
2466 {
2467   if (!DBUS_AUTH_IN_END_STATE (auth))
2468     return;
2469
2470   *str = &auth->incoming;
2471 }
2472
2473
2474 /**
2475  * Gets rid of unused bytes returned by _dbus_auth_get_unused_bytes()
2476  * after we've gotten them and successfully moved them elsewhere.
2477  *
2478  * @param auth the auth conversation
2479  */
2480 void
2481 _dbus_auth_delete_unused_bytes (DBusAuth *auth)
2482 {
2483   if (!DBUS_AUTH_IN_END_STATE (auth))
2484     return;
2485
2486   _dbus_string_set_length (&auth->incoming, 0);
2487 }
2488
2489 /**
2490  * Called post-authentication, indicates whether we need to encode
2491  * the message stream with _dbus_auth_encode_data() prior to
2492  * sending it to the peer.
2493  *
2494  * @param auth the auth conversation
2495  * @returns #TRUE if we need to encode the stream
2496  */
2497 dbus_bool_t
2498 _dbus_auth_needs_encoding (DBusAuth *auth)
2499 {
2500   if (auth->state != &common_state_authenticated)
2501     return FALSE;
2502   
2503   if (auth->mech != NULL)
2504     {
2505       if (DBUS_AUTH_IS_CLIENT (auth))
2506         return auth->mech->client_encode_func != NULL;
2507       else
2508         return auth->mech->server_encode_func != NULL;
2509     }
2510   else
2511     return FALSE;
2512 }
2513
2514 /**
2515  * Called post-authentication, encodes a block of bytes for sending to
2516  * the peer. If no encoding was negotiated, just copies the bytes
2517  * (you can avoid this by checking _dbus_auth_needs_encoding()).
2518  *
2519  * @param auth the auth conversation
2520  * @param plaintext the plain text data
2521  * @param encoded initialized string to where encoded data is appended
2522  * @returns #TRUE if we had enough memory and successfully encoded
2523  */
2524 dbus_bool_t
2525 _dbus_auth_encode_data (DBusAuth         *auth,
2526                         const DBusString *plaintext,
2527                         DBusString       *encoded)
2528 {
2529   _dbus_assert (plaintext != encoded);
2530   
2531   if (auth->state != &common_state_authenticated)
2532     return FALSE;
2533   
2534   if (_dbus_auth_needs_encoding (auth))
2535     {
2536       if (DBUS_AUTH_IS_CLIENT (auth))
2537         return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
2538       else
2539         return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
2540     }
2541   else
2542     {
2543       return _dbus_string_copy (plaintext, 0, encoded,
2544                                 _dbus_string_get_length (encoded));
2545     }
2546 }
2547
2548 /**
2549  * Called post-authentication, indicates whether we need to decode
2550  * the message stream with _dbus_auth_decode_data() after
2551  * receiving it from the peer.
2552  *
2553  * @param auth the auth conversation
2554  * @returns #TRUE if we need to encode the stream
2555  */
2556 dbus_bool_t
2557 _dbus_auth_needs_decoding (DBusAuth *auth)
2558 {
2559   if (auth->state != &common_state_authenticated)
2560     return FALSE;
2561     
2562   if (auth->mech != NULL)
2563     {
2564       if (DBUS_AUTH_IS_CLIENT (auth))
2565         return auth->mech->client_decode_func != NULL;
2566       else
2567         return auth->mech->server_decode_func != NULL;
2568     }
2569   else
2570     return FALSE;
2571 }
2572
2573
2574 /**
2575  * Called post-authentication, decodes a block of bytes received from
2576  * the peer. If no encoding was negotiated, just copies the bytes (you
2577  * can avoid this by checking _dbus_auth_needs_decoding()).
2578  *
2579  * @todo 1.0? We need to be able to distinguish "out of memory" error
2580  * from "the data is hosed" error.
2581  *
2582  * @param auth the auth conversation
2583  * @param encoded the encoded data
2584  * @param plaintext initialized string where decoded data is appended
2585  * @returns #TRUE if we had enough memory and successfully decoded
2586  */
2587 dbus_bool_t
2588 _dbus_auth_decode_data (DBusAuth         *auth,
2589                         const DBusString *encoded,
2590                         DBusString       *plaintext)
2591 {
2592   _dbus_assert (plaintext != encoded);
2593   
2594   if (auth->state != &common_state_authenticated)
2595     return FALSE;
2596   
2597   if (_dbus_auth_needs_decoding (auth))
2598     {
2599       if (DBUS_AUTH_IS_CLIENT (auth))
2600         return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
2601       else
2602         return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
2603     }
2604   else
2605     {
2606       return _dbus_string_copy (encoded, 0, plaintext,
2607                                 _dbus_string_get_length (plaintext));
2608     }
2609 }
2610
2611 /**
2612  * Sets credentials received via reliable means from the operating
2613  * system.
2614  *
2615  * @param auth the auth conversation
2616  * @param credentials the credentials received
2617  * @returns #FALSE on OOM
2618  */
2619 dbus_bool_t
2620 _dbus_auth_set_credentials (DBusAuth               *auth,
2621                             DBusCredentials        *credentials)
2622 {
2623   _dbus_credentials_clear (auth->credentials);
2624   return _dbus_credentials_add_credentials (auth->credentials,
2625                                             credentials);
2626 }
2627
2628 /**
2629  * Gets the identity we authorized the client as.  Apps may have
2630  * different policies as to what identities they allow.
2631  *
2632  * Returned credentials are not a copy and should not be modified
2633  *
2634  * @param auth the auth conversation
2635  * @returns the credentials we've authorized BY REFERENCE do not modify
2636  */
2637 DBusCredentials*
2638 _dbus_auth_get_identity (DBusAuth               *auth)
2639 {
2640   if (auth->state == &common_state_authenticated)
2641     {
2642       return auth->authorized_identity;
2643     }
2644   else
2645     {
2646       /* FIXME instead of this, keep an empty credential around that
2647        * doesn't require allocation or something
2648        */
2649       /* return empty credentials */
2650       _dbus_assert (_dbus_credentials_are_empty (auth->authorized_identity));
2651       return auth->authorized_identity;
2652     }
2653 }
2654
2655 /**
2656  * Gets the GUID from the server if we've authenticated; gets
2657  * #NULL otherwise.
2658  * @param auth the auth object
2659  * @returns the GUID in ASCII hex format
2660  */
2661 const char*
2662 _dbus_auth_get_guid_from_server (DBusAuth *auth)
2663 {
2664   _dbus_assert (DBUS_AUTH_IS_CLIENT (auth));
2665   
2666   if (auth->state == &common_state_authenticated)
2667     return _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
2668   else
2669     return NULL;
2670 }
2671
2672 /**
2673  * Sets the "authentication context" which scopes cookies
2674  * with the DBUS_COOKIE_SHA1 auth mechanism for example.
2675  *
2676  * @param auth the auth conversation
2677  * @param context the context
2678  * @returns #FALSE if no memory
2679  */
2680 dbus_bool_t
2681 _dbus_auth_set_context (DBusAuth               *auth,
2682                         const DBusString       *context)
2683 {
2684   return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context),
2685                                    &auth->context, 0, _dbus_string_get_length (context));
2686 }
2687
2688 /** @} */
2689
2690 /* tests in dbus-auth-util.c */