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