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-protocol.h"
00030 #include "dbus-userdb.h"
00031 
00068 typedef dbus_bool_t (* DBusInitialResponseFunction)  (DBusAuth         *auth,
00069                                                       DBusString       *response);
00070 
00075 typedef dbus_bool_t (* DBusAuthDataFunction)     (DBusAuth         *auth,
00076                                                   const DBusString *data);
00077 
00081 typedef dbus_bool_t (* DBusAuthEncodeFunction)   (DBusAuth         *auth,
00082                                                   const DBusString *data,
00083                                                   DBusString       *encoded);
00084 
00088 typedef dbus_bool_t (* DBusAuthDecodeFunction)   (DBusAuth         *auth,
00089                                                   const DBusString *data,
00090                                                   DBusString       *decoded);
00091 
00095 typedef void        (* DBusAuthShutdownFunction) (DBusAuth       *auth);
00096 
00100 typedef struct
00101 {
00102   const char *mechanism; 
00103   DBusAuthDataFunction server_data_func; 
00104   DBusAuthEncodeFunction server_encode_func; 
00105   DBusAuthDecodeFunction server_decode_func; 
00106   DBusAuthShutdownFunction server_shutdown_func; 
00107   DBusInitialResponseFunction client_initial_response_func; 
00108   DBusAuthDataFunction client_data_func; 
00109   DBusAuthEncodeFunction client_encode_func; 
00110   DBusAuthDecodeFunction client_decode_func; 
00111   DBusAuthShutdownFunction client_shutdown_func; 
00112 } DBusAuthMechanismHandler;
00113 
00117 typedef enum {
00118   DBUS_AUTH_COMMAND_AUTH,
00119   DBUS_AUTH_COMMAND_CANCEL,
00120   DBUS_AUTH_COMMAND_DATA,
00121   DBUS_AUTH_COMMAND_BEGIN,
00122   DBUS_AUTH_COMMAND_REJECTED,
00123   DBUS_AUTH_COMMAND_OK,
00124   DBUS_AUTH_COMMAND_ERROR,
00125   DBUS_AUTH_COMMAND_UNKNOWN
00126 } DBusAuthCommand;
00127 
00133 typedef dbus_bool_t (* DBusAuthStateFunction) (DBusAuth         *auth,
00134                                                DBusAuthCommand   command,
00135                                                const DBusString *args);
00136 
00140 typedef struct
00141 {
00142   const char *name;               
00143   DBusAuthStateFunction handler;  
00144 } DBusAuthStateData;
00145 
00149 struct DBusAuth
00150 {
00151   int refcount;           
00152   const char *side;       
00154   DBusString incoming;    
00155   DBusString outgoing;    
00157   const DBusAuthStateData *state;         
00159   const DBusAuthMechanismHandler *mech;   
00161   DBusString identity;                   
00165   DBusCredentials credentials;      
00169   DBusCredentials authorized_identity; 
00171   DBusCredentials desired_identity;    
00173   DBusString context;               
00174   DBusKeyring *keyring;             
00175   int cookie_id;                    
00176   DBusString challenge;             
00178   char **allowed_mechs;             
00182   unsigned int needed_memory : 1;   
00185   unsigned int already_got_mechanisms : 1;       
00186   unsigned int already_asked_for_initial_response : 1; 
00187   unsigned int buffer_outstanding : 1; 
00188 };
00189 
00193 typedef struct
00194 {
00195   DBusAuth base;    
00197   DBusList *mechs_to_try; 
00199   DBusString guid_from_server; 
00201 } DBusAuthClient;
00202 
00206 typedef struct
00207 {
00208   DBusAuth base;    
00210   int failures;     
00211   int max_failures; 
00213   DBusString guid;  
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                                               const DBusString *args_from_ok);
00229 static dbus_bool_t send_cancel               (DBusAuth *auth);
00230 
00235 static dbus_bool_t handle_server_state_waiting_for_auth  (DBusAuth         *auth,
00236                                                           DBusAuthCommand   command,
00237                                                           const DBusString *args);
00238 static dbus_bool_t handle_server_state_waiting_for_data  (DBusAuth         *auth,
00239                                                           DBusAuthCommand   command,
00240                                                           const DBusString *args);
00241 static dbus_bool_t handle_server_state_waiting_for_begin (DBusAuth         *auth,
00242                                                           DBusAuthCommand   command,
00243                                                           const DBusString *args);
00244   
00245 static const DBusAuthStateData server_state_waiting_for_auth = {
00246   "WaitingForAuth", handle_server_state_waiting_for_auth
00247 };
00248 static const DBusAuthStateData server_state_waiting_for_data = {
00249   "WaitingForData", handle_server_state_waiting_for_data
00250 };
00251 static const DBusAuthStateData server_state_waiting_for_begin = {
00252   "WaitingForBegin", handle_server_state_waiting_for_begin
00253 };
00254   
00259 static dbus_bool_t handle_client_state_waiting_for_data   (DBusAuth         *auth,
00260                                                            DBusAuthCommand   command,
00261                                                            const DBusString *args);
00262 static dbus_bool_t handle_client_state_waiting_for_ok     (DBusAuth         *auth,
00263                                                            DBusAuthCommand   command,
00264                                                            const DBusString *args);
00265 static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth         *auth,
00266                                                            DBusAuthCommand   command,
00267                                                            const DBusString *args);
00268 
00269 static const DBusAuthStateData client_state_need_send_auth = {
00270   "NeedSendAuth", NULL
00271 };
00272 static const DBusAuthStateData client_state_waiting_for_data = {
00273   "WaitingForData", handle_client_state_waiting_for_data
00274 };
00275 static const DBusAuthStateData client_state_waiting_for_ok = {
00276   "WaitingForOK", handle_client_state_waiting_for_ok
00277 };
00278 static const DBusAuthStateData client_state_waiting_for_reject = {
00279   "WaitingForReject", handle_client_state_waiting_for_reject
00280 };
00281   
00286 static const DBusAuthStateData common_state_authenticated = {
00287   "Authenticated",  NULL
00288 };
00289 
00290 static const DBusAuthStateData common_state_need_disconnect = {
00291   "NeedDisconnect",  NULL
00292 };
00293 
00294 static const char auth_side_client[] = "client";
00295 static const char auth_side_server[] = "server";
00300 #define DBUS_AUTH_IS_SERVER(auth) ((auth)->side == auth_side_server)
00301 
00305 #define DBUS_AUTH_IS_CLIENT(auth) ((auth)->side == auth_side_client)
00306 
00310 #define DBUS_AUTH_CLIENT(auth)    ((DBusAuthClient*)(auth))
00311 
00315 #define DBUS_AUTH_SERVER(auth)    ((DBusAuthServer*)(auth))
00316 
00322 #define DBUS_AUTH_NAME(auth)      ((auth)->side)
00323 
00324 static DBusAuth*
00325 _dbus_auth_new (int size)
00326 {
00327   DBusAuth *auth;
00328   
00329   auth = dbus_malloc0 (size);
00330   if (auth == NULL)
00331     return NULL;
00332   
00333   auth->refcount = 1;
00334 
00335   _dbus_credentials_clear (&auth->credentials);
00336   _dbus_credentials_clear (&auth->authorized_identity);
00337   _dbus_credentials_clear (&auth->desired_identity);
00338   
00339   auth->keyring = NULL;
00340   auth->cookie_id = -1;
00341   
00342   /* note that we don't use the max string length feature,
00343    * because you can't use that feature if you're going to
00344    * try to recover from out-of-memory (it creates
00345    * what looks like unrecoverable inability to alloc
00346    * more space in the string). But we do handle
00347    * overlong buffers in _dbus_auth_do_work().
00348    */
00349   
00350   if (!_dbus_string_init (&auth->incoming))
00351     goto enomem_0;
00352 
00353   if (!_dbus_string_init (&auth->outgoing))
00354     goto enomem_1;
00355     
00356   if (!_dbus_string_init (&auth->identity))
00357     goto enomem_2;
00358 
00359   if (!_dbus_string_init (&auth->context))
00360     goto enomem_3;
00361 
00362   if (!_dbus_string_init (&auth->challenge))
00363     goto enomem_4;
00364 
00365   /* default context if none is specified */
00366   if (!_dbus_string_append (&auth->context, "org_freedesktop_general"))
00367     goto enomem_5;
00368   
00369   return auth;
00370 
00371  enomem_5:
00372   _dbus_string_free (&auth->challenge);
00373  enomem_4:
00374   _dbus_string_free (&auth->context);
00375  enomem_3:
00376   _dbus_string_free (&auth->identity);
00377  enomem_2:
00378   _dbus_string_free (&auth->outgoing);
00379  enomem_1:
00380   _dbus_string_free (&auth->incoming);
00381  enomem_0:
00382   dbus_free (auth);
00383   return NULL;
00384 }
00385 
00386 static void
00387 shutdown_mech (DBusAuth *auth)
00388 {
00389   /* Cancel any auth */
00390   auth->already_asked_for_initial_response = FALSE;
00391   _dbus_string_set_length (&auth->identity, 0);
00392 
00393   _dbus_credentials_clear (&auth->authorized_identity);
00394   _dbus_credentials_clear (&auth->desired_identity);
00395   
00396   if (auth->mech != NULL)
00397     {
00398       _dbus_verbose ("%s: Shutting down mechanism %s\n",
00399                      DBUS_AUTH_NAME (auth), auth->mech->mechanism);
00400       
00401       if (DBUS_AUTH_IS_CLIENT (auth))
00402         (* auth->mech->client_shutdown_func) (auth);
00403       else
00404         (* auth->mech->server_shutdown_func) (auth);
00405       
00406       auth->mech = NULL;
00407     }
00408 }
00409 
00410 /* Returns TRUE but with an empty string hash if the
00411  * cookie_id isn't known. As with all this code
00412  * TRUE just means we had enough memory.
00413  */
00414 static dbus_bool_t
00415 sha1_compute_hash (DBusAuth         *auth,
00416                    int               cookie_id,
00417                    const DBusString *server_challenge,
00418                    const DBusString *client_challenge,
00419                    DBusString       *hash)
00420 {
00421   DBusString cookie;
00422   DBusString to_hash;
00423   dbus_bool_t retval;
00424   
00425   _dbus_assert (auth->keyring != NULL);
00426 
00427   retval = FALSE;
00428   
00429   if (!_dbus_string_init (&cookie))
00430     return FALSE;
00431 
00432   if (!_dbus_keyring_get_hex_key (auth->keyring, cookie_id,
00433                                   &cookie))
00434     goto out_0;
00435 
00436   if (_dbus_string_get_length (&cookie) == 0)
00437     {
00438       retval = TRUE;
00439       goto out_0;
00440     }
00441 
00442   if (!_dbus_string_init (&to_hash))
00443     goto out_0;
00444   
00445   if (!_dbus_string_copy (server_challenge, 0,
00446                           &to_hash, _dbus_string_get_length (&to_hash)))
00447     goto out_1;
00448 
00449   if (!_dbus_string_append (&to_hash, ":"))
00450     goto out_1;
00451   
00452   if (!_dbus_string_copy (client_challenge, 0,
00453                           &to_hash, _dbus_string_get_length (&to_hash)))
00454     goto out_1;
00455 
00456   if (!_dbus_string_append (&to_hash, ":"))
00457     goto out_1;
00458 
00459   if (!_dbus_string_copy (&cookie, 0,
00460                           &to_hash, _dbus_string_get_length (&to_hash)))
00461     goto out_1;
00462 
00463   if (!_dbus_sha_compute (&to_hash, hash))
00464     goto out_1;
00465   
00466   retval = TRUE;
00467 
00468  out_1:
00469   _dbus_string_zero (&to_hash);
00470   _dbus_string_free (&to_hash);
00471  out_0:
00472   _dbus_string_zero (&cookie);
00473   _dbus_string_free (&cookie);
00474   return retval;
00475 }
00476 
00481 #define N_CHALLENGE_BYTES (128/8)
00482 
00483 static dbus_bool_t
00484 sha1_handle_first_client_response (DBusAuth         *auth,
00485                                    const DBusString *data)
00486 {
00487   /* We haven't sent a challenge yet, we're expecting a desired
00488    * username from the client.
00489    */
00490   DBusString tmp;
00491   DBusString tmp2;
00492   dbus_bool_t retval;
00493   DBusError error;
00494   
00495   retval = FALSE;
00496 
00497   _dbus_string_set_length (&auth->challenge, 0);
00498   
00499   if (_dbus_string_get_length (data) > 0)
00500     {
00501       if (_dbus_string_get_length (&auth->identity) > 0)
00502         {
00503           /* Tried to send two auth identities, wtf */
00504           _dbus_verbose ("%s: client tried to send auth identity, but we already have one\n",
00505                          DBUS_AUTH_NAME (auth));
00506           return send_rejected (auth);
00507         }
00508       else
00509         {
00510           /* this is our auth identity */
00511           if (!_dbus_string_copy (data, 0, &auth->identity, 0))
00512             return FALSE;
00513         }
00514     }
00515       
00516   if (!_dbus_credentials_from_username (data, &auth->desired_identity))
00517     {
00518       _dbus_verbose ("%s: Did not get a valid username from client\n",
00519                      DBUS_AUTH_NAME (auth));
00520       return send_rejected (auth);
00521     }
00522       
00523   if (!_dbus_string_init (&tmp))
00524     return FALSE;
00525 
00526   if (!_dbus_string_init (&tmp2))
00527     {
00528       _dbus_string_free (&tmp);
00529       return FALSE;
00530     }
00531 
00532   /* we cache the keyring for speed, so here we drop it if it's the
00533    * wrong one. FIXME caching the keyring here is useless since we use
00534    * a different DBusAuth for every connection.
00535    */
00536   if (auth->keyring &&
00537       !_dbus_keyring_is_for_user (auth->keyring,
00538                                   data))
00539     {
00540       _dbus_keyring_unref (auth->keyring);
00541       auth->keyring = NULL;
00542     }
00543   
00544   if (auth->keyring == NULL)
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_parse_uid (&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.pid = auth->credentials.pid;
01052       auth->authorized_identity.uid = auth->desired_identity.uid;
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)