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