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