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

Generated on Wed Jun 9 05:01:25 2004 for D-BUS by doxygen1.2.15