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