gsasl  1.8.0
scram/client.c
Go to the documentation of this file.
00001 /* client.c --- SASL SCRAM client side functions.
00002  * Copyright (C) 2009-2012 Simon Josefsson
00003  *
00004  * This file is part of GNU SASL Library.
00005  *
00006  * GNU SASL Library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public License
00008  * as published by the Free Software Foundation; either version 2.1 of
00009  * the License, or (at your option) any later version.
00010  *
00011  * GNU SASL Library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with GNU SASL Library; if not, write to the Free
00018  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include "config.h"
00025 #endif
00026 
00027 /* Get specification. */
00028 #include "scram.h"
00029 
00030 /* Get malloc, free. */
00031 #include <stdlib.h>
00032 
00033 /* Get memcpy, strlen, strchr. */
00034 #include <string.h>
00035 
00036 /* Get bool. */
00037 #include <stdbool.h>
00038 
00039 #include "tokens.h"
00040 #include "parser.h"
00041 #include "printer.h"
00042 #include "gc.h"
00043 #include "memxor.h"
00044 
00045 #define CNONCE_ENTROPY_BYTES 18
00046 
00047 struct scram_client_state
00048 {
00049   int plus;
00050   int step;
00051   char *cfmb;                   /* client first message bare */
00052   char *serversignature;
00053   char *authmessage;
00054   char *cbtlsunique;
00055   size_t cbtlsuniquelen;
00056   struct scram_client_first cf;
00057   struct scram_server_first sf;
00058   struct scram_client_final cl;
00059   struct scram_server_final sl;
00060 };
00061 
00062 static int
00063 scram_start (Gsasl_session * sctx, void **mech_data, int plus)
00064 {
00065   struct scram_client_state *state;
00066   char buf[CNONCE_ENTROPY_BYTES];
00067   const char *p;
00068   int rc;
00069 
00070   state = (struct scram_client_state *) calloc (sizeof (*state), 1);
00071   if (state == NULL)
00072     return GSASL_MALLOC_ERROR;
00073 
00074   state->plus = plus;
00075 
00076   rc = gsasl_nonce (buf, CNONCE_ENTROPY_BYTES);
00077   if (rc != GSASL_OK)
00078     {
00079       free (state);
00080       return rc;
00081     }
00082 
00083   rc = gsasl_base64_to (buf, CNONCE_ENTROPY_BYTES,
00084                         &state->cf.client_nonce, NULL);
00085   if (rc != GSASL_OK)
00086     {
00087       free (state);
00088       return rc;
00089     }
00090 
00091   p = gsasl_property_get (sctx, GSASL_CB_TLS_UNIQUE);
00092   if (state->plus && !p)
00093     {
00094       free (state->cf.client_nonce);
00095       free (state);
00096       return GSASL_NO_CB_TLS_UNIQUE;
00097     }
00098   if (p)
00099     {
00100       rc = gsasl_base64_from (p, strlen (p), &state->cbtlsunique,
00101                               &state->cbtlsuniquelen);
00102       if (rc != GSASL_OK)
00103         {
00104           free (state->cf.client_nonce);
00105           free (state);
00106           return rc;
00107         }
00108     }
00109 
00110   *mech_data = state;
00111 
00112   return GSASL_OK;
00113 }
00114 
00115 int
00116 _gsasl_scram_sha1_client_start (Gsasl_session * sctx, void **mech_data)
00117 {
00118   return scram_start (sctx, mech_data, 0);
00119 }
00120 
00121 int
00122 _gsasl_scram_sha1_plus_client_start (Gsasl_session * sctx, void **mech_data)
00123 {
00124   return scram_start (sctx, mech_data, 1);
00125 }
00126 
00127 static char
00128 hexdigit_to_char (char hexdigit)
00129 {
00130   if (hexdigit >= '0' && hexdigit <= '9')
00131     return hexdigit - '0';
00132   if (hexdigit >= 'a' && hexdigit <= 'f')
00133     return hexdigit - 'a' + 10;
00134   return 0;
00135 }
00136 
00137 static char
00138 hex_to_char (char u, char l)
00139 {
00140   return (char) (((unsigned char) hexdigit_to_char (u)) * 16
00141                  + hexdigit_to_char (l));
00142 }
00143 
00144 static void
00145 sha1_hex_to_byte (char *saltedpassword, const char *p)
00146 {
00147   while (*p)
00148     {
00149       *saltedpassword = hex_to_char (p[0], p[1]);
00150       p += 2;
00151       saltedpassword++;
00152     }
00153 }
00154 
00155 static bool
00156 hex_p (const char *hexstr)
00157 {
00158   static const char hexalpha[] = "0123456789abcdef";
00159 
00160   for (; *hexstr; hexstr++)
00161     if (strchr (hexalpha, *hexstr) == NULL)
00162       return false;
00163 
00164   return true;
00165 }
00166 
00167 int
00168 _gsasl_scram_sha1_client_step (Gsasl_session * sctx,
00169                                void *mech_data,
00170                                const char *input, size_t input_len,
00171                                char **output, size_t * output_len)
00172 {
00173   struct scram_client_state *state = mech_data;
00174   int res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
00175   int rc;
00176 
00177   *output = NULL;
00178   *output_len = 0;
00179 
00180   switch (state->step)
00181     {
00182     case 0:
00183       {
00184         const char *p;
00185 
00186         if (state->plus)
00187           {
00188             state->cf.cbflag = 'p';
00189             state->cf.cbname = strdup ("tls-unique");
00190           }
00191         else
00192           {
00193             if (state->cbtlsuniquelen > 0)
00194               state->cf.cbflag = 'y';
00195             else
00196               state->cf.cbflag = 'n';
00197           }
00198 
00199         p = gsasl_property_get (sctx, GSASL_AUTHID);
00200         if (!p)
00201           return GSASL_NO_AUTHID;
00202 
00203         rc = gsasl_saslprep (p, GSASL_ALLOW_UNASSIGNED,
00204                              &state->cf.username, NULL);
00205         if (rc != GSASL_OK)
00206           return rc;
00207 
00208         p = gsasl_property_get (sctx, GSASL_AUTHZID);
00209         if (p)
00210           state->cf.authzid = strdup (p);
00211 
00212         rc = scram_print_client_first (&state->cf, output);
00213         if (rc == -2)
00214           return GSASL_MALLOC_ERROR;
00215         else if (rc != 0)
00216           return GSASL_AUTHENTICATION_ERROR;
00217 
00218         *output_len = strlen (*output);
00219 
00220         /* Point p to client-first-message-bare. */
00221         p = strchr (*output, ',');
00222         if (!p)
00223           return GSASL_AUTHENTICATION_ERROR;
00224         p++;
00225         p = strchr (p, ',');
00226         if (!p)
00227           return GSASL_AUTHENTICATION_ERROR;
00228         p++;
00229 
00230         /* Save "client-first-message-bare" for the next step. */
00231         state->cfmb = strdup (p);
00232         if (!state->cfmb)
00233           return GSASL_MALLOC_ERROR;
00234 
00235         /* Prepare B64("cbind-input") for the next step. */
00236         if (state->cf.cbflag == 'p')
00237           {
00238             size_t len = (p - *output) + state->cbtlsuniquelen;
00239             char *cbind_input = malloc (len);
00240             if (cbind_input == NULL)
00241               return GSASL_MALLOC_ERROR;
00242             memcpy (cbind_input, *output, p - *output);
00243             memcpy (cbind_input + (p - *output), state->cbtlsunique,
00244                     state->cbtlsuniquelen);
00245             rc = gsasl_base64_to (cbind_input, len, &state->cl.cbind, NULL);
00246             free (cbind_input);
00247           }
00248         else
00249           rc = gsasl_base64_to (*output, p - *output, &state->cl.cbind, NULL);
00250         if (rc != 0)
00251           return rc;
00252 
00253         /* We are done. */
00254         state->step++;
00255         return GSASL_NEEDS_MORE;
00256         break;
00257       }
00258 
00259     case 1:
00260       {
00261         if (scram_parse_server_first (input, input_len, &state->sf) < 0)
00262           return GSASL_MECHANISM_PARSE_ERROR;
00263 
00264         if (strlen (state->sf.nonce) < strlen (state->cf.client_nonce) ||
00265             memcmp (state->cf.client_nonce, state->sf.nonce,
00266                     strlen (state->cf.client_nonce)) != 0)
00267           return GSASL_AUTHENTICATION_ERROR;
00268 
00269         state->cl.nonce = strdup (state->sf.nonce);
00270         if (!state->cl.nonce)
00271           return GSASL_MALLOC_ERROR;
00272 
00273         /* Save salt/iter as properties, so that client callback can
00274            access them. */
00275         {
00276           char *str = NULL;
00277           int n;
00278           n = asprintf (&str, "%lu", (unsigned long) state->sf.iter);
00279           if (n < 0 || str == NULL)
00280             return GSASL_MALLOC_ERROR;
00281           gsasl_property_set (sctx, GSASL_SCRAM_ITER, str);
00282           free (str);
00283         }
00284 
00285         gsasl_property_set (sctx, GSASL_SCRAM_SALT, state->sf.salt);
00286 
00287         /* Generate ClientProof. */
00288         {
00289           char saltedpassword[20];
00290           char *clientkey;
00291           char *storedkey;
00292           char *clientsignature;
00293           char clientproof[20];
00294           const char *p;
00295 
00296           /* Get SaltedPassword. */
00297           p = gsasl_property_get (sctx, GSASL_SCRAM_SALTED_PASSWORD);
00298           if (p && strlen (p) == 40 && hex_p (p))
00299             sha1_hex_to_byte (saltedpassword, p);
00300           else if ((p = gsasl_property_get (sctx, GSASL_PASSWORD)) != NULL)
00301             {
00302               Gc_rc err;
00303               char *salt;
00304               size_t saltlen;
00305               char *preppasswd;
00306 
00307               rc = gsasl_saslprep (p, 0, &preppasswd, NULL);
00308               if (rc != GSASL_OK)
00309                 return rc;
00310 
00311               rc = gsasl_base64_from (state->sf.salt, strlen (state->sf.salt),
00312                                       &salt, &saltlen);
00313               if (rc != 0)
00314                 {
00315                   gsasl_free (preppasswd);
00316                   return rc;
00317                 }
00318 
00319               /* SaltedPassword := Hi(password, salt) */
00320               err = gc_pbkdf2_sha1 (preppasswd, strlen (preppasswd),
00321                                     salt, saltlen,
00322                                     state->sf.iter, saltedpassword, 20);
00323               gsasl_free (preppasswd);
00324               gsasl_free (salt);
00325               if (err != GC_OK)
00326                 return GSASL_MALLOC_ERROR;
00327             }
00328           else
00329             return GSASL_NO_PASSWORD;
00330 
00331           /* Get client-final-message-without-proof. */
00332           {
00333             char *cfmwp;
00334             int n;
00335 
00336             state->cl.proof = strdup ("p");
00337             rc = scram_print_client_final (&state->cl, &cfmwp);
00338             if (rc != 0)
00339               return GSASL_MALLOC_ERROR;
00340             free (state->cl.proof);
00341 
00342             /* Compute AuthMessage */
00343             n = asprintf (&state->authmessage, "%s,%.*s,%.*s",
00344                           state->cfmb,
00345                           (int) input_len, input,
00346                           (int) (strlen (cfmwp) - 4), cfmwp);
00347             free (cfmwp);
00348             if (n <= 0 || !state->authmessage)
00349               return GSASL_MALLOC_ERROR;
00350           }
00351 
00352           /* ClientKey := HMAC(SaltedPassword, "Client Key") */
00353 #define CLIENT_KEY "Client Key"
00354           rc = gsasl_hmac_sha1 (saltedpassword, 20,
00355                                 CLIENT_KEY, strlen (CLIENT_KEY), &clientkey);
00356           if (rc != 0)
00357             return rc;
00358 
00359           /* StoredKey := H(ClientKey) */
00360           rc = gsasl_sha1 (clientkey, 20, &storedkey);
00361           if (rc != 0)
00362             {
00363               free (clientkey);
00364               return rc;
00365             }
00366 
00367           /* ClientSignature := HMAC(StoredKey, AuthMessage) */
00368           rc = gsasl_hmac_sha1 (storedkey, 20,
00369                                 state->authmessage,
00370                                 strlen (state->authmessage),
00371                                 &clientsignature);
00372           free (storedkey);
00373           if (rc != 0)
00374             {
00375               free (clientkey);
00376               return rc;
00377             }
00378 
00379           /* ClientProof := ClientKey XOR ClientSignature */
00380           memcpy (clientproof, clientkey, 20);
00381           memxor (clientproof, clientsignature, 20);
00382 
00383           free (clientkey);
00384           free (clientsignature);
00385 
00386           rc = gsasl_base64_to (clientproof, 20, &state->cl.proof, NULL);
00387           if (rc != 0)
00388             return rc;
00389 
00390           /* Generate ServerSignature, for comparison in next step. */
00391           {
00392             char *serverkey;
00393             char *serversignature;
00394 
00395             /* ServerKey := HMAC(SaltedPassword, "Server Key") */
00396 #define SERVER_KEY "Server Key"
00397             rc = gsasl_hmac_sha1 (saltedpassword, 20,
00398                                   SERVER_KEY, strlen (SERVER_KEY),
00399                                   &serverkey);
00400             if (rc != 0)
00401               return rc;
00402 
00403             /* ServerSignature := HMAC(ServerKey, AuthMessage) */
00404             rc = gsasl_hmac_sha1 (serverkey, 20,
00405                                   state->authmessage,
00406                                   strlen (state->authmessage),
00407                                   &serversignature);
00408             gsasl_free (serverkey);
00409             if (rc != 0)
00410               return rc;
00411 
00412             rc = gsasl_base64_to (serversignature, 20,
00413                                   &state->serversignature, NULL);
00414             gsasl_free (serversignature);
00415             if (rc != 0)
00416               return rc;
00417           }
00418         }
00419 
00420         rc = scram_print_client_final (&state->cl, output);
00421         if (rc != 0)
00422           return GSASL_MALLOC_ERROR;
00423 
00424         *output_len = strlen (*output);
00425 
00426         state->step++;
00427         return GSASL_NEEDS_MORE;
00428         break;
00429       }
00430 
00431     case 2:
00432       {
00433         if (scram_parse_server_final (input, input_len, &state->sl) < 0)
00434           return GSASL_MECHANISM_PARSE_ERROR;
00435 
00436         if (strcmp (state->sl.verifier, state->serversignature) != 0)
00437           return GSASL_AUTHENTICATION_ERROR;
00438 
00439         state->step++;
00440         return GSASL_OK;
00441         break;
00442       }
00443 
00444     default:
00445       break;
00446     }
00447 
00448   return res;
00449 }
00450 
00451 void
00452 _gsasl_scram_sha1_client_finish (Gsasl_session * sctx, void *mech_data)
00453 {
00454   struct scram_client_state *state = mech_data;
00455 
00456   if (!state)
00457     return;
00458 
00459   free (state->cfmb);
00460   free (state->serversignature);
00461   free (state->authmessage);
00462   free (state->cbtlsunique);
00463   scram_free_client_first (&state->cf);
00464   scram_free_server_first (&state->sf);
00465   scram_free_client_final (&state->cl);
00466   scram_free_server_final (&state->sl);
00467 
00468   free (state);
00469 }