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