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