gsasl  2.2.1
scram/server.c
Go to the documentation of this file.
1 /* server.c --- SASL CRAM-MD5 server side functions.
2  * Copyright (C) 2009-2024 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 #include <config.h>
24 
25 /* Get specification. */
26 #include "scram.h"
27 
28 /* Get malloc, free, strtoul. */
29 #include <stdlib.h>
30 
31 /* Get ULONG_MAX. */
32 #include <limits.h>
33 
34 /* Get memcpy, strdup, strlen. */
35 #include <string.h>
36 
37 /* Get MAX. */
38 #include "minmax.h"
39 
40 #include "tokens.h"
41 #include "parser.h"
42 #include "printer.h"
43 #include "gc.h"
44 #include "memxor.h"
45 #include "tools.h"
46 #include "mechtools.h"
47 
48 #define DEFAULT_SALT_BYTES 12
49 #define SNONCE_ENTROPY_BYTES 18
50 
52 {
53  bool plus;
55  int step;
56  char *cbind;
57  char *gs2header; /* copy of client first gs2-header */
58  char *cfmb_str; /* copy of client first message bare */
59  char *sf_str; /* copy of server first message */
60  char *snonce;
61  char *clientproof;
64  char *authmessage;
65  char *cb;
66  size_t cblen;
67  struct scram_client_first cf;
68  struct scram_server_first sf;
69  struct scram_client_final cl;
70  struct scram_server_final sl;
71 };
72 
73 static int
74 scram_start (Gsasl_session *sctx _GL_UNUSED, void **mech_data,
75  bool plus, Gsasl_hash hash)
76 {
77  struct scram_server_state *state;
79  int rc;
80 
81  state = (struct scram_server_state *) calloc (sizeof (*state), 1);
82  if (state == NULL)
83  return GSASL_MALLOC_ERROR;
84 
85  state->plus = plus;
86  state->hash = hash;
87 
89  if (rc != GSASL_OK)
90  goto end;
91 
92  rc = gsasl_base64_to (buf, SNONCE_ENTROPY_BYTES, &state->snonce, NULL);
93  if (rc != GSASL_OK)
94  goto end;
95 
97  if (rc != GSASL_OK)
98  goto end;
99 
100  rc = gsasl_base64_to (buf, DEFAULT_SALT_BYTES, &state->sf.salt, NULL);
101  if (rc != GSASL_OK)
102  goto end;
103 
104  *mech_data = state;
105 
106  return GSASL_OK;
107 
108 end:
109  free (state->sf.salt);
110  free (state->snonce);
111  free (state);
112  return rc;
113 }
114 
115 #ifdef USE_SCRAM_SHA1
116 int
117 _gsasl_scram_sha1_server_start (Gsasl_session *sctx, void **mech_data)
118 {
119  return scram_start (sctx, mech_data, false, GSASL_HASH_SHA1);
120 }
121 
122 int
123 _gsasl_scram_sha1_plus_server_start (Gsasl_session *sctx, void **mech_data)
124 {
125  return scram_start (sctx, mech_data, true, GSASL_HASH_SHA1);
126 }
127 #endif
128 
129 #ifdef USE_SCRAM_SHA256
130 int
131 _gsasl_scram_sha256_server_start (Gsasl_session *sctx, void **mech_data)
132 {
133  return scram_start (sctx, mech_data, false, GSASL_HASH_SHA256);
134 }
135 
136 int
137 _gsasl_scram_sha256_plus_server_start (Gsasl_session *sctx, void **mech_data)
138 {
139  return scram_start (sctx, mech_data, true, GSASL_HASH_SHA256);
140 }
141 #endif
142 
143 static int
144 extract_serverkey (struct scram_server_state *state,
145  const char *b64, char *buf)
146 {
147  char *bin;
148  size_t binlen;
149  int rc;
150 
151  rc = gsasl_base64_from (b64, strlen (b64), &bin, &binlen);
152  if (rc != GSASL_OK)
153  return rc;
154 
155  if (binlen != gsasl_hash_length (state->hash))
156  {
157  free (bin);
159  }
160 
161  memcpy (buf, bin, binlen);
162 
163  free (bin);
164 
165  return GSASL_OK;
166 }
167 
168 int
170  void *mech_data,
171  const char *input,
172  size_t input_len, char **output, size_t *output_len)
173 {
174  struct scram_server_state *state = mech_data;
176  int rc;
177 
178  *output = NULL;
179  *output_len = 0;
180 
181  switch (state->step)
182  {
183  case 0:
184  {
185  if (input_len == 0)
186  return GSASL_NEEDS_MORE;
187 
188  if (scram_parse_client_first (input, input_len, &state->cf) < 0)
190 
191  if (state->plus)
192  {
193  const char *p;
194 
195  /* In PLUS server mode, we require use of channel bindings. */
196  if (state->cf.cbflag != 'p' || state->cf.cbname == NULL)
198 
199  if (strcmp (state->cf.cbname, "tls-exporter") == 0)
200  {
202  if (!p)
204  }
205  else if (strcmp (state->cf.cbname, "tls-unique") == 0)
206  {
208  if (!p)
209  return GSASL_NO_CB_TLS_UNIQUE;
210  }
211  else
213 
214  rc = gsasl_base64_from (p, strlen (p), &state->cb, &state->cblen);
215  if (rc != GSASL_OK)
216  return rc;
217  }
218  else if (state->cf.cbflag == 'y')
219  {
220  const char *p = gsasl_property_get (sctx, GSASL_CB_TLS_EXPORTER);
221  /* In non-PLUS mode we reject a client 'y' cbflag since we
222  support channel bindings UNLESS we actually don't have
223  any channel bindings (application told to libgsasl that
224  it doesn't want PLUS). */
225  if (!p)
227  if (p != NULL)
229  }
230 
231  /* Check that username doesn't fail SASLprep. */
232  {
233  char *tmp;
235  &tmp, NULL);
236  if (rc != GSASL_OK || *tmp == '\0')
238  gsasl_free (tmp);
239  }
240 
241  {
242  const char *p;
243 
244  /* Save "gs2-header" and "message-bare" for next step. */
245  p = memchr (input, ',', input_len);
246  if (!p)
248  p++;
249  p = memchr (p, ',', input_len - (p - input));
250  if (!p)
252  p++;
253 
254  state->gs2header = malloc (p - input + 1);
255  if (!state->gs2header)
256  return GSASL_MALLOC_ERROR;
257  memcpy (state->gs2header, input, p - input);
258  state->gs2header[p - input] = '\0';
259 
260  state->cfmb_str = malloc (input_len - (p - input) + 1);
261  if (!state->cfmb_str)
262  return GSASL_MALLOC_ERROR;
263  memcpy (state->cfmb_str, p, input_len - (p - input));
264  state->cfmb_str[input_len - (p - input)] = '\0';
265  }
266 
267  /* Create new nonce. */
268  {
269  size_t cnlen = strlen (state->cf.client_nonce);
270  size_t snlen = strlen (state->snonce);
271 
272  state->sf.nonce = malloc (cnlen + snlen + 1);
273  if (!state->sf.nonce)
274  return GSASL_MALLOC_ERROR;
275 
276  memcpy (state->sf.nonce, state->cf.client_nonce, cnlen);
277  memcpy (state->sf.nonce + cnlen, state->snonce, snlen);
278  state->sf.nonce[cnlen + snlen] = '\0';
279  }
280 
281  rc = gsasl_property_set (sctx, GSASL_AUTHID, state->cf.username);
282  if (rc != GSASL_OK)
283  return rc;
284  rc = gsasl_property_set (sctx, GSASL_AUTHZID, state->cf.authzid);
285  if (rc != GSASL_OK)
286  return rc;
287 
288  {
289  const char *p = gsasl_property_get (sctx, GSASL_SCRAM_ITER);
290 
291  if (p)
292  state->sf.iter = strtoul (p, NULL, 10);
293  if (!p || state->sf.iter == 0 || state->sf.iter == ULONG_MAX)
294  state->sf.iter = 4096;
295 
296  /* Save salt/iter as properties, so that client callback can
297  access them. */
298  {
299  char *str = NULL;
300  int n;
301  n = asprintf (&str, "%zu", state->sf.iter);
302  if (n < 0 || str == NULL)
303  return GSASL_MALLOC_ERROR;
304  rc = gsasl_property_set (sctx, GSASL_SCRAM_ITER, str);
305  free (str);
306  if (rc != GSASL_OK)
307  return rc;
308  }
309  }
310 
311  {
312  const char *p = gsasl_property_get (sctx, GSASL_SCRAM_SALT);
313  if (p)
314  {
315  free (state->sf.salt);
316  state->sf.salt = strdup (p);
317  }
318  else
319  {
320  rc =
321  gsasl_property_set (sctx, GSASL_SCRAM_SALT, state->sf.salt);
322  if (rc != GSASL_OK)
323  return rc;
324  }
325  }
326 
327  rc = scram_print_server_first (&state->sf, &state->sf_str);
328  if (rc != 0)
329  return GSASL_MALLOC_ERROR;
330 
331  *output = strdup (state->sf_str);
332  if (!*output)
333  return GSASL_MALLOC_ERROR;
334  *output_len = strlen (*output);
335 
336  state->step++;
337  return GSASL_NEEDS_MORE;
338  break;
339  }
340 
341  case 1:
342  {
343  if (scram_parse_client_final (input, input_len, &state->cl) < 0)
345 
346  if (strcmp (state->cl.nonce, state->sf.nonce) != 0)
348 
349  /* Base64 decode the c= field and check that it matches
350  client-first. Also check channel binding data. */
351  {
352  size_t len;
353 
354  free (state->cbind);
355  rc = gsasl_base64_from (state->cl.cbind, strlen (state->cl.cbind),
356  &state->cbind, &len);
357  if (rc != 0)
358  return rc;
359 
360  if (state->cf.cbflag == 'p')
361  {
362  if (len < strlen (state->gs2header))
364 
365  if (memcmp (state->cbind, state->gs2header,
366  strlen (state->gs2header)) != 0)
368 
369  if (len - strlen (state->gs2header) != state->cblen)
371 
372  if (memcmp (state->cbind + strlen (state->gs2header),
373  state->cb, state->cblen) != 0)
375  }
376  else
377  {
378  if (len != strlen (state->gs2header))
380 
381  if (memcmp (state->cbind, state->gs2header, len) != 0)
383  }
384  }
385 
386  /* Base64 decode client proof and check that length matches
387  hash size. */
388  {
389  size_t len;
390 
391  free (state->clientproof);
392  rc = gsasl_base64_from (state->cl.proof, strlen (state->cl.proof),
393  &state->clientproof, &len);
394  if (rc != 0)
395  return rc;
396  if (gsasl_hash_length (state->hash) != len)
398  }
399  {
400  const char *p, *q;
401 
402  /* Get StoredKey and ServerKey */
403  if ((p = gsasl_property_get (sctx, GSASL_SCRAM_SERVERKEY))
404  && (q = gsasl_property_get (sctx, GSASL_SCRAM_STOREDKEY)))
405  {
406  rc = extract_serverkey (state, p, state->serverkey);
407  if (rc != GSASL_OK)
408  return rc;
409  rc = extract_serverkey (state, q, state->storedkey);
410  if (rc != GSASL_OK)
411  return rc;
412  }
413  else if ((q = gsasl_property_get (sctx,
415  || (p = gsasl_property_get (sctx, GSASL_PASSWORD)))
416  {
417  char clientkey[GSASL_HASH_MAX_SIZE];
418  char *b64str;
419 
420  if (q)
421  {
422  char *binsaltedpassword;
423  size_t outlen;
424 
425  rc = gsasl_hex_from (q, &binsaltedpassword, &outlen);
426  if (rc != GSASL_OK)
427  return rc;
428 
429  if (outlen != gsasl_hash_length (state->hash))
430  {
431  gsasl_free (binsaltedpassword);
433  }
434 
436  (state->hash, binsaltedpassword,
437  clientkey, state->serverkey, state->storedkey);
438  gsasl_free (binsaltedpassword);
439  if (rc != GSASL_OK)
440  return rc;
441  }
442  else
443  {
444  char *salt;
445  size_t saltlen;
446  char saltedpassword[GSASL_HASH_MAX_SIZE];
447 
448  rc = gsasl_base64_from (state->sf.salt,
449  strlen (state->sf.salt),
450  &salt, &saltlen);
451  if (rc != GSASL_OK)
452  return rc;
453 
455  p,
456  state->sf.iter,
457  salt, saltlen,
458  saltedpassword,
459  clientkey,
460  state->serverkey,
461  state->storedkey);
462  gsasl_free (salt);
463  if (rc != GSASL_OK)
464  return rc;
465 
466  rc = set_saltedpassword (sctx, state->hash, saltedpassword);
467  if (rc != GSASL_OK)
468  return rc;
469  }
470 
471  rc = gsasl_base64_to (state->serverkey,
472  gsasl_hash_length (state->hash),
473  &b64str, NULL);
474  if (rc != GSASL_OK)
475  return rc;
476  rc = gsasl_property_set (sctx, GSASL_SCRAM_SERVERKEY, b64str);
477  free (b64str);
478  if (rc != GSASL_OK)
479  return rc;
480 
481  rc = gsasl_base64_to (state->storedkey,
482  gsasl_hash_length (state->hash),
483  &b64str, NULL);
484  if (rc != 0)
485  return rc;
486  rc = gsasl_property_set (sctx, GSASL_SCRAM_STOREDKEY, b64str);
487  free (b64str);
488  if (rc != GSASL_OK)
489  return rc;
490  }
491  else
492  return GSASL_NO_PASSWORD;
493 
494  /* Compute AuthMessage */
495  {
496  size_t len;
497  int n;
498 
499  /* Get client-final-message-without-proof. */
500  p = memmem (input, input_len, ",p=", 3);
501  if (!p)
503  len = p - input;
504 
505  n = asprintf (&state->authmessage, "%s,%.*s,%.*s",
506  state->cfmb_str,
507  (int) strlen (state->sf_str), state->sf_str,
508  (int) len, input);
509  if (n <= 0 || !state->authmessage)
510  return GSASL_MALLOC_ERROR;
511  }
512 
513  /* Check client proof. */
514  {
515  char clientsignature[GSASL_HASH_MAX_SIZE];
516  char maybe_storedkey[GSASL_HASH_MAX_SIZE];
517 
518  /* ClientSignature := HMAC(StoredKey, AuthMessage) */
519  rc = _gsasl_hmac (state->hash,
520  state->storedkey,
521  gsasl_hash_length (state->hash),
522  state->authmessage, strlen (state->authmessage),
523  clientsignature);
524  if (rc != 0)
525  return rc;
526 
527  /* ClientKey := ClientProof XOR ClientSignature */
528  memxor (clientsignature, state->clientproof,
529  gsasl_hash_length (state->hash));
530 
531  rc = _gsasl_hash (state->hash, clientsignature,
532  gsasl_hash_length (state->hash),
533  maybe_storedkey);
534  if (rc != 0)
535  return rc;
536 
537  rc = memcmp (state->storedkey, maybe_storedkey,
538  gsasl_hash_length (state->hash));
539  if (rc != 0)
541  }
542 
543  /* Generate server verifier. */
544  {
545  char serversignature[GSASL_HASH_MAX_SIZE];
546 
547  /* ServerSignature := HMAC(ServerKey, AuthMessage) */
548  rc = _gsasl_hmac (state->hash, state->serverkey,
549  gsasl_hash_length (state->hash),
550  state->authmessage,
551  strlen (state->authmessage), serversignature);
552  if (rc != 0)
553  return rc;
554 
555  rc = gsasl_base64_to (serversignature,
556  gsasl_hash_length (state->hash),
557  &state->sl.verifier, NULL);
558  if (rc != 0)
559  return rc;
560  }
561  }
562 
563  rc = scram_print_server_final (&state->sl, output);
564  if (rc != 0)
565  return GSASL_MALLOC_ERROR;
566  *output_len = strlen (*output);
567 
568  state->step++;
569  return GSASL_OK;
570  break;
571  }
572 
573  default:
574  break;
575  }
576 
577  return res;
578 }
579 
580 void
581 _gsasl_scram_server_finish (Gsasl_session *sctx _GL_UNUSED, void *mech_data)
582 {
583  struct scram_server_state *state = mech_data;
584 
585  if (!state)
586  return;
587 
588  free (state->cbind);
589  free (state->gs2header);
590  free (state->cfmb_str);
591  free (state->sf_str);
592  free (state->snonce);
593  free (state->clientproof);
594  free (state->authmessage);
595  free (state->cb);
596  scram_free_client_first (&state->cf);
597  scram_free_server_first (&state->sf);
598  scram_free_client_final (&state->cl);
599  scram_free_server_final (&state->sl);
600 
601  free (state);
602 }
int gsasl_base64_from(const char *in, size_t inlen, char **out, size_t *outlen)
Definition: base64.c:75
int gsasl_base64_to(const char *in, size_t inlen, char **out, size_t *outlen)
Definition: base64.c:45
int gsasl_hex_from(const char *in, char **out, size_t *outlen)
Definition: base64.c:144
size_t gsasl_hash_length(Gsasl_hash hash)
Definition: crypto.c:73
int gsasl_scram_secrets_from_salted_password(Gsasl_hash hash, const char *salted_password, char *client_key, char *server_key, char *stored_key)
Definition: crypto.c:104
int gsasl_nonce(char *data, size_t datalen)
Definition: crypto.c:39
int gsasl_scram_secrets_from_password(Gsasl_hash hash, const char *password, unsigned int iteration_count, const char *salt, size_t saltlen, char *salted_password, char *client_key, char *server_key, char *stored_key)
Definition: crypto.c:156
int rc
Definition: error.c:37
@ GSASL_ALLOW_UNASSIGNED
Definition: gsasl.h:332
Gsasl_hash
Definition: gsasl.h:428
@ GSASL_HASH_SHA1
Definition: gsasl.h:430
@ GSASL_HASH_SHA256
Definition: gsasl.h:431
@ GSASL_OK
Definition: gsasl.h:129
@ GSASL_NO_CB_TLS_EXPORTER
Definition: gsasl.h:155
@ GSASL_AUTHENTICATION_ERROR
Definition: gsasl.h:138
@ GSASL_NEEDS_MORE
Definition: gsasl.h:130
@ GSASL_MALLOC_ERROR
Definition: gsasl.h:133
@ GSASL_NO_PASSWORD
Definition: gsasl.h:146
@ GSASL_MECHANISM_CALLED_TOO_MANY_TIMES
Definition: gsasl.h:132
@ GSASL_MECHANISM_PARSE_ERROR
Definition: gsasl.h:137
@ GSASL_NO_CB_TLS_UNIQUE
Definition: gsasl.h:151
_GSASL_API int gsasl_property_set(Gsasl_session *sctx, Gsasl_property prop, const char *data)
Definition: property.c:189
_GSASL_API const char * gsasl_property_get(Gsasl_session *sctx, Gsasl_property prop)
Definition: property.c:292
@ GSASL_HASH_MAX_SIZE
Definition: gsasl.h:452
@ GSASL_SCRAM_STOREDKEY
Definition: gsasl.h:242
@ GSASL_AUTHZID
Definition: gsasl.h:225
@ GSASL_SCRAM_SALT
Definition: gsasl.h:239
@ GSASL_CB_TLS_UNIQUE
Definition: gsasl.h:243
@ GSASL_SCRAM_SALTED_PASSWORD
Definition: gsasl.h:240
@ GSASL_PASSWORD
Definition: gsasl.h:226
@ GSASL_SCRAM_ITER
Definition: gsasl.h:238
@ GSASL_AUTHID
Definition: gsasl.h:224
@ GSASL_SCRAM_SERVERKEY
Definition: gsasl.h:241
@ GSASL_CB_TLS_EXPORTER
Definition: gsasl.h:248
_GSASL_API int gsasl_saslprep(const char *in, Gsasl_saslprep_flags flags, char **out, int *stringpreprc)
int _gsasl_hmac(Gsasl_hash hash, const char *key, size_t keylen, const char *in, size_t inlen, char *outhash)
Definition: mechtools.c:329
int _gsasl_hash(Gsasl_hash hash, const char *in, size_t inlen, char *outhash)
Definition: mechtools.c:296
int scram_parse_client_final(const char *str, size_t len, struct scram_client_final *cl)
Definition: scram/parser.c:329
int scram_parse_client_first(const char *str, size_t len, struct scram_client_first *cf)
Definition: scram/parser.c:76
int scram_print_server_final(struct scram_server_final *sl, char **out)
int scram_print_server_first(struct scram_server_first *sf, char **out)
void _gsasl_scram_server_finish(Gsasl_session *sctx _GL_UNUSED, void *mech_data)
Definition: scram/server.c:581
#define DEFAULT_SALT_BYTES
Definition: scram/server.c:48
#define SNONCE_ENTROPY_BYTES
Definition: scram/server.c:49
int _gsasl_scram_server_step(Gsasl_session *sctx, void *mech_data, const char *input, size_t input_len, char **output, size_t *output_len)
Definition: scram/server.c:169
void gsasl_free(void *ptr)
Definition: src/free.c:41
char serverkey[GSASL_HASH_MAX_SIZE]
Definition: scram/server.c:63
char storedkey[GSASL_HASH_MAX_SIZE]
Definition: scram/server.c:62
struct scram_server_final sl
Definition: scram/server.c:70
struct scram_server_first sf
Definition: scram/server.c:68
struct scram_client_first cf
Definition: scram/server.c:67
struct scram_client_final cl
Definition: scram/server.c:69
void scram_free_server_first(struct scram_server_first *sf)
Definition: tokens.c:46
void scram_free_client_first(struct scram_client_first *cf)
Definition: tokens.c:35
void scram_free_server_final(struct scram_server_final *sl)
Definition: tokens.c:65
void scram_free_client_final(struct scram_client_final *cl)
Definition: tokens.c:55
int set_saltedpassword(Gsasl_session *sctx, Gsasl_hash hash, const char *hashbuf)
Definition: tools.c:31