Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

dbus-auth.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* dbus-auth.c Authentication
00003  *
00004  * Copyright (C) 2002, 2003, 2004 Red Hat Inc.
00005  *
00006  * Licensed under the Academic Free License version 2.1
00007  * 
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  * 
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 #include "dbus-auth.h"
00024 #include "dbus-string.h"
00025 #include "dbus-list.h"
00026 #include "dbus-internals.h"
00027 #include "dbus-keyring.h"
00028 #include "dbus-sha.h"
00029 #include "dbus-userdb.h"
00030 #include "dbus-protocol.h"
00031 
00072 typedef dbus_bool_t (* DBusInitialResponseFunction)  (DBusAuth         *auth,
00073                                                       DBusString       *response);
00074 
00079 typedef dbus_bool_t (* DBusAuthDataFunction)     (DBusAuth         *auth,
00080                                                   const DBusString *data);
00081 
00085 typedef dbus_bool_t (* DBusAuthEncodeFunction)   (DBusAuth         *auth,
00086                                                   const DBusString *data,
00087                                                   DBusString       *encoded);
00088 
00092 typedef dbus_bool_t (* DBusAuthDecodeFunction)   (DBusAuth         *auth,
00093                                                   const DBusString *data,
00094                                                   DBusString       *decoded);
00095 
00099 typedef void        (* DBusAuthShutdownFunction) (DBusAuth       *auth);
00100 
00104 typedef struct
00105 {
00106   const char *mechanism; 
00107   DBusAuthDataFunction server_data_func; 
00108   DBusAuthEncodeFunction server_encode_func; 
00109   DBusAuthDecodeFunction server_decode_func; 
00110   DBusAuthShutdownFunction server_shutdown_func; 
00111   DBusInitialResponseFunction client_initial_response_func; 
00112   DBusAuthDataFunction client_data_func; 
00113   DBusAuthEncodeFunction client_encode_func; 
00114   DBusAuthDecodeFunction client_decode_func; 
00115   DBusAuthShutdownFunction client_shutdown_func; 
00116 } DBusAuthMechanismHandler;
00117 
00121 typedef enum {
00122   DBUS_AUTH_COMMAND_AUTH,
00123   DBUS_AUTH_COMMAND_CANCEL,
00124   DBUS_AUTH_COMMAND_DATA,
00125   DBUS_AUTH_COMMAND_BEGIN,
00126   DBUS_AUTH_COMMAND_REJECTED,
00127   DBUS_AUTH_COMMAND_OK,
00128   DBUS_AUTH_COMMAND_ERROR,
00129   DBUS_AUTH_COMMAND_UNKNOWN
00130 } DBusAuthCommand;
00131 
00137 typedef dbus_bool_t (* DBusAuthStateFunction) (DBusAuth         *auth,
00138                                                DBusAuthCommand   command,
00139                                                const DBusString *args);
00140 
00144 typedef struct
00145 {
00146   const char *name;               
00147   DBusAuthStateFunction handler;  
00148 } DBusAuthStateData;
00149 
00153 struct DBusAuth
00154 {
00155   int refcount;           
00156   const char *side;       
00158   DBusString incoming;    
00159   DBusString outgoing;    
00161   const DBusAuthStateData *state;         
00163   const DBusAuthMechanismHandler *mech;   
00165   DBusString identity;                   
00169   DBusCredentials credentials;      
00173   DBusCredentials authorized_identity; 
00175   DBusCredentials desired_identity;    
00177   DBusString context;               
00178   DBusKeyring *keyring;             
00179   int cookie_id;                    
00180   DBusString challenge;             
00182   char **allowed_mechs;             
00186   unsigned int needed_memory : 1;   
00189   unsigned int already_got_mechanisms : 1;       
00190   unsigned int already_asked_for_initial_response : 1; 
00191   unsigned int buffer_outstanding : 1; 
00192 };
00193 
00197 typedef struct
00198 {
00199   DBusAuth base;    
00201   DBusList *mechs_to_try; 
00203 } DBusAuthClient;
00204 
00208 typedef struct
00209 {
00210   DBusAuth base;    
00212   int failures;     
00213   int max_failures; 
00215 } DBusAuthServer;
00216 
00217 static void        goto_state                (DBusAuth                       *auth,
00218                                               const DBusAuthStateData        *new_state);
00219 static dbus_bool_t send_auth                 (DBusAuth *auth,
00220                                               const DBusAuthMechanismHandler *mech);
00221 static dbus_bool_t send_data                 (DBusAuth *auth,
00222                                               DBusString *data);
00223 static dbus_bool_t send_rejected             (DBusAuth *auth);
00224 static dbus_bool_t send_error                (DBusAuth *auth,
00225                                               const char *message);
00226 static dbus_bool_t send_ok                   (DBusAuth *auth);
00227 static dbus_bool_t send_begin                (DBusAuth *auth);
00228 static dbus_bool_t send_cancel               (DBusAuth *auth);
00229 
00234 static dbus_bool_t handle_server_state_waiting_for_auth  (DBusAuth         *auth,
00235                                                           DBusAuthCommand   command,
00236                                                           const DBusString *args);
00237 static dbus_bool_t handle_server_state_waiting_for_data  (DBusAuth         *auth,
00238                                                           DBusAuthCommand   command,
00239                                                           const DBusString *args);
00240 static dbus_bool_t handle_server_state_waiting_for_begin (DBusAuth         *auth,
00241                                                           DBusAuthCommand   command,
00242                                                           const DBusString *args);
00243   
00244 static const DBusAuthStateData server_state_waiting_for_auth = {
00245   "WaitingForAuth", handle_server_state_waiting_for_auth
00246 };
00247 static const DBusAuthStateData server_state_waiting_for_data = {
00248   "WaitingForData", handle_server_state_waiting_for_data
00249 };
00250 static const DBusAuthStateData server_state_waiting_for_begin = {
00251   "WaitingForBegin", handle_server_state_waiting_for_begin
00252 };
00253   
00258 static dbus_bool_t handle_client_state_waiting_for_data   (DBusAuth         *auth,
00259                                                            DBusAuthCommand   command,
00260                                                            const DBusString *args);
00261 static dbus_bool_t handle_client_state_waiting_for_ok     (DBusAuth         *auth,
00262                                                            DBusAuthCommand   command,
00263                                                            const DBusString *args);
00264 static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth         *auth,
00265                                                            DBusAuthCommand   command,
00266                                                            const DBusString *args);
00267 
00268 static const DBusAuthStateData client_state_need_send_auth = {
00269   "NeedSendAuth", NULL
00270 };
00271 static const DBusAuthStateData client_state_waiting_for_data = {
00272   "WaitingForData", handle_client_state_waiting_for_data
00273 };
00274 static const DBusAuthStateData client_state_waiting_for_ok = {
00275   "WaitingForOK", handle_client_state_waiting_for_ok
00276 };
00277 static const DBusAuthStateData client_state_waiting_for_reject = {
00278   "WaitingForReject", handle_client_state_waiting_for_reject
00279 };
00280   
00285 static const DBusAuthStateData common_state_authenticated = {
00286   "Authenticated",  NULL
00287 };
00288 
00289 static const DBusAuthStateData common_state_need_disconnect = {
00290   "NeedDisconnect",  NULL
00291 };
00292 
00293 static const char auth_side_client[] = "client";
00294 static const char auth_side_server[] = "server";
00299 #define DBUS_AUTH_IS_SERVER(auth) ((auth)->side == auth_side_server)
00300 
00304 #define DBUS_AUTH_IS_CLIENT(auth) ((auth)->side == auth_side_client)
00305 
00309 #define DBUS_AUTH_CLIENT(auth)    ((DBusAuthClient*)(auth))
00310 
00314 #define DBUS_AUTH_SERVER(auth)    ((DBusAuthServer*)(auth))
00315 
00321 #define DBUS_AUTH_NAME(auth)      ((auth)->side)
00322 
00323 static DBusAuth*
00324 _dbus_auth_new (int size)
00325 {
00326   DBusAuth *auth;
00327   
00328   auth = dbus_malloc0 (size);
00329   if (auth == NULL)
00330     return NULL;
00331   
00332   auth->refcount = 1;
00333 
00334   _dbus_credentials_clear (&auth->credentials);
00335   _dbus_credentials_clear (&auth->authorized_identity);
00336   _dbus_credentials_clear (&auth->desired_identity);
00337   
00338   auth->keyring = NULL;
00339   auth->cookie_id = -1;
00340   
00341   /* note that we don't use the max string length feature,
00342    * because you can't use that feature if you're going to
00343    * try to recover from out-of-memory (it creates
00344    * what looks like unrecoverable inability to alloc
00345    * more space in the string). But we do handle
00346    * overlong buffers in _dbus_auth_do_work().
00347    */
00348   
00349   if (!_dbus_string_init (&auth->incoming))
00350     goto enomem_0;
00351 
00352   if (!_dbus_string_init (&auth->outgoing))
00353     goto enomem_1;
00354     
00355   if (!_dbus_string_init (&auth->identity))
00356     goto enomem_2;
00357 
00358   if (!_dbus_string_init (&auth->context))
00359     goto enomem_3;
00360 
00361   if (!_dbus_string_init (&auth->challenge))
00362     goto enomem_4;
00363 
00364   /* default context if none is specified */
00365   if (!_dbus_string_append (&auth->context, "org_freedesktop_general"))
00366     goto enomem_5;
00367   
00368   return auth;
00369 
00370  enomem_5:
00371   _dbus_string_free (&auth->challenge);
00372  enomem_4:
00373   _dbus_string_free (&auth->context);
00374  enomem_3:
00375   _dbus_string_free (&auth->identity);
00376  enomem_2:
00377   _dbus_string_free (&auth->outgoing);
00378  enomem_1:
00379   _dbus_string_free (&auth->incoming);
00380  enomem_0:
00381   dbus_free (auth);
00382   return NULL;
00383 }
00384 
00385 static void
00386 shutdown_mech (DBusAuth *auth)
00387 {
00388   /* Cancel any auth */
00389   auth->already_asked_for_initial_response = FALSE;
00390   _dbus_string_set_length (&auth->identity, 0);
00391 
00392   _dbus_credentials_clear (&auth->authorized_identity);
00393   _dbus_credentials_clear (&auth->desired_identity);
00394   
00395   if (auth->mech != NULL)
00396     {
00397       _dbus_verbose ("%s: Shutting down mechanism %s\n",
00398                      DBUS_AUTH_NAME (auth), auth->mech->mechanism);
00399       
00400       if (DBUS_AUTH_IS_CLIENT (auth))
00401         (* auth->mech->client_shutdown_func) (auth);
00402       else
00403         (* auth->mech->server_shutdown_func) (auth);
00404       
00405       auth->mech = NULL;
00406     }
00407 }
00408 
00409 /* Returns TRUE but with an empty string hash if the
00410  * cookie_id isn't known. As with all this code
00411  * TRUE just means we had enough memory.
00412  */
00413 static dbus_bool_t
00414 sha1_compute_hash (DBusAuth         *auth,
00415                    int               cookie_id,
00416                    const DBusString *server_challenge,
00417                    const DBusString *client_challenge,
00418                    DBusString       *hash)
00419 {
00420   DBusString cookie;
00421   DBusString to_hash;
00422   dbus_bool_t retval;
00423   
00424   _dbus_assert (auth->keyring != NULL);
00425 
00426   retval = FALSE;
00427   
00428   if (!_dbus_string_init (&cookie))
00429     return FALSE;
00430 
00431   if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id,
00432                                   &cookie))
00433     goto out_0;
00434 
00435   if (_dbus_string_get_length (&cookie) == 0)
00436     {
00437       retval = TRUE;
00438       goto out_0;
00439     }
00440 
00441   if (!_dbus_string_init (&to_hash))
00442     goto out_0;
00443   
00444   if (!_dbus_string_copy (server_challenge, 0,
00445                           &to_hash, _dbus_string_get_length (&to_hash)))
00446     goto out_1;
00447 
00448   if (!_dbus_string_append (&to_hash, ":"))
00449     goto out_1;
00450   
00451   if (!_dbus_string_copy (client_challenge, 0,
00452                           &to_hash, _dbus_string_get_length (&to_hash)))
00453     goto out_1;
00454 
00455   if (!_dbus_string_append (&to_hash, ":"))
00456     goto out_1;
00457 
00458   if (!_dbus_string_copy (&cookie, 0,
00459                           &to_hash, _dbus_string_get_length (&to_hash)))
00460     goto out_1;
00461 
00462   if (!_dbus_sha_compute (&to_hash, hash))
00463     goto out_1;
00464   
00465   retval = TRUE;
00466 
00467  out_1:
00468   _dbus_string_zero (&to_hash);
00469   _dbus_string_free (&to_hash);
00470  out_0:
00471   _dbus_string_zero (&cookie);
00472   _dbus_string_free (&cookie);
00473   return retval;
00474 }
00475 
00480 #define N_CHALLENGE_BYTES (128/8)
00481 
00482 static dbus_bool_t
00483 sha1_handle_first_client_response (DBusAuth         *auth,
00484                                    const DBusString *data)
00485 {
00486   /* We haven't sent a challenge yet, we're expecting a desired
00487    * username from the client.
00488    */
00489   DBusString tmp;
00490   DBusString tmp2;
00491   dbus_bool_t retval;
00492   DBusError error;
00493   
00494   retval = FALSE;
00495 
00496   _dbus_string_set_length (&auth->challenge, 0);
00497   
00498   if (_dbus_string_get_length (data) > 0)
00499     {
00500       if (_dbus_string_get_length (&auth->identity) > 0)
00501         {
00502           /* Tried to send two auth identities, wtf */
00503           _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
00504                          DBUS_AUTH_NAME (auth));
00505           return send_rejected (auth);
00506         }
00507       else
00508         {
00509           /* this is our auth identity */
00510           if (!_dbus_string_copy (data, 0, &auth->identity, 0))
00511             return FALSE;
00512         }
00513     }
00514       
00515   if (!_dbus_credentials_from_username (data, &auth->desired_identity))
00516     {
00517       _dbus_verbose ("%s: Did not get a valid username from client\n",
00518                      DBUS_AUTH_NAME (auth));
00519       return send_rejected (auth);
00520     }
00521       
00522   if (!_dbus_string_init (&tmp))
00523     return FALSE;
00524 
00525   if (!_dbus_string_init (&tmp2))
00526     {
00527       _dbus_string_free (&tmp);
00528       return FALSE;
00529     }
00530 
00531   /* we cache the keyring for speed, so here we drop it if it's the
00532    * wrong one. FIXME caching the keyring here is useless since we use
00533    * a different DBusAuth for every connection.
00534    */
00535   if (auth->keyring &&
00536       !_dbus_keyring_is_for_user (auth->keyring,
00537                                   data))
00538     {
00539       _dbus_keyring_unref (auth->keyring);
00540       auth->keyring = NULL;
00541     }
00542   
00543   if (auth->keyring == NULL)
00544     {
00545       DBusError error;
00546 
00547       dbus_error_init (&error);
00548       auth->keyring = _dbus_keyring_new_homedir (data,
00549                                                  &auth->context,
00550                                                  &error);
00551 
00552       if (auth->keyring == NULL)
00553         {
00554           if (dbus_error_has_name (&error,
00555                                    DBUS_ERROR_NO_MEMORY))
00556             {
00557               dbus_error_free (&error);
00558               goto out;
00559             }
00560           else
00561             {
00562               _DBUS_ASSERT_ERROR_IS_SET (&error);
00563               _dbus_verbose ("%s: Error loading keyring: %s\n",
00564                              DBUS_AUTH_NAME (auth), error.message);
00565               if (send_rejected (auth))
00566                 retval = TRUE; /* retval is only about mem */
00567               dbus_error_free (&error);
00568               goto out;
00569             }
00570         }
00571       else
00572         {
00573           _dbus_assert (!dbus_error_is_set (&error));
00574         }
00575     }
00576 
00577   _dbus_assert (auth->keyring != NULL);
00578 
00579   dbus_error_init (&error);
00580   auth->cookie_id = _dbus_keyring_get_best_key (auth->keyring, &error);
00581   if (auth->cookie_id < 0)
00582     {
00583       _DBUS_ASSERT_ERROR_IS_SET (&error);
00584       _dbus_verbose ("%s: Could not get a cookie ID to send to client: %s\n",
00585                      DBUS_AUTH_NAME (auth), error.message);
00586       if (send_rejected (auth))
00587         retval = TRUE;
00588       dbus_error_free (&error);
00589       goto out;
00590     }
00591   else
00592     {
00593       _dbus_assert (!dbus_error_is_set (&error));
00594     }
00595 
00596   if (!_dbus_string_copy (&auth->context, 0,
00597                           &tmp2, _dbus_string_get_length (&tmp2)))
00598     goto out;
00599 
00600   if (!_dbus_string_append (&tmp2, " "))
00601     goto out;
00602 
00603   if (!_dbus_string_append_int (&tmp2, auth->cookie_id))
00604     goto out;
00605 
00606   if (!_dbus_string_append (&tmp2, " "))
00607     goto out;  
00608   
00609   if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
00610     goto out;
00611 
00612   _dbus_string_set_length (&auth->challenge, 0);
00613   if (!_dbus_string_hex_encode (&tmp, 0, &auth->challenge, 0))
00614     goto out;
00615   
00616   if (!_dbus_string_hex_encode (&tmp, 0, &tmp2,
00617                                 _dbus_string_get_length (&tmp2)))
00618     goto out;
00619 
00620   if (!send_data (auth, &tmp2))
00621     goto out;
00622       
00623   goto_state (auth, &server_state_waiting_for_data);
00624   retval = TRUE;
00625   
00626  out:
00627   _dbus_string_zero (&tmp);
00628   _dbus_string_free (&tmp);
00629   _dbus_string_zero (&tmp2);
00630   _dbus_string_free (&tmp2);
00631 
00632   return retval;
00633 }
00634 
00635 static dbus_bool_t
00636 sha1_handle_second_client_response (DBusAuth         *auth,
00637                                     const DBusString *data)
00638 {
00639   /* We are expecting a response which is the hex-encoded client
00640    * challenge, space, then SHA-1 hash of the concatenation of our
00641    * challenge, ":", client challenge, ":", secret key, all
00642    * hex-encoded.
00643    */
00644   int i;
00645   DBusString client_challenge;
00646   DBusString client_hash;
00647   dbus_bool_t retval;
00648   DBusString correct_hash;
00649   
00650   retval = FALSE;
00651   
00652   if (!_dbus_string_find_blank (data, 0, &i))
00653     {
00654       _dbus_verbose ("%s: no space separator in client response\n",
00655                      DBUS_AUTH_NAME (auth));
00656       return send_rejected (auth);
00657     }
00658   
00659   if (!_dbus_string_init (&client_challenge))
00660     goto out_0;
00661 
00662   if (!_dbus_string_init (&client_hash))
00663     goto out_1;  
00664 
00665   if (!_dbus_string_copy_len (data, 0, i, &client_challenge,
00666                               0))
00667     goto out_2;
00668 
00669   _dbus_string_skip_blank (data, i, &i);
00670   
00671   if (!_dbus_string_copy_len (data, i,
00672                               _dbus_string_get_length (data) - i,
00673                               &client_hash,
00674                               0))
00675     goto out_2;
00676 
00677   if (_dbus_string_get_length (&client_challenge) == 0 ||
00678       _dbus_string_get_length (&client_hash) == 0)
00679     {
00680       _dbus_verbose ("%s: zero-length client challenge or hash\n",
00681                      DBUS_AUTH_NAME (auth));
00682       if (send_rejected (auth))
00683         retval = TRUE;
00684       goto out_2;
00685     }
00686 
00687   if (!_dbus_string_init (&correct_hash))
00688     goto out_2;
00689 
00690   if (!sha1_compute_hash (auth, auth->cookie_id,
00691                           &auth->challenge, 
00692                           &client_challenge,
00693                           &correct_hash))
00694     goto out_3;
00695 
00696   /* if cookie_id was invalid, then we get an empty hash */
00697   if (_dbus_string_get_length (&correct_hash) == 0)
00698     {
00699       if (send_rejected (auth))
00700         retval = TRUE;
00701       goto out_3;
00702     }
00703   
00704   if (!_dbus_string_equal (&client_hash, &correct_hash))
00705     {
00706       if (send_rejected (auth))
00707         retval = TRUE;
00708       goto out_3;
00709     }
00710       
00711   if (!send_ok (auth))
00712     goto out_3;
00713 
00714   _dbus_verbose ("%s: authenticated client with UID "DBUS_UID_FORMAT" using DBUS_COOKIE_SHA1\n",
00715                  DBUS_AUTH_NAME (auth), auth->desired_identity.uid);
00716   
00717   auth->authorized_identity = auth->desired_identity;
00718   retval = TRUE;
00719   
00720  out_3:
00721   _dbus_string_zero (&correct_hash);
00722   _dbus_string_free (&correct_hash);
00723  out_2:
00724   _dbus_string_zero (&client_hash);
00725   _dbus_string_free (&client_hash);
00726  out_1:
00727   _dbus_string_free (&client_challenge);
00728  out_0:
00729   return retval;
00730 }
00731 
00732 static dbus_bool_t
00733 handle_server_data_cookie_sha1_mech (DBusAuth         *auth,
00734                                      const DBusString *data)
00735 {
00736   if (auth->cookie_id < 0)
00737     return sha1_handle_first_client_response (auth, data);
00738   else
00739     return sha1_handle_second_client_response (auth, data);
00740 }
00741 
00742 static void
00743 handle_server_shutdown_cookie_sha1_mech (DBusAuth *auth)
00744 {
00745   auth->cookie_id = -1;  
00746   _dbus_string_set_length (&auth->challenge, 0);
00747 }
00748 
00749 static dbus_bool_t
00750 handle_client_initial_response_cookie_sha1_mech (DBusAuth   *auth,
00751                                                  DBusString *response)
00752 {
00753   const DBusString *username;
00754   dbus_bool_t retval;
00755 
00756   retval = FALSE;
00757 
00758   if (!_dbus_username_from_current_process (&username))
00759     goto out_0;
00760 
00761   if (!_dbus_string_hex_encode (username, 0,
00762                                 response,
00763                                 _dbus_string_get_length (response)))
00764     goto out_0;
00765 
00766   retval = TRUE;
00767   
00768  out_0:
00769   return retval;
00770 }
00771 
00772 static dbus_bool_t
00773 handle_client_data_cookie_sha1_mech (DBusAuth         *auth,
00774                                      const DBusString *data)
00775 {
00776   /* The data we get from the server should be the cookie context
00777    * name, the cookie ID, and the server challenge, separated by
00778    * spaces. We send back our challenge string and the correct hash.
00779    */
00780   dbus_bool_t retval;
00781   DBusString context;
00782   DBusString cookie_id_str;
00783   DBusString server_challenge;
00784   DBusString client_challenge;
00785   DBusString correct_hash;
00786   DBusString tmp;
00787   int i, j;
00788   long val;
00789   
00790   retval = FALSE;                 
00791   
00792   if (!_dbus_string_find_blank (data, 0, &i))
00793     {
00794       if (send_error (auth,
00795                       "Server did not send context/ID/challenge properly"))
00796         retval = TRUE;
00797       goto out_0;
00798     }
00799 
00800   if (!_dbus_string_init (&context))
00801     goto out_0;
00802 
00803   if (!_dbus_string_copy_len (data, 0, i,
00804                               &context, 0))
00805     goto out_1;
00806   
00807   _dbus_string_skip_blank (data, i, &i);
00808   if (!_dbus_string_find_blank (data, i, &j))
00809     {
00810       if (send_error (auth,
00811                       "Server did not send context/ID/challenge properly"))
00812         retval = TRUE;
00813       goto out_1;
00814     }
00815 
00816   if (!_dbus_string_init (&cookie_id_str))
00817     goto out_1;
00818   
00819   if (!_dbus_string_copy_len (data, i, j - i,
00820                               &cookie_id_str, 0))
00821     goto out_2;  
00822 
00823   if (!_dbus_string_init (&server_challenge))
00824     goto out_2;
00825 
00826   i = j;
00827   _dbus_string_skip_blank (data, i, &i);
00828   j = _dbus_string_get_length (data);
00829 
00830   if (!_dbus_string_copy_len (data, i, j - i,
00831                               &server_challenge, 0))
00832     goto out_3;
00833 
00834   if (!_dbus_keyring_validate_context (&context))
00835     {
00836       if (send_error (auth, "Server sent invalid cookie context"))
00837         retval = TRUE;
00838       goto out_3;
00839     }
00840 
00841   if (!_dbus_string_parse_int (&cookie_id_str, 0, &val, NULL))
00842     {
00843       if (send_error (auth, "Could not parse cookie ID as an integer"))
00844         retval = TRUE;
00845       goto out_3;
00846     }
00847 
00848   if (_dbus_string_get_length (&server_challenge) == 0)
00849     {
00850       if (send_error (auth, "Empty server challenge string"))
00851         retval = TRUE;
00852       goto out_3;
00853     }
00854 
00855   if (auth->keyring == NULL)
00856     {
00857       DBusError error;
00858 
00859       dbus_error_init (&error);
00860       auth->keyring = _dbus_keyring_new_homedir (NULL,
00861                                                  &context,
00862                                                  &error);
00863 
00864       if (auth->keyring == NULL)
00865         {
00866           if (dbus_error_has_name (&error,
00867                                    DBUS_ERROR_NO_MEMORY))
00868             {
00869               dbus_error_free (&error);
00870               goto out_3;
00871             }
00872           else
00873             {
00874               _DBUS_ASSERT_ERROR_IS_SET (&error);
00875 
00876               _dbus_verbose ("%s: Error loading keyring: %s\n",
00877                              DBUS_AUTH_NAME (auth), error.message);
00878               
00879               if (send_error (auth, "Could not load cookie file"))
00880                 retval = TRUE; /* retval is only about mem */
00881               
00882               dbus_error_free (&error);
00883               goto out_3;
00884             }
00885         }
00886       else
00887         {
00888           _dbus_assert (!dbus_error_is_set (&error));
00889         }
00890     }
00891   
00892   _dbus_assert (auth->keyring != NULL);
00893   
00894   if (!_dbus_string_init (&tmp))
00895     goto out_3;
00896   
00897   if (!_dbus_generate_random_bytes (&tmp, N_CHALLENGE_BYTES))
00898     goto out_4;
00899 
00900   if (!_dbus_string_init (&client_challenge))
00901     goto out_4;
00902 
00903   if (!_dbus_string_hex_encode (&tmp, 0, &client_challenge, 0))
00904     goto out_5;
00905 
00906   if (!_dbus_string_init (&correct_hash))
00907     goto out_5;
00908   
00909   if (!sha1_compute_hash (auth, val,
00910                           &server_challenge,
00911                           &client_challenge,
00912                           &correct_hash))
00913     goto out_6;
00914 
00915   if (_dbus_string_get_length (&correct_hash) == 0)
00916     {
00917       /* couldn't find the cookie ID or something */
00918       if (send_error (auth, "Don't have the requested cookie ID"))
00919         retval = TRUE;
00920       goto out_6;
00921     }
00922   
00923   _dbus_string_set_length (&tmp, 0);
00924   
00925   if (!_dbus_string_copy (&client_challenge, 0, &tmp,
00926                           _dbus_string_get_length (&tmp)))
00927     goto out_6;
00928 
00929   if (!_dbus_string_append (&tmp, " "))
00930     goto out_6;
00931 
00932   if (!_dbus_string_copy (&correct_hash, 0, &tmp,
00933                           _dbus_string_get_length (&tmp)))
00934     goto out_6;
00935 
00936   if (!send_data (auth, &tmp))
00937     goto out_6;
00938 
00939   retval = TRUE;
00940 
00941  out_6:
00942   _dbus_string_zero (&correct_hash);
00943   _dbus_string_free (&correct_hash);
00944  out_5:
00945   _dbus_string_free (&client_challenge);
00946  out_4:
00947   _dbus_string_zero (&tmp);
00948   _dbus_string_free (&tmp);
00949  out_3:
00950   _dbus_string_free (&server_challenge);
00951  out_2:
00952   _dbus_string_free (&cookie_id_str);
00953  out_1:
00954   _dbus_string_free (&context);
00955  out_0:
00956   return retval;
00957 }
00958 
00959 static void
00960 handle_client_shutdown_cookie_sha1_mech (DBusAuth *auth)
00961 {
00962   auth->cookie_id = -1;  
00963   _dbus_string_set_length (&auth->challenge, 0);
00964 }
00965 
00966 static dbus_bool_t
00967 handle_server_data_external_mech (DBusAuth         *auth,
00968                                   const DBusString *data)
00969 {
00970   if (auth->credentials.uid == DBUS_UID_UNSET)
00971     {
00972       _dbus_verbose ("%s: no credentials, mechanism EXTERNAL can't authenticate\n",
00973                      DBUS_AUTH_NAME (auth));
00974       return send_rejected (auth);
00975     }
00976   
00977   if (_dbus_string_get_length (data) > 0)
00978     {
00979       if (_dbus_string_get_length (&auth->identity) > 0)
00980         {
00981           /* Tried to send two auth identities, wtf */
00982           _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
00983                          DBUS_AUTH_NAME (auth));
00984           return send_rejected (auth);
00985         }
00986       else
00987         {
00988           /* this is our auth identity */
00989           if (!_dbus_string_copy (data, 0, &auth->identity, 0))
00990             return FALSE;
00991         }
00992     }
00993 
00994   /* Poke client for an auth identity, if none given */
00995   if (_dbus_string_get_length (&auth->identity) == 0 &&
00996       !auth->already_asked_for_initial_response)
00997     {
00998       if (send_data (auth, NULL))
00999         {
01000           _dbus_verbose ("%s: sending empty challenge asking client for auth identity\n",
01001                          DBUS_AUTH_NAME (auth));
01002           auth->already_asked_for_initial_response = TRUE;
01003           return TRUE;
01004         }
01005       else
01006         return FALSE;
01007     }
01008 
01009   _dbus_credentials_clear (&auth->desired_identity);
01010   
01011   /* If auth->identity is still empty here, then client
01012    * responded with an empty string after we poked it for
01013    * an initial response. This means to try to auth the
01014    * identity provided in the credentials.
01015    */
01016   if (_dbus_string_get_length (&auth->identity) == 0)
01017     {
01018       auth->desired_identity.uid = auth->credentials.uid;
01019     }
01020   else
01021     {
01022       if (!_dbus_uid_from_string (&auth->identity,
01023                                   &auth->desired_identity.uid))
01024         {
01025           _dbus_verbose ("%s: could not get credentials from uid string\n",
01026                          DBUS_AUTH_NAME (auth));
01027           return send_rejected (auth);
01028         }
01029     }
01030 
01031   if (auth->desired_identity.uid == DBUS_UID_UNSET)
01032     {
01033       _dbus_verbose ("%s: desired user %s is no good\n",
01034                      DBUS_AUTH_NAME (auth),
01035                      _dbus_string_get_const_data (&auth->identity));
01036       return send_rejected (auth);
01037     }
01038   
01039   if (_dbus_credentials_match (&auth->desired_identity,
01040                                &auth->credentials))
01041     {
01042       /* client has authenticated */      
01043       if (!send_ok (auth))
01044         return FALSE;
01045 
01046       _dbus_verbose ("%s: authenticated client with UID "DBUS_UID_FORMAT
01047                      " matching socket credentials UID "DBUS_UID_FORMAT"\n",
01048                      DBUS_AUTH_NAME (auth),
01049                      auth->desired_identity.uid,
01050                      auth->credentials.uid);
01051 
01052       auth->authorized_identity.pid = auth->credentials.pid;
01053       auth->authorized_identity.uid = auth->desired_identity.uid;
01054       return TRUE;
01055     }
01056   else
01057     {
01058       _dbus_verbose ("%s: credentials uid="DBUS_UID_FORMAT
01059                      " gid="DBUS_GID_FORMAT
01060                      " do not allow uid="DBUS_UID_FORMAT
01061                      " gid="DBUS_GID_FORMAT"\n",
01062                      DBUS_AUTH_NAME (auth),
01063                      auth->credentials.uid, auth->credentials.gid,
01064                      auth->desired_identity.uid, auth->desired_identity.gid);
01065       return send_rejected (auth);
01066     }
01067 }
01068 
01069 static void
01070 handle_server_shutdown_external_mech (DBusAuth *auth)
01071 {
01072 
01073 }
01074 
01075 static dbus_bool_t
01076 handle_client_initial_response_external_mech (DBusAuth         *auth,
01077                                               DBusString       *response)
01078 {
01079   /* We always append our UID as an initial response, so the server
01080    * doesn't have to send back an empty challenge to check whether we
01081    * want to specify an identity. i.e. this avoids a round trip that
01082    * the spec for the EXTERNAL mechanism otherwise requires.
01083    */
01084   DBusString plaintext;
01085 
01086   if (!_dbus_string_init (&plaintext))
01087     return FALSE;
01088   
01089   if (!_dbus_string_append_uint (&plaintext,
01090                                  _dbus_getuid ()))
01091     goto failed;
01092 
01093   if (!_dbus_string_hex_encode (&plaintext, 0,
01094                                 response,
01095                                 _dbus_string_get_length (response)))
01096     goto failed;
01097 
01098   _dbus_string_free (&plaintext);
01099   
01100   return TRUE;
01101 
01102  failed:
01103   _dbus_string_free (&plaintext);
01104   return FALSE;  
01105 }
01106 
01107 static dbus_bool_t
01108 handle_client_data_external_mech (DBusAuth         *auth,
01109                                   const DBusString *data)
01110 {
01111   
01112   return TRUE;
01113 }
01114 
01115 static void
01116 handle_client_shutdown_external_mech (DBusAuth *auth)
01117 {
01118 
01119 }
01120 
01121 /* Put mechanisms here in order of preference.
01122  * What I eventually want to have is:
01123  *
01124  *  - a mechanism that checks UNIX domain socket credentials
01125  *  - a simple magic cookie mechanism like X11 or ICE
01126  *  - mechanisms that chain to Cyrus SASL, so we can use anything it
01127  *    offers such as Kerberos, X509, whatever.
01128  * 
01129  */
01130 static const DBusAuthMechanismHandler
01131 all_mechanisms[] = {
01132   { "EXTERNAL",
01133     handle_server_data_external_mech,
01134     NULL, NULL,
01135     handle_server_shutdown_external_mech,
01136     handle_client_initial_response_external_mech,
01137     handle_client_data_external_mech,
01138     NULL, NULL,
01139     handle_client_shutdown_external_mech },
01140   { "DBUS_COOKIE_SHA1",
01141     handle_server_data_cookie_sha1_mech,
01142     NULL, NULL,
01143     handle_server_shutdown_cookie_sha1_mech,
01144     handle_client_initial_response_cookie_sha1_mech,
01145     handle_client_data_cookie_sha1_mech,
01146     NULL, NULL,
01147     handle_client_shutdown_cookie_sha1_mech },
01148   { NULL, NULL }
01149 };
01150 
01151 static const DBusAuthMechanismHandler*
01152 find_mech (const DBusString  *name,
01153            char             **allowed_mechs)
01154 {
01155   int i;
01156   
01157   if (allowed_mechs != NULL &&
01158       !_dbus_string_array_contains ((const char**) allowed_mechs,
01159                                     _dbus_string_get_const_data (name)))
01160     return NULL;
01161   
01162   i = 0;
01163   while (all_mechanisms[i].mechanism != NULL)
01164     {      
01165       if (_dbus_string_equal_c_str (name,
01166                                     all_mechanisms[i].mechanism))
01167 
01168         return &all_mechanisms[i];
01169       
01170       ++i;
01171     }
01172   
01173   return NULL;
01174 }
01175 
01176 static dbus_bool_t
01177 send_auth (DBusAuth *auth, const DBusAuthMechanismHandler *mech)
01178 {
01179   DBusString auth_command;
01180 
01181   if (!_dbus_string_init (&auth_command))
01182     return FALSE;
01183       
01184   if (!_dbus_string_append (&auth_command,
01185                             "AUTH "))
01186     {
01187       _dbus_string_free (&auth_command);
01188       return FALSE;
01189     }  
01190   
01191   if (!_dbus_string_append (&auth_command,
01192                             mech->mechanism))
01193     {
01194       _dbus_string_free (&auth_command);
01195       return FALSE;
01196     }
01197 
01198   if (mech->client_initial_response_func != NULL)
01199     {
01200       if (!_dbus_string_append (&auth_command, " "))
01201         {
01202           _dbus_string_free (&auth_command);
01203           return FALSE;
01204         }
01205       
01206       if (!(* mech->client_initial_response_func) (auth, &auth_command))
01207         {
01208           _dbus_string_free (&auth_command);
01209           return FALSE;
01210         }
01211     }
01212   
01213   if (!_dbus_string_append (&auth_command,
01214                             "\r\n"))
01215     {
01216       _dbus_string_free (&auth_command);
01217       return FALSE;
01218     }
01219 
01220   if (!_dbus_string_copy (&auth_command, 0,
01221                           &auth->outgoing,
01222                           _dbus_string_get_length (&auth->outgoing)))
01223     {
01224       _dbus_string_free (&auth_command);
01225       return FALSE;
01226     }
01227 
01228   _dbus_string_free (&auth_command);
01229   shutdown_mech (auth);
01230   auth->mech = mech;      
01231   goto_state (auth, &client_state_waiting_for_data);
01232 
01233   return TRUE;
01234 }
01235 
01236 static dbus_bool_t
01237 send_data (DBusAuth *auth, DBusString *data)
01238 {
01239   int old_len;
01240 
01241   if (data == NULL || _dbus_string_get_length (data) == 0)
01242     return _dbus_string_append (&auth->outgoing, "DATA\r\n");
01243   else
01244     {
01245       old_len = _dbus_string_get_length (&auth->outgoing);
01246       if (!_dbus_string_append (&auth->outgoing, "DATA "))
01247         goto out;
01248 
01249       if (!_dbus_string_hex_encode (data, 0, &auth->outgoing,
01250                                     _dbus_string_get_length (&auth->outgoing)))
01251         goto out;
01252 
01253       if (!_dbus_string_append (&auth->outgoing, "\r\n"))
01254         goto out;
01255 
01256       return TRUE;
01257 
01258     out:
01259       _dbus_string_set_length (&auth->outgoing, old_len);
01260 
01261       return FALSE;
01262     }
01263 }
01264 
01265 static dbus_bool_t
01266 send_rejected (DBusAuth *auth)
01267 {
01268   DBusString command;
01269   DBusAuthServer *server_auth;
01270   int i;
01271   
01272   if (!_dbus_string_init (&command))
01273     return FALSE;
01274   
01275   if (!_dbus_string_append (&command,
01276                             "REJECTED"))
01277     goto nomem;
01278 
01279   i = 0;
01280   while (all_mechanisms[i].mechanism != NULL)
01281     {
01282       if (!_dbus_string_append (&command,
01283                                 " "))
01284         goto nomem;
01285 
01286       if (!_dbus_string_append (&command,
01287                                 all_mechanisms[i].mechanism))
01288         goto nomem;
01289       
01290       ++i;
01291     }
01292   
01293   if (!_dbus_string_append (&command, "\r\n"))
01294     goto nomem;
01295 
01296   if (!_dbus_string_copy (&command, 0, &auth->outgoing,
01297                           _dbus_string_get_length (&auth->outgoing)))
01298     goto nomem;
01299 
01300   shutdown_mech (auth);
01301   
01302   _dbus_assert (DBUS_AUTH_IS_SERVER (auth));
01303   server_auth = DBUS_AUTH_SERVER (auth);
01304   server_auth->failures += 1;
01305 
01306   if (server_auth->failures >= server_auth->max_failures)
01307     goto_state (auth, &common_state_need_disconnect);
01308   else
01309     goto_state (auth, &server_state_waiting_for_auth);
01310 
01311   _dbus_string_free (&command);
01312   
01313   return TRUE;
01314 
01315  nomem:
01316   _dbus_string_free (&command);
01317   return FALSE;
01318 }
01319 
01320 static dbus_bool_t
01321 send_error (DBusAuth *auth, const char *message)
01322 {
01323   return _dbus_string_append_printf (&auth->outgoing,
01324                                      "ERROR \"%s\"\r\n", message);
01325 }
01326 
01327 static dbus_bool_t
01328 send_ok (DBusAuth *auth)
01329 {
01330   if (_dbus_string_append (&auth->outgoing, "OK\r\n"))
01331     {
01332       goto_state (auth, &server_state_waiting_for_begin);
01333       return TRUE;
01334     }
01335   else
01336     return FALSE;
01337 }
01338 
01339 static dbus_bool_t
01340 send_begin (DBusAuth *auth)
01341 {
01342   if (_dbus_string_append (&auth->outgoing, "BEGIN\r\n"))
01343     {
01344       goto_state (auth, &common_state_authenticated);
01345       return TRUE;
01346     }
01347   else
01348     return FALSE;
01349 }
01350 
01351 static dbus_bool_t
01352 send_cancel (DBusAuth *auth)
01353 {
01354   if (_dbus_string_append (&auth->outgoing, "CANCEL\r\n"))
01355     {
01356       goto_state (auth, &client_state_waiting_for_reject);
01357       return TRUE;
01358     }
01359   else
01360     return FALSE;
01361 }
01362 
01363 static dbus_bool_t
01364 process_data (DBusAuth             *auth,
01365              const DBusString     *args,
01366              DBusAuthDataFunction  data_func)
01367 {
01368   int end;
01369   DBusString decoded;
01370 
01371   if (!_dbus_string_init (&decoded))
01372     return FALSE;
01373 
01374   if (!_dbus_string_hex_decode (args, 0, &end, &decoded, 0))
01375     {
01376       _dbus_string_free (&decoded);
01377       return FALSE;
01378     }
01379 
01380   if (_dbus_string_get_length (args) != end)
01381     {
01382       _dbus_string_free (&decoded);
01383       if (!send_error (auth, "Invalid hex encoding"))
01384         return FALSE;
01385 
01386       return TRUE;
01387     }
01388 
01389 #ifdef DBUS_ENABLE_VERBOSE_MODE
01390   if (_dbus_string_validate_ascii (&decoded, 0,
01391                                    _dbus_string_get_length (&decoded)))
01392     _dbus_verbose ("%s: data: '%s'\n",
01393                    DBUS_AUTH_NAME (auth),
01394                    _dbus_string_get_const_data (&decoded));
01395 #endif
01396       
01397   if (!(* data_func) (auth, &decoded))
01398     {
01399       _dbus_string_free (&decoded);
01400       return FALSE;
01401     }
01402 
01403   _dbus_string_free (&decoded);
01404   return TRUE;
01405 }
01406 
01407 static dbus_bool_t
01408 handle_auth (DBusAuth *auth, const DBusString *args)
01409 {
01410   if (_dbus_string_get_length (args) == 0)
01411     {
01412       /* No args to the auth, send mechanisms */
01413       if (!send_rejected (auth))
01414         return FALSE;
01415 
01416       return TRUE;
01417     }
01418   else
01419     {
01420       int i;
01421       DBusString mech;
01422       DBusString hex_response;
01423       
01424       _dbus_string_find_blank (args, 0, &i);
01425 
01426       if (!_dbus_string_init (&mech))
01427         return FALSE;
01428 
01429       if (!_dbus_string_init (&hex_response))
01430         {
01431           _dbus_string_free (&mech);
01432           return FALSE;
01433         }
01434       
01435       if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
01436         goto failed;
01437 
01438       _dbus_string_skip_blank (args, i, &i);
01439       if (!_dbus_string_copy (args, i, &hex_response, 0))
01440         goto failed;
01441      
01442       auth->mech = find_mech (&mech, auth->allowed_mechs);
01443       if (auth->mech != NULL)
01444         {
01445           _dbus_verbose ("%s: Trying mechanism %s\n",
01446                          DBUS_AUTH_NAME (auth),
01447                          auth->mech->mechanism);
01448           
01449           if (!process_data (auth, &hex_response,
01450                              auth->mech->server_data_func))
01451             goto failed;
01452         }
01453       else
01454         {
01455           /* Unsupported mechanism */
01456           _dbus_verbose ("%s: Unsupported mechanism %s\n",
01457                          DBUS_AUTH_NAME (auth),
01458                          _dbus_string_get_const_data (&mech));
01459           
01460           if (!send_rejected (auth))
01461             goto failed;
01462         }
01463 
01464       _dbus_string_free (&mech);      
01465       _dbus_string_free (&hex_response);
01466 
01467       return TRUE;
01468       
01469     failed:
01470       auth->mech = NULL;
01471       _dbus_string_free (&mech);
01472       _dbus_string_free (&hex_response);
01473       return FALSE;
01474     }
01475 }
01476 
01477 static dbus_bool_t
01478 handle_server_state_waiting_for_auth  (DBusAuth         *auth,
01479                                        DBusAuthCommand   command,
01480                                        const DBusString *args)
01481 {
01482   switch (command)
01483     {
01484     case DBUS_AUTH_COMMAND_AUTH:
01485       return handle_auth (auth, args);
01486 
01487     case DBUS_AUTH_COMMAND_CANCEL:
01488     case DBUS_AUTH_COMMAND_DATA:
01489       return send_error (auth, "Not currently in an auth conversation");
01490 
01491     case DBUS_AUTH_COMMAND_BEGIN:
01492       goto_state (auth, &common_state_need_disconnect);
01493       return TRUE;
01494 
01495     case DBUS_AUTH_COMMAND_ERROR:
01496       return send_rejected (auth);
01497 
01498     case DBUS_AUTH_COMMAND_REJECTED:
01499     case DBUS_AUTH_COMMAND_OK:
01500     case DBUS_AUTH_COMMAND_UNKNOWN:
01501     default:
01502       return send_error (auth, "Unknown command");
01503     }
01504 }
01505 
01506 static dbus_bool_t
01507 handle_server_state_waiting_for_data  (DBusAuth         *auth,
01508                                        DBusAuthCommand   command,
01509                                        const DBusString *args)
01510 {
01511   switch (command)
01512     {
01513     case DBUS_AUTH_COMMAND_AUTH:
01514       return send_error (auth, "Sent AUTH while another AUTH in progress");
01515 
01516     case DBUS_AUTH_COMMAND_CANCEL:
01517     case DBUS_AUTH_COMMAND_ERROR:
01518       return send_rejected (auth);
01519 
01520     case DBUS_AUTH_COMMAND_DATA:
01521       return process_data (auth, args, auth->mech->server_data_func);
01522 
01523     case DBUS_AUTH_COMMAND_BEGIN:
01524       goto_state (auth, &common_state_need_disconnect);
01525       return TRUE;
01526 
01527     case DBUS_AUTH_COMMAND_REJECTED:
01528     case DBUS_AUTH_COMMAND_OK:
01529     case DBUS_AUTH_COMMAND_UNKNOWN:
01530     default:
01531       return send_error (auth, "Unknown command");
01532     }
01533 }
01534 
01535 static dbus_bool_t
01536 handle_server_state_waiting_for_begin (DBusAuth         *auth,
01537                                        DBusAuthCommand   command,
01538                                        const DBusString *args)
01539 {
01540   switch (command)
01541     {
01542     case DBUS_AUTH_COMMAND_AUTH:
01543       return send_error (auth, "Sent AUTH while expecting BEGIN");
01544 
01545     case DBUS_AUTH_COMMAND_DATA:
01546       return send_error (auth, "Sent DATA while expecting BEGIN");
01547 
01548     case DBUS_AUTH_COMMAND_BEGIN:
01549       goto_state (auth, &common_state_authenticated);
01550       return TRUE;
01551 
01552     case DBUS_AUTH_COMMAND_REJECTED:
01553     case DBUS_AUTH_COMMAND_OK:
01554     case DBUS_AUTH_COMMAND_UNKNOWN:
01555     default:
01556       return send_error (auth, "Unknown command");
01557 
01558     case DBUS_AUTH_COMMAND_CANCEL:
01559     case DBUS_AUTH_COMMAND_ERROR:
01560       return send_rejected (auth);
01561     }
01562 }
01563 
01564 /* return FALSE if no memory, TRUE if all OK */
01565 static dbus_bool_t
01566 get_word (const DBusString *str,
01567           int              *start,
01568           DBusString       *word)
01569 {
01570   int i;
01571 
01572   _dbus_string_skip_blank (str, *start, start);
01573   _dbus_string_find_blank (str, *start, &i);
01574   
01575   if (i > *start)
01576     {
01577       if (!_dbus_string_copy_len (str, *start, i - *start, word, 0))
01578         return FALSE;
01579       
01580       *start = i;
01581     }
01582 
01583   return TRUE;
01584 }
01585 
01586 static dbus_bool_t
01587 record_mechanisms (DBusAuth         *auth,
01588                    const DBusString *args)
01589 {
01590   int next;
01591   int len;
01592 
01593   if (auth->already_got_mechanisms)
01594     return TRUE;
01595   
01596   len = _dbus_string_get_length (args);
01597   
01598   next = 0;
01599   while (next < len)
01600     {
01601       DBusString m;
01602       const DBusAuthMechanismHandler *mech;
01603       
01604       if (!_dbus_string_init (&m))
01605         goto nomem;
01606       
01607       if (!get_word (args, &next, &m))
01608         {
01609           _dbus_string_free (&m);
01610           goto nomem;
01611         }
01612 
01613       mech = find_mech (&m, auth->allowed_mechs);
01614 
01615       if (mech != NULL)
01616         {
01617           /* FIXME right now we try mechanisms in the order
01618            * the server lists them; should we do them in
01619            * some more deterministic order?
01620            *
01621            * Probably in all_mechanisms order, our order of
01622            * preference. Of course when the server is us,
01623            * it lists things in that order anyhow.
01624            */
01625 
01626           _dbus_verbose ("%s: Adding mechanism %s to list we will try\n",
01627                          DBUS_AUTH_NAME (auth), mech->mechanism);
01628           
01629           if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
01630                                   (void*) mech))
01631             {
01632               _dbus_string_free (&m);
01633               goto nomem;
01634             }
01635         }
01636       else
01637         {
01638           _dbus_verbose ("%s: Server offered mechanism \"%s\" that we don't know how to use\n",
01639                          DBUS_AUTH_NAME (auth),
01640                          _dbus_string_get_const_data (&m));
01641         }
01642 
01643       _dbus_string_free (&m);
01644     }
01645   
01646   auth->already_got_mechanisms = TRUE;
01647   
01648   return TRUE;
01649 
01650  nomem:
01651   _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
01652   
01653   return FALSE;
01654 }
01655 
01656 static dbus_bool_t
01657 process_rejected (DBusAuth *auth, const DBusString *args)
01658 {
01659   const DBusAuthMechanismHandler *mech;
01660   DBusAuthClient *client;
01661 
01662   client = DBUS_AUTH_CLIENT (auth);
01663 
01664   if (!auth->already_got_mechanisms)
01665     {
01666       if (!record_mechanisms (auth, args))
01667         return FALSE;
01668     }
01669   
01670   if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
01671     {
01672       mech = client->mechs_to_try->data;
01673 
01674       if (!send_auth (auth, mech))
01675         return FALSE;
01676 
01677       _dbus_list_pop_first (&client->mechs_to_try);
01678 
01679       _dbus_verbose ("%s: Trying mechanism %s\n",
01680                      DBUS_AUTH_NAME (auth),
01681                      mech->mechanism);
01682     }
01683   else
01684     {
01685       /* Give up */
01686       _dbus_verbose ("%s: Disconnecting because we are out of mechanisms to try using\n",
01687                      DBUS_AUTH_NAME (auth));
01688       goto_state (auth, &common_state_need_disconnect);
01689     }
01690   
01691   return TRUE;
01692 }
01693 
01694 
01695 static dbus_bool_t
01696 handle_client_state_waiting_for_data (DBusAuth         *auth,
01697                                       DBusAuthCommand   command,
01698                                       const DBusString *args)
01699 {
01700   _dbus_assert (auth->mech != NULL);
01701  
01702   switch (command)
01703     {
01704     case DBUS_AUTH_COMMAND_DATA:
01705       return process_data (auth, args, auth->mech->client_data_func);
01706 
01707     case DBUS_AUTH_COMMAND_REJECTED:
01708       return process_rejected (auth, args);
01709 
01710     case DBUS_AUTH_COMMAND_OK:
01711       return send_begin (auth);
01712 
01713     case DBUS_AUTH_COMMAND_ERROR:
01714       return send_cancel (auth);
01715 
01716     case DBUS_AUTH_COMMAND_AUTH:
01717     case DBUS_AUTH_COMMAND_CANCEL:
01718     case DBUS_AUTH_COMMAND_BEGIN:
01719     case DBUS_AUTH_COMMAND_UNKNOWN:
01720     default:
01721       return send_error (auth, "Unknown command");
01722     }
01723 }
01724 
01725 static dbus_bool_t
01726 handle_client_state_waiting_for_ok (DBusAuth         *auth,
01727                                     DBusAuthCommand   command,
01728                                     const DBusString *args)
01729 {
01730   switch (command)
01731     {
01732     case DBUS_AUTH_COMMAND_REJECTED:
01733       return process_rejected (auth, args);
01734 
01735     case DBUS_AUTH_COMMAND_OK:
01736       return send_begin (auth);
01737 
01738     case DBUS_AUTH_COMMAND_DATA:
01739     case DBUS_AUTH_COMMAND_ERROR:
01740       return send_cancel (auth);
01741 
01742     case DBUS_AUTH_COMMAND_AUTH:
01743     case DBUS_AUTH_COMMAND_CANCEL:
01744     case DBUS_AUTH_COMMAND_BEGIN:
01745     case DBUS_AUTH_COMMAND_UNKNOWN:
01746     default:
01747       return send_error (auth, "Unknown command");
01748     }
01749 }
01750 
01751 static dbus_bool_t
01752 handle_client_state_waiting_for_reject (DBusAuth         *auth,
01753                                         DBusAuthCommand   command,
01754                                         const DBusString *args)
01755 {
01756   switch (command)
01757     {
01758     case DBUS_AUTH_COMMAND_REJECTED:
01759       return process_rejected (auth, args);
01760       
01761     case DBUS_AUTH_COMMAND_AUTH:
01762     case DBUS_AUTH_COMMAND_CANCEL:
01763     case DBUS_AUTH_COMMAND_DATA:
01764     case DBUS_AUTH_COMMAND_BEGIN:
01765     case DBUS_AUTH_COMMAND_OK:
01766     case DBUS_AUTH_COMMAND_ERROR:
01767     case DBUS_AUTH_COMMAND_UNKNOWN:
01768     default:
01769       goto_state (auth, &common_state_need_disconnect);
01770       return TRUE;
01771     }
01772 }
01773 
01777 typedef struct {
01778   const char *name;        
01779   DBusAuthCommand command; 
01780 } DBusAuthCommandName;
01781 
01782 static DBusAuthCommandName auth_command_names[] = {
01783   { "AUTH",     DBUS_AUTH_COMMAND_AUTH },
01784   { "CANCEL",   DBUS_AUTH_COMMAND_CANCEL },
01785   { "DATA",     DBUS_AUTH_COMMAND_DATA },
01786   { "BEGIN",    DBUS_AUTH_COMMAND_BEGIN },
01787   { "REJECTED", DBUS_AUTH_COMMAND_REJECTED },
01788   { "OK",       DBUS_AUTH_COMMAND_OK },
01789   { "ERROR",    DBUS_AUTH_COMMAND_ERROR }
01790 };
01791 
01792 static DBusAuthCommand
01793 lookup_command_from_name (DBusString *command)
01794 {
01795   int i;
01796 
01797   for (i = 0; i < _DBUS_N_ELEMENTS (auth_command_names); i++)
01798     {
01799       if (_dbus_string_equal_c_str (command,
01800                                     auth_command_names[i].name))
01801         return auth_command_names[i].command;
01802     }
01803 
01804   return DBUS_AUTH_COMMAND_UNKNOWN;
01805 }
01806 
01807 static void
01808 goto_state (DBusAuth *auth, const DBusAuthStateData *state)
01809 {
01810   _dbus_verbose ("%s: going from state %s to state %s\n",
01811                  DBUS_AUTH_NAME (auth),
01812                  auth->state->name,
01813                  state->name);
01814 
01815   auth->state = state;
01816 }
01817 
01818 /* returns whether to call it again right away */
01819 static dbus_bool_t
01820 process_command (DBusAuth *auth)
01821 {
01822   DBusAuthCommand command;
01823   DBusString line;
01824   DBusString args;
01825   int eol;
01826   int i, j;
01827   dbus_bool_t retval;
01828 
01829   /* _dbus_verbose ("%s:   trying process_command()\n"); */
01830   
01831   retval = FALSE;
01832   
01833   eol = 0;
01834   if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
01835     return FALSE;
01836   
01837   if (!_dbus_string_init (&line))
01838     {
01839       auth->needed_memory = TRUE;
01840       return FALSE;
01841     }
01842 
01843   if (!_dbus_string_init (&args))
01844     {
01845       _dbus_string_free (&line);
01846       auth->needed_memory = TRUE;
01847       return FALSE;
01848     }
01849   
01850   if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &line, 0))
01851     goto out;
01852 
01853   if (!_dbus_string_validate_ascii (&line, 0,
01854                                     _dbus_string_get_length (&line)))
01855     {
01856       _dbus_verbose ("%s: Command contained non-ASCII chars or embedded nul\n",
01857                      DBUS_AUTH_NAME (auth));
01858       if (!send_error (auth, "Command contained non-ASCII"))
01859         goto out;
01860       else
01861         goto next_command;
01862     }
01863   
01864   _dbus_verbose ("%s: got command \"%s\"\n",
01865                  DBUS_AUTH_NAME (auth),
01866                  _dbus_string_get_const_data (&line));
01867   
01868   _dbus_string_find_blank (&line, 0, &i);
01869   _dbus_string_skip_blank (&line, i, &j);
01870 
01871   if (j > i)
01872     _dbus_string_delete (&line, i, j - i);
01873   
01874   if (!_dbus_string_move (&line, i, &args, 0))
01875     goto out;
01876   
01877   command = lookup_command_from_name (&line);
01878   if (!(* auth->state->handler) (auth, command, &args))
01879     goto out;
01880 
01881  next_command:
01882   
01883   /* We've succeeded in processing the whole command so drop it out
01884    * of the incoming buffer and return TRUE to try another command.
01885    */
01886 
01887   _dbus_string_delete (&auth->incoming, 0, eol);
01888   
01889   /* kill the \r\n */
01890   _dbus_string_delete (&auth->incoming, 0, 2);
01891 
01892   retval = TRUE;
01893   
01894  out:
01895   _dbus_string_free (&args);
01896   _dbus_string_free (&line);
01897 
01898   if (!retval)
01899     auth->needed_memory = TRUE;
01900   else
01901     auth->needed_memory = FALSE;
01902   
01903   return retval;
01904 }
01905 
01906 
01921 DBusAuth*
01922 _dbus_auth_server_new (void)
01923 {
01924   DBusAuth *auth;
01925   DBusAuthServer *server_auth;
01926 
01927   auth = _dbus_auth_new (sizeof (DBusAuthServer));
01928   if (auth == NULL)
01929     return NULL;
01930 
01931   auth->side = auth_side_server;
01932   auth->state = &server_state_waiting_for_auth;
01933 
01934   server_auth = DBUS_AUTH_SERVER (auth);
01935 
01936   /* perhaps this should be per-mechanism with a lower
01937    * max
01938    */
01939   server_auth->failures = 0;
01940   server_auth->max_failures = 6;
01941   
01942   return auth;
01943 }
01944 
01952 DBusAuth*
01953 _dbus_auth_client_new (void)
01954 {
01955   DBusAuth *auth;
01956 
01957   auth = _dbus_auth_new (sizeof (DBusAuthClient));
01958   if (auth == NULL)
01959     return NULL;
01960 
01961   auth->side = auth_side_client;
01962   auth->state = &client_state_need_send_auth;
01963 
01964   /* Start the auth conversation by sending AUTH for our default
01965    * mechanism */
01966   if (!send_auth (auth, &all_mechanisms[0]))
01967     {
01968       _dbus_auth_unref (auth);
01969       return NULL;
01970     }
01971   
01972   return auth;
01973 }
01974 
01981 DBusAuth *
01982 _dbus_auth_ref (DBusAuth *auth)
01983 {
01984   _dbus_assert (auth != NULL);
01985   
01986   auth->refcount += 1;
01987   
01988   return auth;
01989 }
01990 
01996 void
01997 _dbus_auth_unref (DBusAuth *auth)
01998 {
01999   _dbus_assert (auth != NULL);
02000   _dbus_assert (auth->refcount > 0);
02001 
02002   auth->refcount -= 1;
02003   if (auth->refcount == 0)
02004     {
02005       shutdown_mech (auth);
02006 
02007       if (DBUS_AUTH_IS_CLIENT (auth))
02008         {
02009           _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
02010         }
02011 
02012       if (auth->keyring)
02013         _dbus_keyring_unref (auth->keyring);
02014 
02015       _dbus_string_free (&auth->context);
02016       _dbus_string_free (&auth->challenge);
02017       _dbus_string_free (&auth->identity);
02018       _dbus_string_free (&auth->incoming);
02019       _dbus_string_free (&auth->outgoing);
02020 
02021       dbus_free_string_array (auth->allowed_mechs);
02022       
02023       dbus_free (auth);
02024     }
02025 }
02026 
02035 dbus_bool_t
02036 _dbus_auth_set_mechanisms (DBusAuth    *auth,
02037                            const char **mechanisms)
02038 {
02039   char **copy;
02040 
02041   if (mechanisms != NULL)
02042     {
02043       copy = _dbus_dup_string_array (mechanisms);
02044       if (copy == NULL)
02045         return FALSE;
02046     }
02047   else
02048     copy = NULL;
02049   
02050   dbus_free_string_array (auth->allowed_mechs);
02051 
02052   auth->allowed_mechs = copy;
02053 
02054   return TRUE;
02055 }
02056 
02061 #define DBUS_AUTH_IN_END_STATE(auth) ((auth)->state->handler == NULL)
02062 
02070 DBusAuthState
02071 _dbus_auth_do_work (DBusAuth *auth)
02072 {
02073   auth->needed_memory = FALSE;
02074 
02075   /* Max amount we'll buffer up before deciding someone's on crack */
02076 #define MAX_BUFFER (16 * _DBUS_ONE_KILOBYTE)
02077 
02078   do
02079     {
02080       if (DBUS_AUTH_IN_END_STATE (auth))
02081         break;
02082       
02083       if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
02084           _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
02085         {
02086           goto_state (auth, &common_state_need_disconnect);
02087           _dbus_verbose ("%s: Disconnecting due to excessive data buffered in auth phase\n",
02088                          DBUS_AUTH_NAME (auth));
02089           break;
02090         }
02091     }
02092   while (process_command (auth));
02093 
02094   if (auth->needed_memory)
02095     return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
02096   else if (_dbus_string_get_length (&auth->outgoing) > 0)
02097     return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
02098   else if (auth->state == &common_state_need_disconnect)
02099     return DBUS_AUTH_STATE_NEED_DISCONNECT;
02100   else if (auth->state == &common_state_authenticated)
02101     return DBUS_AUTH_STATE_AUTHENTICATED;
02102   else return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
02103 }
02104 
02114 dbus_bool_t
02115 _dbus_auth_get_bytes_to_send (DBusAuth          *auth,
02116                               const DBusString **str)
02117 {
02118   _dbus_assert (auth != NULL);
02119   _dbus_assert (str != NULL);
02120 
02121   *str = NULL;
02122   
02123   if (_dbus_string_get_length (&auth->outgoing) == 0)
02124     return FALSE;
02125 
02126   *str = &auth->outgoing;
02127 
02128   return TRUE;
02129 }
02130 
02139 void
02140 _dbus_auth_bytes_sent (DBusAuth *auth,
02141                        int       bytes_sent)
02142 {
02143   _dbus_verbose ("%s: Sent %d bytes of: %s\n",
02144                  DBUS_AUTH_NAME (auth),
02145                  bytes_sent,
02146                  _dbus_string_get_const_data (&auth->outgoing));
02147   
02148   _dbus_string_delete (&auth->outgoing,
02149                        0, bytes_sent);
02150 }
02151 
02159 void
02160 _dbus_auth_get_buffer (DBusAuth     *auth,
02161                        DBusString **buffer)
02162 {
02163   _dbus_assert (auth != NULL);
02164   _dbus_assert (!auth->buffer_outstanding);
02165   
02166   *buffer = &auth->incoming;
02167 
02168   auth->buffer_outstanding = TRUE;
02169 }
02170 
02178 void
02179 _dbus_auth_return_buffer (DBusAuth               *auth,
02180                           DBusString             *buffer,
02181                           int                     bytes_read)
02182 {
02183   _dbus_assert (buffer == &auth->incoming);
02184   _dbus_assert (auth->buffer_outstanding);
02185 
02186   auth->buffer_outstanding = FALSE;
02187 }
02188 
02198 void
02199 _dbus_auth_get_unused_bytes (DBusAuth           *auth,
02200                              const DBusString **str)
02201 {
02202   if (!DBUS_AUTH_IN_END_STATE (auth))
02203     return;
02204 
02205   *str = &auth->incoming;
02206 }
02207 
02208 
02215 void
02216 _dbus_auth_delete_unused_bytes (DBusAuth *auth)
02217 {
02218   if (!DBUS_AUTH_IN_END_STATE (auth))
02219     return;
02220 
02221   _dbus_string_set_length (&auth->incoming, 0);
02222 }
02223 
02232 dbus_bool_t
02233 _dbus_auth_needs_encoding (DBusAuth *auth)
02234 {
02235   if (auth->state != &common_state_authenticated)
02236     return FALSE;
02237   
02238   if (auth->mech != NULL)
02239     {
02240       if (DBUS_AUTH_IS_CLIENT (auth))
02241         return auth->mech->client_encode_func != NULL;
02242       else
02243         return auth->mech->server_encode_func != NULL;
02244     }
02245   else
02246     return FALSE;
02247 }
02248 
02259 dbus_bool_t
02260 _dbus_auth_encode_data (DBusAuth         *auth,
02261                         const DBusString *plaintext,
02262                         DBusString       *encoded)
02263 {
02264   _dbus_assert (plaintext != encoded);
02265   
02266   if (auth->state != &common_state_authenticated)
02267     return FALSE;
02268   
02269   if (_dbus_auth_needs_encoding (auth))
02270     {
02271       if (DBUS_AUTH_IS_CLIENT (auth))
02272         return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
02273       else
02274         return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
02275     }
02276   else
02277     {
02278       return _dbus_string_copy (plaintext, 0, encoded,
02279                                 _dbus_string_get_length (encoded));
02280     }
02281 }
02282 
02291 dbus_bool_t
02292 _dbus_auth_needs_decoding (DBusAuth *auth)
02293 {
02294   if (auth->state != &common_state_authenticated)
02295     return FALSE;
02296     
02297   if (auth->mech != NULL)
02298     {
02299       if (DBUS_AUTH_IS_CLIENT (auth))
02300         return auth->mech->client_decode_func != NULL;
02301       else
02302         return auth->mech->server_decode_func != NULL;
02303     }
02304   else
02305     return FALSE;
02306 }
02307 
02308 
02322 dbus_bool_t
02323 _dbus_auth_decode_data (DBusAuth         *auth,
02324                         const DBusString *encoded,
02325                         DBusString       *plaintext)
02326 {
02327   _dbus_assert (plaintext != encoded);
02328   
02329   if (auth->state != &common_state_authenticated)
02330     return FALSE;
02331   
02332   if (_dbus_auth_needs_decoding (auth))
02333     {
02334       if (DBUS_AUTH_IS_CLIENT (auth))
02335         return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
02336       else
02337         return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
02338     }
02339   else
02340     {
02341       return _dbus_string_copy (encoded, 0, plaintext,
02342                                 _dbus_string_get_length (plaintext));
02343     }
02344 }
02345 
02353 void
02354 _dbus_auth_set_credentials (DBusAuth               *auth,
02355                             const DBusCredentials  *credentials)
02356 {
02357   auth->credentials = *credentials;
02358 }
02359 
02367 void
02368 _dbus_auth_get_identity (DBusAuth               *auth,
02369                          DBusCredentials        *credentials)
02370 {
02371   if (auth->state == &common_state_authenticated)
02372     *credentials = auth->authorized_identity;
02373   else
02374     _dbus_credentials_clear (credentials);
02375 }
02376 
02385 dbus_bool_t
02386 _dbus_auth_set_context (DBusAuth               *auth,
02387                         const DBusString       *context)
02388 {
02389   return _dbus_string_replace_len (context, 0, _dbus_string_get_length (context),
02390                                    &auth->context, 0, _dbus_string_get_length (context));
02391 }
02392 
02395 #ifdef DBUS_BUILD_TESTS
02396 #include "dbus-test.h"
02397 #include "dbus-auth-script.h"
02398 #include <stdio.h>
02399 
02400 static dbus_bool_t
02401 process_test_subdir (const DBusString          *test_base_dir,
02402                      const char                *subdir)
02403 {
02404   DBusString test_directory;
02405   DBusString filename;
02406   DBusDirIter *dir;
02407   dbus_bool_t retval;
02408   DBusError error;
02409 
02410   retval = FALSE;
02411   dir = NULL;
02412   
02413   if (!_dbus_string_init (&test_directory))
02414     _dbus_assert_not_reached ("didn't allocate test_directory\n");
02415 
02416   _dbus_string_init_const (&filename, subdir);
02417   
02418   if (!_dbus_string_copy (test_base_dir, 0,
02419                           &test_directory, 0))
02420     _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
02421   
02422   if (!_dbus_concat_dir_and_file (&test_directory, &filename))    
02423     _dbus_assert_not_reached ("couldn't allocate full path");
02424 
02425   _dbus_string_free (&filename);
02426   if (!_dbus_string_init (&filename))
02427     _dbus_assert_not_reached ("didn't allocate filename string\n");
02428 
02429   dbus_error_init (&error);
02430   dir = _dbus_directory_open (&test_directory, &error);
02431   if (dir == NULL)
02432     {
02433       _dbus_warn ("Could not open %s: %s\n",
02434                   _dbus_string_get_const_data (&test_directory),
02435                   error.message);
02436       dbus_error_free (&error);
02437       goto failed;
02438     }
02439 
02440   printf ("Testing %s:\n", subdir);
02441   
02442  next:
02443   while (_dbus_directory_get_next_file (dir, &filename, &error))
02444     {
02445       DBusString full_path;
02446       
02447       if (!_dbus_string_init (&full_path))
02448         _dbus_assert_not_reached ("couldn't init string");
02449 
02450       if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
02451         _dbus_assert_not_reached ("couldn't copy dir to full_path");
02452 
02453       if (!_dbus_concat_dir_and_file (&full_path, &filename))
02454         _dbus_assert_not_reached ("couldn't concat file to dir");
02455 
02456       if (!_dbus_string_ends_with_c_str (&filename, ".auth-script"))
02457         {
02458           _dbus_verbose ("Skipping non-.auth-script file %s\n",
02459                          _dbus_string_get_const_data (&filename));
02460           _dbus_string_free (&full_path);
02461           goto next;
02462         }
02463 
02464       printf ("    %s\n", _dbus_string_get_const_data (&filename));
02465       
02466       if (!_dbus_auth_script_run (&full_path))
02467         {
02468           _dbus_string_free (&full_path);
02469           goto failed;
02470         }
02471       else
02472         _dbus_string_free (&full_path);
02473     }
02474 
02475   if (dbus_error_is_set (&error))
02476     {
02477       _dbus_warn ("Could not get next file in %s: %s\n",
02478                   _dbus_string_get_const_data (&test_directory), error.message);
02479       dbus_error_free (&error);
02480       goto failed;
02481     }
02482     
02483   retval = TRUE;
02484   
02485  failed:
02486 
02487   if (dir)
02488     _dbus_directory_close (dir);
02489   _dbus_string_free (&test_directory);
02490   _dbus_string_free (&filename);
02491 
02492   return retval;
02493 }
02494 
02495 static dbus_bool_t
02496 process_test_dirs (const char *test_data_dir)
02497 {
02498   DBusString test_directory;
02499   dbus_bool_t retval;
02500 
02501   retval = FALSE;
02502   
02503   _dbus_string_init_const (&test_directory, test_data_dir);
02504 
02505   if (!process_test_subdir (&test_directory, "auth"))
02506     goto failed;
02507 
02508   retval = TRUE;
02509   
02510  failed:
02511 
02512   _dbus_string_free (&test_directory);
02513   
02514   return retval;
02515 }
02516 
02517 dbus_bool_t
02518 _dbus_auth_test (const char *test_data_dir)
02519 {
02520   
02521   if (test_data_dir == NULL)
02522     return TRUE;
02523   
02524   if (!process_test_dirs (test_data_dir))
02525     return FALSE;
02526 
02527   return TRUE;
02528 }
02529 
02530 #endif /* DBUS_BUILD_TESTS */

Generated on Sat Sep 25 19:17:11 2004 for D-BUS by  doxygen 1.3.8-20040913