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

Generated on Tue Feb 10 18:14:01 2004 for D-BUS by doxygen 1.3.5