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