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

Generated on Wed Oct 22 14:05:01 2003 for D-BUS by doxygen1.3-rc3