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
47struct 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
62static int
63scram_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
115int
116_gsasl_scram_sha1_client_start (Gsasl_session * sctx, void **mech_data)
117{
118 return scram_start (sctx, mech_data, 0);
119}
120
121int
122_gsasl_scram_sha1_plus_client_start (Gsasl_session * sctx, void **mech_data)
123{
124 return scram_start (sctx, mech_data, 1);
125}
126
127static char
128hexdigit_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
137static char
138hex_to_char (char u, char l)
139{
140 return (char) (((unsigned char) hexdigit_to_char (u)) * 16
141 + hexdigit_to_char (l));
142}
143
144static void
145sha1_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
155static bool
156hex_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
167int
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
451void
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