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