1 | /* client.c --- SASL SCRAM client side functions. |
2 | * Copyright (C) 2009-2012 Simon Josefsson |
3 | * |
4 | * This file is part of GNU SASL Library. |
5 | * |
6 | * GNU SASL Library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public License |
8 | * as published by the Free Software Foundation; either version 2.1 of |
9 | * the License, or (at your option) any later version. |
10 | * |
11 | * GNU SASL Library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with GNU SASL Library; if not, write to the Free |
18 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | * |
21 | */ |
22 | |
23 | #ifdef HAVE_CONFIG_H |
24 | #include "config.h" |
25 | #endif |
26 | |
27 | /* Get specification. */ |
28 | #include "scram.h" |
29 | |
30 | /* Get malloc, free. */ |
31 | #include <stdlib.h> |
32 | |
33 | /* Get memcpy, strlen, strchr. */ |
34 | #include <string.h> |
35 | |
36 | /* Get bool. */ |
37 | #include <stdbool.h> |
38 | |
39 | #include "tokens.h" |
40 | #include "parser.h" |
41 | #include "printer.h" |
42 | #include "gc.h" |
43 | #include "memxor.h" |
44 | |
45 | #define CNONCE_ENTROPY_BYTES 18 |
46 | |
47 | struct scram_client_state |
48 | { |
49 | int plus; |
50 | int step; |
51 | char *cfmb; /* client first message bare */ |
52 | char *serversignature; |
53 | char *authmessage; |
54 | char *cbtlsunique; |
55 | size_t cbtlsuniquelen; |
56 | struct scram_client_first cf; |
57 | struct scram_server_first sf; |
58 | struct scram_client_final cl; |
59 | struct scram_server_final sl; |
60 | }; |
61 | |
62 | static int |
63 | scram_start (Gsasl_session * sctx, void **mech_data, int plus) |
64 | { |
65 | struct scram_client_state *state; |
66 | char buf[CNONCE_ENTROPY_BYTES]; |
67 | const char *p; |
68 | int rc; |
69 | |
70 | state = (struct scram_client_state *) calloc (sizeof (*state), 1); |
71 | if (state == NULL) |
72 | return GSASL_MALLOC_ERROR; |
73 | |
74 | state->plus = plus; |
75 | |
76 | rc = gsasl_nonce (buf, CNONCE_ENTROPY_BYTES); |
77 | if (rc != GSASL_OK) |
78 | { |
79 | free (state); |
80 | return rc; |
81 | } |
82 | |
83 | rc = gsasl_base64_to (buf, CNONCE_ENTROPY_BYTES, |
84 | &state->cf.client_nonce, NULL); |
85 | if (rc != GSASL_OK) |
86 | { |
87 | free (state); |
88 | return rc; |
89 | } |
90 | |
91 | p = gsasl_property_get (sctx, GSASL_CB_TLS_UNIQUE); |
92 | if (state->plus && !p) |
93 | { |
94 | free (state->cf.client_nonce); |
95 | free (state); |
96 | return GSASL_NO_CB_TLS_UNIQUE; |
97 | } |
98 | if (p) |
99 | { |
100 | rc = gsasl_base64_from (p, strlen (p), &state->cbtlsunique, |
101 | &state->cbtlsuniquelen); |
102 | if (rc != GSASL_OK) |
103 | { |
104 | free (state->cf.client_nonce); |
105 | free (state); |
106 | return rc; |
107 | } |
108 | } |
109 | |
110 | *mech_data = state; |
111 | |
112 | return GSASL_OK; |
113 | } |
114 | |
115 | int |
116 | _gsasl_scram_sha1_client_start (Gsasl_session * sctx, void **mech_data) |
117 | { |
118 | return scram_start (sctx, mech_data, 0); |
119 | } |
120 | |
121 | int |
122 | _gsasl_scram_sha1_plus_client_start (Gsasl_session * sctx, void **mech_data) |
123 | { |
124 | return scram_start (sctx, mech_data, 1); |
125 | } |
126 | |
127 | static char |
128 | hexdigit_to_char (char hexdigit) |
129 | { |
130 | if (hexdigit >= '0' && hexdigit <= '9') |
131 | return hexdigit - '0'; |
132 | if (hexdigit >= 'a' && hexdigit <= 'f') |
133 | return hexdigit - 'a' + 10; |
134 | return 0; |
135 | } |
136 | |
137 | static char |
138 | hex_to_char (char u, char l) |
139 | { |
140 | return (char) (((unsigned char) hexdigit_to_char (u)) * 16 |
141 | + hexdigit_to_char (l)); |
142 | } |
143 | |
144 | static void |
145 | sha1_hex_to_byte (char *saltedpassword, const char *p) |
146 | { |
147 | while (*p) |
148 | { |
149 | *saltedpassword = hex_to_char (p[0], p[1]); |
150 | p += 2; |
151 | saltedpassword++; |
152 | } |
153 | } |
154 | |
155 | static bool |
156 | hex_p (const char *hexstr) |
157 | { |
158 | static const char hexalpha[] = "0123456789abcdef" ; |
159 | |
160 | for (; *hexstr; hexstr++) |
161 | if (strchr (hexalpha, *hexstr) == NULL) |
162 | return false; |
163 | |
164 | return true; |
165 | } |
166 | |
167 | int |
168 | _gsasl_scram_sha1_client_step (Gsasl_session * sctx, |
169 | void *mech_data, |
170 | const char *input, size_t input_len, |
171 | char **output, size_t * output_len) |
172 | { |
173 | struct scram_client_state *state = mech_data; |
174 | int res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES; |
175 | int rc; |
176 | |
177 | *output = NULL; |
178 | *output_len = 0; |
179 | |
180 | switch (state->step) |
181 | { |
182 | case 0: |
183 | { |
184 | const char *p; |
185 | |
186 | if (state->plus) |
187 | { |
188 | state->cf.cbflag = 'p'; |
189 | state->cf.cbname = strdup ("tls-unique" ); |
190 | } |
191 | else |
192 | { |
193 | if (state->cbtlsuniquelen > 0) |
194 | state->cf.cbflag = 'y'; |
195 | else |
196 | state->cf.cbflag = 'n'; |
197 | } |
198 | |
199 | p = gsasl_property_get (sctx, GSASL_AUTHID); |
200 | if (!p) |
201 | return GSASL_NO_AUTHID; |
202 | |
203 | rc = gsasl_saslprep (p, GSASL_ALLOW_UNASSIGNED, |
204 | &state->cf.username, NULL); |
205 | if (rc != GSASL_OK) |
206 | return rc; |
207 | |
208 | p = gsasl_property_get (sctx, GSASL_AUTHZID); |
209 | if (p) |
210 | state->cf.authzid = strdup (p); |
211 | |
212 | rc = scram_print_client_first (&state->cf, output); |
213 | if (rc == -2) |
214 | return GSASL_MALLOC_ERROR; |
215 | else if (rc != 0) |
216 | return GSASL_AUTHENTICATION_ERROR; |
217 | |
218 | *output_len = strlen (*output); |
219 | |
220 | /* Point p to client-first-message-bare. */ |
221 | p = strchr (*output, ','); |
222 | if (!p) |
223 | return GSASL_AUTHENTICATION_ERROR; |
224 | p++; |
225 | p = strchr (p, ','); |
226 | if (!p) |
227 | return GSASL_AUTHENTICATION_ERROR; |
228 | p++; |
229 | |
230 | /* Save "client-first-message-bare" for the next step. */ |
231 | state->cfmb = strdup (p); |
232 | if (!state->cfmb) |
233 | return GSASL_MALLOC_ERROR; |
234 | |
235 | /* Prepare B64("cbind-input") for the next step. */ |
236 | if (state->cf.cbflag == 'p') |
237 | { |
238 | size_t len = (p - *output) + state->cbtlsuniquelen; |
239 | char *cbind_input = malloc (len); |
240 | if (cbind_input == NULL) |
241 | return GSASL_MALLOC_ERROR; |
242 | memcpy (cbind_input, *output, p - *output); |
243 | memcpy (cbind_input + (p - *output), state->cbtlsunique, |
244 | state->cbtlsuniquelen); |
245 | rc = gsasl_base64_to (cbind_input, len, &state->cl.cbind, NULL); |
246 | free (cbind_input); |
247 | } |
248 | else |
249 | rc = gsasl_base64_to (*output, p - *output, &state->cl.cbind, NULL); |
250 | if (rc != 0) |
251 | return rc; |
252 | |
253 | /* We are done. */ |
254 | state->step++; |
255 | return GSASL_NEEDS_MORE; |
256 | break; |
257 | } |
258 | |
259 | case 1: |
260 | { |
261 | if (scram_parse_server_first (input, input_len, &state->sf) < 0) |
262 | return GSASL_MECHANISM_PARSE_ERROR; |
263 | |
264 | if (strlen (state->sf.nonce) < strlen (state->cf.client_nonce) || |
265 | memcmp (state->cf.client_nonce, state->sf.nonce, |
266 | strlen (state->cf.client_nonce)) != 0) |
267 | return GSASL_AUTHENTICATION_ERROR; |
268 | |
269 | state->cl.nonce = strdup (state->sf.nonce); |
270 | if (!state->cl.nonce) |
271 | return GSASL_MALLOC_ERROR; |
272 | |
273 | /* Save salt/iter as properties, so that client callback can |
274 | access them. */ |
275 | { |
276 | char *str = NULL; |
277 | int n; |
278 | n = asprintf (&str, "%lu" , (unsigned long) state->sf.iter); |
279 | if (n < 0 || str == NULL) |
280 | return GSASL_MALLOC_ERROR; |
281 | gsasl_property_set (sctx, GSASL_SCRAM_ITER, str); |
282 | free (str); |
283 | } |
284 | |
285 | gsasl_property_set (sctx, GSASL_SCRAM_SALT, state->sf.salt); |
286 | |
287 | /* Generate ClientProof. */ |
288 | { |
289 | char saltedpassword[20]; |
290 | char *clientkey; |
291 | char *storedkey; |
292 | char *clientsignature; |
293 | char clientproof[20]; |
294 | const char *p; |
295 | |
296 | /* Get SaltedPassword. */ |
297 | p = gsasl_property_get (sctx, GSASL_SCRAM_SALTED_PASSWORD); |
298 | if (p && strlen (p) == 40 && hex_p (p)) |
299 | sha1_hex_to_byte (saltedpassword, p); |
300 | else if ((p = gsasl_property_get (sctx, GSASL_PASSWORD)) != NULL) |
301 | { |
302 | Gc_rc err; |
303 | char *salt; |
304 | size_t saltlen; |
305 | char *preppasswd; |
306 | |
307 | rc = gsasl_saslprep (p, 0, &preppasswd, NULL); |
308 | if (rc != GSASL_OK) |
309 | return rc; |
310 | |
311 | rc = gsasl_base64_from (state->sf.salt, strlen (state->sf.salt), |
312 | &salt, &saltlen); |
313 | if (rc != 0) |
314 | { |
315 | gsasl_free (preppasswd); |
316 | return rc; |
317 | } |
318 | |
319 | /* SaltedPassword := Hi(password, salt) */ |
320 | err = gc_pbkdf2_sha1 (preppasswd, strlen (preppasswd), |
321 | salt, saltlen, |
322 | state->sf.iter, saltedpassword, 20); |
323 | gsasl_free (preppasswd); |
324 | gsasl_free (salt); |
325 | if (err != GC_OK) |
326 | return GSASL_MALLOC_ERROR; |
327 | } |
328 | else |
329 | return GSASL_NO_PASSWORD; |
330 | |
331 | /* Get client-final-message-without-proof. */ |
332 | { |
333 | char *cfmwp; |
334 | int n; |
335 | |
336 | state->cl.proof = strdup ("p" ); |
337 | rc = scram_print_client_final (&state->cl, &cfmwp); |
338 | if (rc != 0) |
339 | return GSASL_MALLOC_ERROR; |
340 | free (state->cl.proof); |
341 | |
342 | /* Compute AuthMessage */ |
343 | n = asprintf (&state->authmessage, "%s,%.*s,%.*s" , |
344 | state->cfmb, |
345 | (int) input_len, input, |
346 | (int) (strlen (cfmwp) - 4), cfmwp); |
347 | free (cfmwp); |
348 | if (n <= 0 || !state->authmessage) |
349 | return GSASL_MALLOC_ERROR; |
350 | } |
351 | |
352 | /* ClientKey := HMAC(SaltedPassword, "Client Key") */ |
353 | #define CLIENT_KEY "Client Key" |
354 | rc = gsasl_hmac_sha1 (saltedpassword, 20, |
355 | CLIENT_KEY, strlen (CLIENT_KEY), &clientkey); |
356 | if (rc != 0) |
357 | return rc; |
358 | |
359 | /* StoredKey := H(ClientKey) */ |
360 | rc = gsasl_sha1 (clientkey, 20, &storedkey); |
361 | if (rc != 0) |
362 | { |
363 | free (clientkey); |
364 | return rc; |
365 | } |
366 | |
367 | /* ClientSignature := HMAC(StoredKey, AuthMessage) */ |
368 | rc = gsasl_hmac_sha1 (storedkey, 20, |
369 | state->authmessage, |
370 | strlen (state->authmessage), |
371 | &clientsignature); |
372 | free (storedkey); |
373 | if (rc != 0) |
374 | { |
375 | free (clientkey); |
376 | return rc; |
377 | } |
378 | |
379 | /* ClientProof := ClientKey XOR ClientSignature */ |
380 | memcpy (clientproof, clientkey, 20); |
381 | memxor (clientproof, clientsignature, 20); |
382 | |
383 | free (clientkey); |
384 | free (clientsignature); |
385 | |
386 | rc = gsasl_base64_to (clientproof, 20, &state->cl.proof, NULL); |
387 | if (rc != 0) |
388 | return rc; |
389 | |
390 | /* Generate ServerSignature, for comparison in next step. */ |
391 | { |
392 | char *serverkey; |
393 | char *serversignature; |
394 | |
395 | /* ServerKey := HMAC(SaltedPassword, "Server Key") */ |
396 | #define SERVER_KEY "Server Key" |
397 | rc = gsasl_hmac_sha1 (saltedpassword, 20, |
398 | SERVER_KEY, strlen (SERVER_KEY), |
399 | &serverkey); |
400 | if (rc != 0) |
401 | return rc; |
402 | |
403 | /* ServerSignature := HMAC(ServerKey, AuthMessage) */ |
404 | rc = gsasl_hmac_sha1 (serverkey, 20, |
405 | state->authmessage, |
406 | strlen (state->authmessage), |
407 | &serversignature); |
408 | gsasl_free (serverkey); |
409 | if (rc != 0) |
410 | return rc; |
411 | |
412 | rc = gsasl_base64_to (serversignature, 20, |
413 | &state->serversignature, NULL); |
414 | gsasl_free (serversignature); |
415 | if (rc != 0) |
416 | return rc; |
417 | } |
418 | } |
419 | |
420 | rc = scram_print_client_final (&state->cl, output); |
421 | if (rc != 0) |
422 | return GSASL_MALLOC_ERROR; |
423 | |
424 | *output_len = strlen (*output); |
425 | |
426 | state->step++; |
427 | return GSASL_NEEDS_MORE; |
428 | break; |
429 | } |
430 | |
431 | case 2: |
432 | { |
433 | if (scram_parse_server_final (input, input_len, &state->sl) < 0) |
434 | return GSASL_MECHANISM_PARSE_ERROR; |
435 | |
436 | if (strcmp (state->sl.verifier, state->serversignature) != 0) |
437 | return GSASL_AUTHENTICATION_ERROR; |
438 | |
439 | state->step++; |
440 | return GSASL_OK; |
441 | break; |
442 | } |
443 | |
444 | default: |
445 | break; |
446 | } |
447 | |
448 | return res; |
449 | } |
450 | |
451 | void |
452 | _gsasl_scram_sha1_client_finish (Gsasl_session * sctx, void *mech_data) |
453 | { |
454 | struct scram_client_state *state = mech_data; |
455 | |
456 | if (!state) |
457 | return; |
458 | |
459 | free (state->cfmb); |
460 | free (state->serversignature); |
461 | free (state->authmessage); |
462 | free (state->cbtlsunique); |
463 | scram_free_client_first (&state->cf); |
464 | scram_free_server_first (&state->sf); |
465 | scram_free_client_final (&state->cl); |
466 | scram_free_server_final (&state->sl); |
467 | |
468 | free (state); |
469 | } |
470 | |