|
gsasl
1.7.6
|
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 }
1.7.6.1