1 | /* server.c --- SASL CRAM-MD5 server 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, strtoul. */ |
31 | #include <stdlib.h> |
32 | |
33 | /* Get ULONG_MAX. */ |
34 | #include <limits.h> |
35 | |
36 | /* Get memcpy, strdup, strlen. */ |
37 | #include <string.h> |
38 | |
39 | /* Get MAX. */ |
40 | #include "minmax.h" |
41 | |
42 | #include "tokens.h" |
43 | #include "parser.h" |
44 | #include "printer.h" |
45 | #include "gc.h" |
46 | #include "memxor.h" |
47 | |
48 | #define DEFAULT_SALT_BYTES 12 |
49 | #define SNONCE_ENTROPY_BYTES 18 |
50 | |
51 | struct scram_server_state |
52 | { |
53 | int plus; |
54 | int step; |
55 | char *cbind; |
56 | char *; /* copy of client first gs2-header */ |
57 | char *cfmb_str; /* copy of client first message bare */ |
58 | char *sf_str; /* copy of server first message */ |
59 | char *snonce; |
60 | char *clientproof; |
61 | char *storedkey; |
62 | char *serverkey; |
63 | char *authmessage; |
64 | char *cbtlsunique; |
65 | size_t cbtlsuniquelen; |
66 | struct scram_client_first cf; |
67 | struct scram_server_first sf; |
68 | struct scram_client_final cl; |
69 | struct scram_server_final sl; |
70 | }; |
71 | |
72 | static int |
73 | scram_start (Gsasl_session * sctx, void **mech_data, int plus) |
74 | { |
75 | struct scram_server_state *state; |
76 | char buf[MAX (SNONCE_ENTROPY_BYTES, DEFAULT_SALT_BYTES)]; |
77 | const char *p; |
78 | int rc; |
79 | |
80 | state = (struct scram_server_state *) calloc (sizeof (*state), 1); |
81 | if (state == NULL) |
82 | return GSASL_MALLOC_ERROR; |
83 | |
84 | state->plus = plus; |
85 | |
86 | rc = gsasl_nonce (buf, SNONCE_ENTROPY_BYTES); |
87 | if (rc != GSASL_OK) |
88 | goto end; |
89 | |
90 | rc = gsasl_base64_to (buf, SNONCE_ENTROPY_BYTES, &state->snonce, NULL); |
91 | if (rc != GSASL_OK) |
92 | goto end; |
93 | |
94 | rc = gsasl_nonce (buf, DEFAULT_SALT_BYTES); |
95 | if (rc != GSASL_OK) |
96 | goto end; |
97 | |
98 | rc = gsasl_base64_to (buf, DEFAULT_SALT_BYTES, &state->sf.salt, NULL); |
99 | if (rc != GSASL_OK) |
100 | goto end; |
101 | |
102 | p = gsasl_property_get (sctx, GSASL_CB_TLS_UNIQUE); |
103 | if (plus && !p) |
104 | { |
105 | rc = GSASL_NO_CB_TLS_UNIQUE; |
106 | goto end; |
107 | } |
108 | if (p) |
109 | { |
110 | rc = gsasl_base64_from (p, strlen (p), &state->cbtlsunique, |
111 | &state->cbtlsuniquelen); |
112 | if (rc != GSASL_OK) |
113 | goto end; |
114 | } |
115 | |
116 | *mech_data = state; |
117 | |
118 | return GSASL_OK; |
119 | |
120 | end: |
121 | free (state->sf.salt); |
122 | free (state->snonce); |
123 | free (state); |
124 | return rc; |
125 | } |
126 | |
127 | int |
128 | _gsasl_scram_sha1_server_start (Gsasl_session * sctx, void **mech_data) |
129 | { |
130 | return scram_start (sctx, mech_data, 0); |
131 | } |
132 | |
133 | int |
134 | _gsasl_scram_sha1_plus_server_start (Gsasl_session * sctx, void **mech_data) |
135 | { |
136 | return scram_start (sctx, mech_data, 1); |
137 | } |
138 | |
139 | int |
140 | _gsasl_scram_sha1_server_step (Gsasl_session * sctx, |
141 | void *mech_data, |
142 | const char *input, |
143 | size_t input_len, |
144 | char **output, size_t * output_len) |
145 | { |
146 | struct scram_server_state *state = mech_data; |
147 | int res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES; |
148 | int rc; |
149 | |
150 | *output = NULL; |
151 | *output_len = 0; |
152 | |
153 | switch (state->step) |
154 | { |
155 | case 0: |
156 | { |
157 | if (input_len == 0) |
158 | return GSASL_NEEDS_MORE; |
159 | |
160 | if (scram_parse_client_first (input, input_len, &state->cf) < 0) |
161 | return GSASL_MECHANISM_PARSE_ERROR; |
162 | |
163 | /* In PLUS server mode, we require use of channel bindings. */ |
164 | if (state->plus && state->cf.cbflag != 'p') |
165 | return GSASL_AUTHENTICATION_ERROR; |
166 | |
167 | /* In non-PLUS mode, but where have channel bindings data (and |
168 | thus advertised PLUS) we reject a client 'y' cbflag. */ |
169 | if (!state->plus |
170 | && state->cbtlsuniquelen > 0 && state->cf.cbflag == 'y') |
171 | return GSASL_AUTHENTICATION_ERROR; |
172 | |
173 | /* Check that username doesn't fail SASLprep. */ |
174 | { |
175 | char *tmp; |
176 | rc = gsasl_saslprep (state->cf.username, GSASL_ALLOW_UNASSIGNED, |
177 | &tmp, NULL); |
178 | if (rc != GSASL_OK || *tmp == '\0') |
179 | return GSASL_AUTHENTICATION_ERROR; |
180 | gsasl_free (tmp); |
181 | } |
182 | |
183 | { |
184 | const char *p; |
185 | |
186 | /* Save "gs2-header" and "message-bare" for next step. */ |
187 | p = memchr (input, ',', input_len); |
188 | if (!p) |
189 | return GSASL_AUTHENTICATION_ERROR; |
190 | p++; |
191 | p = memchr (p, ',', input_len - (p - input)); |
192 | if (!p) |
193 | return GSASL_AUTHENTICATION_ERROR; |
194 | p++; |
195 | |
196 | state->gs2header = malloc (p - input + 1); |
197 | if (!state->gs2header) |
198 | return GSASL_MALLOC_ERROR; |
199 | memcpy (state->gs2header, input, p - input); |
200 | state->gs2header[p - input] = '\0'; |
201 | |
202 | state->cfmb_str = malloc (input_len - (p - input) + 1); |
203 | if (!state->cfmb_str) |
204 | return GSASL_MALLOC_ERROR; |
205 | memcpy (state->cfmb_str, p, input_len - (p - input)); |
206 | state->cfmb_str[input_len - (p - input)] = '\0'; |
207 | } |
208 | |
209 | /* Create new nonce. */ |
210 | { |
211 | size_t cnlen = strlen (state->cf.client_nonce); |
212 | |
213 | state->sf.nonce = malloc (cnlen + SNONCE_ENTROPY_BYTES + 1); |
214 | if (!state->sf.nonce) |
215 | return GSASL_MALLOC_ERROR; |
216 | |
217 | memcpy (state->sf.nonce, state->cf.client_nonce, cnlen); |
218 | memcpy (state->sf.nonce + cnlen, state->snonce, |
219 | SNONCE_ENTROPY_BYTES); |
220 | state->sf.nonce[cnlen + SNONCE_ENTROPY_BYTES] = '\0'; |
221 | } |
222 | |
223 | gsasl_property_set (sctx, GSASL_AUTHID, state->cf.username); |
224 | gsasl_property_set (sctx, GSASL_AUTHZID, state->cf.authzid); |
225 | |
226 | { |
227 | const char *p = gsasl_property_get (sctx, GSASL_SCRAM_ITER); |
228 | if (p) |
229 | state->sf.iter = strtoul (p, NULL, 10); |
230 | if (!p || state->sf.iter == 0 || state->sf.iter == ULONG_MAX) |
231 | state->sf.iter = 4096; |
232 | } |
233 | |
234 | { |
235 | const char *p = gsasl_property_get (sctx, GSASL_SCRAM_SALT); |
236 | if (p) |
237 | { |
238 | free (state->sf.salt); |
239 | state->sf.salt = strdup (p); |
240 | } |
241 | } |
242 | |
243 | rc = scram_print_server_first (&state->sf, &state->sf_str); |
244 | if (rc != 0) |
245 | return GSASL_MALLOC_ERROR; |
246 | |
247 | *output = strdup (state->sf_str); |
248 | if (!*output) |
249 | return GSASL_MALLOC_ERROR; |
250 | *output_len = strlen (*output); |
251 | |
252 | state->step++; |
253 | return GSASL_NEEDS_MORE; |
254 | break; |
255 | } |
256 | |
257 | case 1: |
258 | { |
259 | if (scram_parse_client_final (input, input_len, &state->cl) < 0) |
260 | return GSASL_MECHANISM_PARSE_ERROR; |
261 | |
262 | if (strcmp (state->cl.nonce, state->sf.nonce) != 0) |
263 | return GSASL_AUTHENTICATION_ERROR; |
264 | |
265 | /* Base64 decode the c= field and check that it matches |
266 | client-first. Also check channel binding data. */ |
267 | { |
268 | size_t len; |
269 | |
270 | rc = gsasl_base64_from (state->cl.cbind, strlen (state->cl.cbind), |
271 | &state->cbind, &len); |
272 | if (rc != 0) |
273 | return rc; |
274 | |
275 | if (state->cf.cbflag == 'p') |
276 | { |
277 | if (len < strlen (state->gs2header)) |
278 | return GSASL_AUTHENTICATION_ERROR; |
279 | |
280 | if (memcmp (state->cbind, state->gs2header, |
281 | strlen (state->gs2header)) != 0) |
282 | return GSASL_AUTHENTICATION_ERROR; |
283 | |
284 | if (len - strlen (state->gs2header) != state->cbtlsuniquelen) |
285 | return GSASL_AUTHENTICATION_ERROR; |
286 | |
287 | if (memcmp (state->cbind + strlen (state->gs2header), |
288 | state->cbtlsunique, state->cbtlsuniquelen) != 0) |
289 | return GSASL_AUTHENTICATION_ERROR; |
290 | } |
291 | else |
292 | { |
293 | if (len != strlen (state->gs2header)) |
294 | return GSASL_AUTHENTICATION_ERROR; |
295 | |
296 | if (memcmp (state->cbind, state->gs2header, len) != 0) |
297 | return GSASL_AUTHENTICATION_ERROR; |
298 | } |
299 | } |
300 | |
301 | /* Base64 decode client proof and check that length matches |
302 | SHA-1 size. */ |
303 | { |
304 | size_t len; |
305 | |
306 | rc = gsasl_base64_from (state->cl.proof, strlen (state->cl.proof), |
307 | &state->clientproof, &len); |
308 | if (rc != 0) |
309 | return rc; |
310 | if (len != 20) |
311 | return GSASL_MECHANISM_PARSE_ERROR; |
312 | } |
313 | |
314 | { |
315 | const char *p; |
316 | |
317 | /* Get StoredKey and ServerKey */ |
318 | if ((p = gsasl_property_get (sctx, GSASL_PASSWORD))) |
319 | { |
320 | Gc_rc err; |
321 | char *salt; |
322 | size_t saltlen; |
323 | char saltedpassword[20]; |
324 | char *clientkey; |
325 | char *preppasswd; |
326 | |
327 | rc = gsasl_saslprep (p, 0, &preppasswd, NULL); |
328 | if (rc != GSASL_OK) |
329 | return rc; |
330 | |
331 | rc = gsasl_base64_from (state->sf.salt, strlen (state->sf.salt), |
332 | &salt, &saltlen); |
333 | if (rc != 0) |
334 | { |
335 | gsasl_free (preppasswd); |
336 | return rc; |
337 | } |
338 | |
339 | /* SaltedPassword := Hi(password, salt) */ |
340 | err = gc_pbkdf2_sha1 (preppasswd, strlen (preppasswd), |
341 | salt, saltlen, |
342 | state->sf.iter, saltedpassword, 20); |
343 | gsasl_free (preppasswd); |
344 | gsasl_free (salt); |
345 | if (err != GC_OK) |
346 | return GSASL_MALLOC_ERROR; |
347 | |
348 | /* ClientKey := HMAC(SaltedPassword, "Client Key") */ |
349 | #define CLIENT_KEY "Client Key" |
350 | rc = gsasl_hmac_sha1 (saltedpassword, 20, |
351 | CLIENT_KEY, strlen (CLIENT_KEY), |
352 | &clientkey); |
353 | if (rc != 0) |
354 | return rc; |
355 | |
356 | /* StoredKey := H(ClientKey) */ |
357 | rc = gsasl_sha1 (clientkey, 20, &state->storedkey); |
358 | free (clientkey); |
359 | if (rc != 0) |
360 | return rc; |
361 | |
362 | /* ServerKey := HMAC(SaltedPassword, "Server Key") */ |
363 | #define SERVER_KEY "Server Key" |
364 | rc = gsasl_hmac_sha1 (saltedpassword, 20, |
365 | SERVER_KEY, strlen (SERVER_KEY), |
366 | &state->serverkey); |
367 | if (rc != 0) |
368 | return rc; |
369 | } |
370 | else |
371 | return GSASL_NO_PASSWORD; |
372 | |
373 | /* Compute AuthMessage */ |
374 | { |
375 | size_t len; |
376 | int n; |
377 | |
378 | /* Get client-final-message-without-proof. */ |
379 | p = memmem (input, input_len, ",p=" , 3); |
380 | if (!p) |
381 | return GSASL_MECHANISM_PARSE_ERROR; |
382 | len = p - input; |
383 | |
384 | n = asprintf (&state->authmessage, "%s,%.*s,%.*s" , |
385 | state->cfmb_str, |
386 | (int) strlen (state->sf_str), state->sf_str, |
387 | (int) len, input); |
388 | if (n <= 0 || !state->authmessage) |
389 | return GSASL_MALLOC_ERROR; |
390 | } |
391 | |
392 | /* Check client proof. */ |
393 | { |
394 | char *clientsignature; |
395 | char *maybe_storedkey; |
396 | |
397 | /* ClientSignature := HMAC(StoredKey, AuthMessage) */ |
398 | rc = gsasl_hmac_sha1 (state->storedkey, 20, |
399 | state->authmessage, |
400 | strlen (state->authmessage), |
401 | &clientsignature); |
402 | if (rc != 0) |
403 | return rc; |
404 | |
405 | /* ClientKey := ClientProof XOR ClientSignature */ |
406 | memxor (clientsignature, state->clientproof, 20); |
407 | |
408 | rc = gsasl_sha1 (clientsignature, 20, &maybe_storedkey); |
409 | free (clientsignature); |
410 | if (rc != 0) |
411 | return rc; |
412 | |
413 | rc = memcmp (state->storedkey, maybe_storedkey, 20); |
414 | free (maybe_storedkey); |
415 | if (rc != 0) |
416 | return GSASL_AUTHENTICATION_ERROR; |
417 | } |
418 | |
419 | /* Generate server verifier. */ |
420 | { |
421 | char *serversignature; |
422 | |
423 | /* ServerSignature := HMAC(ServerKey, AuthMessage) */ |
424 | rc = gsasl_hmac_sha1 (state->serverkey, 20, |
425 | state->authmessage, |
426 | strlen (state->authmessage), |
427 | &serversignature); |
428 | if (rc != 0) |
429 | return rc; |
430 | |
431 | rc = gsasl_base64_to (serversignature, 20, |
432 | &state->sl.verifier, NULL); |
433 | free (serversignature); |
434 | if (rc != 0) |
435 | return rc; |
436 | } |
437 | } |
438 | |
439 | rc = scram_print_server_final (&state->sl, output); |
440 | if (rc != 0) |
441 | return GSASL_MALLOC_ERROR; |
442 | *output_len = strlen (*output); |
443 | |
444 | state->step++; |
445 | return GSASL_OK; |
446 | break; |
447 | } |
448 | |
449 | default: |
450 | break; |
451 | } |
452 | |
453 | return res; |
454 | } |
455 | |
456 | void |
457 | _gsasl_scram_sha1_server_finish (Gsasl_session * sctx, void *mech_data) |
458 | { |
459 | struct scram_server_state *state = mech_data; |
460 | |
461 | if (!state) |
462 | return; |
463 | |
464 | free (state->cbind); |
465 | free (state->gs2header); |
466 | free (state->cfmb_str); |
467 | free (state->sf_str); |
468 | free (state->snonce); |
469 | free (state->clientproof); |
470 | free (state->storedkey); |
471 | free (state->serverkey); |
472 | free (state->authmessage); |
473 | free (state->cbtlsunique); |
474 | scram_free_client_first (&state->cf); |
475 | scram_free_server_first (&state->sf); |
476 | scram_free_client_final (&state->cl); |
477 | scram_free_server_final (&state->sl); |
478 | |
479 | free (state); |
480 | } |
481 | |