gsasl  2.2.1
scram/client.c
Go to the documentation of this file.
1 /* client.c --- SASL SCRAM client 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. */
29 #include <stdlib.h>
30 
31 /* Get memcpy, strlen, strchr. */
32 #include <string.h>
33 
34 /* Get bool. */
35 #include <stdbool.h>
36 
37 #include "tokens.h"
38 #include "parser.h"
39 #include "printer.h"
40 #include "gc.h"
41 #include "memxor.h"
42 #include "tools.h"
43 #include "mechtools.h"
44 
45 #define CNONCE_ENTROPY_BYTES 18
46 
48 {
49  bool plus;
51  int step;
52  char *cfmb; /* client first message bare */
54  char *authmessage;
55  struct scram_client_first cf;
56  struct scram_server_first sf;
57  struct scram_client_final cl;
58  struct scram_server_final sl;
59 };
60 
61 static int
62 scram_start (Gsasl_session *sctx _GL_UNUSED,
63  void **mech_data, bool plus, Gsasl_hash hash)
64 {
65  struct scram_client_state *state;
66  char buf[CNONCE_ENTROPY_BYTES];
67  int rc;
68 
69  state = (struct scram_client_state *) calloc (sizeof (*state), 1);
70  if (state == NULL)
71  return GSASL_MALLOC_ERROR;
72 
73  state->plus = plus;
74  state->hash = hash;
75 
77  if (rc != GSASL_OK)
78  {
79  free (state);
80  return rc;
81  }
82 
84  &state->cf.client_nonce, NULL);
85  if (rc != GSASL_OK)
86  {
87  free (state);
88  return rc;
89  }
90 
91  *mech_data = state;
92 
93  return GSASL_OK;
94 }
95 
96 #ifdef USE_SCRAM_SHA1
97 int
98 _gsasl_scram_sha1_client_start (Gsasl_session *sctx, void **mech_data)
99 {
100  return scram_start (sctx, mech_data, false, GSASL_HASH_SHA1);
101 }
102 
103 int
104 _gsasl_scram_sha1_plus_client_start (Gsasl_session *sctx, void **mech_data)
105 {
106  return scram_start (sctx, mech_data, true, GSASL_HASH_SHA1);
107 }
108 #endif
109 
110 #ifdef USE_SCRAM_SHA256
111 int
112 _gsasl_scram_sha256_client_start (Gsasl_session *sctx, void **mech_data)
113 {
114  return scram_start (sctx, mech_data, false, GSASL_HASH_SHA256);
115 }
116 
117 int
118 _gsasl_scram_sha256_plus_client_start (Gsasl_session *sctx, void **mech_data)
119 {
120  return scram_start (sctx, mech_data, true, GSASL_HASH_SHA256);
121 }
122 #endif
123 
124 int
126  void *mech_data,
127  const char *input, size_t input_len,
128  char **output, size_t *output_len)
129 {
130  struct scram_client_state *state = mech_data;
132  int rc;
133 
134  *output = NULL;
135  *output_len = 0;
136 
137  switch (state->step)
138  {
139  case 0:
140  {
141  const char *p, *b64cb;
142 
143  p = gsasl_property_get (sctx, GSASL_AUTHID);
144  if (!p)
145  return GSASL_NO_AUTHID;
146 
147  free (state->cf.username);
149  &state->cf.username, NULL);
150  if (rc != GSASL_OK)
151  return rc;
152 
153  p = gsasl_property_get (sctx, GSASL_AUTHZID);
154  if (p)
155  state->cf.authzid = strdup (p);
156 
158  if (b64cb)
159  state->cf.cbname = strdup ("tls-exporter");
160  else
161  {
162  b64cb = gsasl_property_get (sctx, GSASL_CB_TLS_UNIQUE);
163  if (b64cb)
164  state->cf.cbname = strdup ("tls-unique");
165  }
166 
167  if (state->plus)
168  {
169  if (!b64cb)
171 
172  state->cf.cbflag = 'p';
173  }
174  else
175  {
176  if (b64cb)
177  state->cf.cbflag = 'y';
178  else
179  state->cf.cbflag = 'n';
180  }
181 
182  rc = scram_print_client_first (&state->cf, output);
183  if (rc == -2)
184  return GSASL_MALLOC_ERROR;
185  else if (rc != 0)
187 
188  *output_len = strlen (*output);
189 
190  /* Point p to client-first-message-bare. */
191  p = strchr (*output, ',');
192  if (!p)
194  p++;
195  p = strchr (p, ',');
196  if (!p)
198  p++;
199 
200  /* Save "client-first-message-bare" for the next step. */
201  state->cfmb = strdup (p);
202  if (!state->cfmb)
203  return GSASL_MALLOC_ERROR;
204 
205  /* Prepare B64("cbind-input") for the next step. */
206  if (state->plus && b64cb)
207  {
208  size_t len;
209  char *cbind_input;
210  char *cb;
211  size_t cblen;
212 
213  rc = gsasl_base64_from (b64cb, strlen (b64cb), &cb, &cblen);
214  if (rc != GSASL_OK)
215  return rc;
216 
217  len = (p - *output) + cblen;
218  cbind_input = malloc (len);
219  if (cbind_input == NULL)
220  return GSASL_MALLOC_ERROR;
221 
222  memcpy (cbind_input, *output, p - *output);
223  memcpy (cbind_input + (p - *output), cb, cblen);
224  free (cb);
225  rc = gsasl_base64_to (cbind_input, len, &state->cl.cbind, NULL);
226  free (cbind_input);
227  }
228  else
229  rc = gsasl_base64_to (*output, p - *output, &state->cl.cbind, NULL);
230  if (rc != GSASL_OK)
231  return rc;
232 
233  /* We are done. */
234  state->step++;
235  return GSASL_NEEDS_MORE;
236  break;
237  }
238 
239  case 1:
240  {
241  if (scram_parse_server_first (input, input_len, &state->sf) < 0)
243 
244  if (strlen (state->sf.nonce) < strlen (state->cf.client_nonce) ||
245  memcmp (state->cf.client_nonce, state->sf.nonce,
246  strlen (state->cf.client_nonce)) != 0)
248 
249  free (state->cl.nonce);
250  state->cl.nonce = strdup (state->sf.nonce);
251  if (!state->cl.nonce)
252  return GSASL_MALLOC_ERROR;
253 
254  /* Save salt/iter as properties, so that client callback can
255  access them. */
256  {
257  char *str = NULL;
258  int n;
259  n = asprintf (&str, "%zu", state->sf.iter);
260  if (n < 0 || str == NULL)
261  return GSASL_MALLOC_ERROR;
262  rc = gsasl_property_set (sctx, GSASL_SCRAM_ITER, str);
263  free (str);
264  if (rc != GSASL_OK)
265  return rc;
266  }
267 
268  rc = gsasl_property_set (sctx, GSASL_SCRAM_SALT, state->sf.salt);
269  if (rc != GSASL_OK)
270  return rc;
271 
272  /* Generate ClientProof. */
273  {
274  char saltedpassword[GSASL_HASH_MAX_SIZE];
275  char clientkey[GSASL_HASH_MAX_SIZE];
276  char serverkey[GSASL_HASH_MAX_SIZE];
277  char storedkey[GSASL_HASH_MAX_SIZE];
278  const char *p;
279 
280  /* Get SaltedPassword. */
281 
283  && (strlen (p) == 2 * gsasl_hash_length (state->hash))
284  && _gsasl_hex_p (p))
285  {
286  _gsasl_hex_decode (p, saltedpassword);
287 
289  saltedpassword,
290  clientkey,
291  serverkey,
292  storedkey);
293  if (rc != 0)
294  return rc;
295  }
296  else if ((p = gsasl_property_get (sctx, GSASL_PASSWORD)) != NULL)
297  {
298  char *salt;
299  size_t saltlen;
300 
301  rc = gsasl_base64_from (state->sf.salt, strlen (state->sf.salt),
302  &salt, &saltlen);
303  if (rc != 0)
304  return rc;
305 
307  p,
308  state->sf.iter,
309  salt, saltlen,
310  saltedpassword,
311  clientkey,
312  serverkey, storedkey);
313  if (rc != 0)
314  return rc;
315 
316  rc = set_saltedpassword (sctx, state->hash, saltedpassword);
317  if (rc != GSASL_OK)
318  return rc;
319 
320  gsasl_free (salt);
321  }
322  else
323  return GSASL_NO_PASSWORD;
324 
325  /* Get client-final-message-without-proof. */
326  {
327  char *cfmwp;
328  int n;
329 
330  state->cl.proof = strdup ("p");
331  rc = scram_print_client_final (&state->cl, &cfmwp);
332  if (rc != 0)
333  return GSASL_MALLOC_ERROR;
334  free (state->cl.proof);
335 
336  /* Compute AuthMessage */
337  n = asprintf (&state->authmessage, "%s,%.*s,%.*s",
338  state->cfmb,
339  (int) input_len, input,
340  (int) (strlen (cfmwp) - 4), cfmwp);
341  free (cfmwp);
342  if (n <= 0 || !state->authmessage)
343  return GSASL_MALLOC_ERROR;
344  }
345 
346  {
347  char clientsignature[GSASL_HASH_MAX_SIZE];
348  char clientproof[GSASL_HASH_MAX_SIZE];
349 
350  /* ClientSignature := HMAC(StoredKey, AuthMessage) */
351  rc = _gsasl_hmac (state->hash,
352  storedkey,
353  gsasl_hash_length (state->hash),
354  state->authmessage,
355  strlen (state->authmessage), clientsignature);
356  if (rc != 0)
357  return rc;
358 
359  /* ClientProof := ClientKey XOR ClientSignature */
360  memcpy (clientproof, clientkey, gsasl_hash_length (state->hash));
361  memxor (clientproof, clientsignature,
362  gsasl_hash_length (state->hash));
363 
364  rc =
365  gsasl_base64_to (clientproof, gsasl_hash_length (state->hash),
366  &state->cl.proof, NULL);
367  if (rc != 0)
368  return rc;
369  }
370 
371  /* Generate ServerSignature, for comparison in next step. */
372  {
374 
375  /* ServerSignature := HMAC(ServerKey, AuthMessage) */
376  rc = _gsasl_hmac (state->hash,
377  serverkey, gsasl_hash_length (state->hash),
378  state->authmessage,
379  strlen (state->authmessage), serversignature);
380  if (rc != 0)
381  return rc;
382 
384  gsasl_hash_length (state->hash),
385  &state->serversignature, NULL);
386  if (rc != 0)
387  return rc;
388  }
389  }
390 
391  rc = scram_print_client_final (&state->cl, output);
392  if (rc != 0)
393  return GSASL_MALLOC_ERROR;
394 
395  *output_len = strlen (*output);
396 
397  state->step++;
398  return GSASL_NEEDS_MORE;
399  break;
400  }
401 
402  case 2:
403  {
404  if (scram_parse_server_final (input, input_len, &state->sl) < 0)
406 
407  if (strcmp (state->sl.verifier, state->serversignature) != 0)
409 
410  state->step++;
411  return GSASL_OK;
412  break;
413  }
414 
415  default:
416  break;
417  }
418 
419  return res;
420 }
421 
422 void
423 _gsasl_scram_client_finish (Gsasl_session *sctx _GL_UNUSED, void *mech_data)
424 {
425  struct scram_client_state *state = mech_data;
426 
427  if (!state)
428  return;
429 
430  free (state->cfmb);
431  free (state->serversignature);
432  free (state->authmessage);
433  scram_free_client_first (&state->cf);
434  scram_free_server_first (&state->sf);
435  scram_free_client_final (&state->cl);
436  scram_free_server_final (&state->sl);
437 
438  free (state);
439 }
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
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_NO_AUTHID
Definition: gsasl.h:144
@ GSASL_MECHANISM_PARSE_ERROR
Definition: gsasl.h:137
_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_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_CB_TLS_EXPORTER
Definition: gsasl.h:248
_GSASL_API int gsasl_saslprep(const char *in, Gsasl_saslprep_flags flags, char **out, int *stringpreprc)
void _gsasl_hex_decode(const char *hexstr, char *bin)
Definition: mechtools.c:256
int _gsasl_hmac(Gsasl_hash hash, const char *key, size_t keylen, const char *in, size_t inlen, char *outhash)
Definition: mechtools.c:329
bool _gsasl_hex_p(const char *hexstr)
Definition: mechtools.c:268
#define CNONCE_ENTROPY_BYTES
Definition: scram/client.c:45
int _gsasl_scram_client_step(Gsasl_session *sctx, void *mech_data, const char *input, size_t input_len, char **output, size_t *output_len)
Definition: scram/client.c:125
void _gsasl_scram_client_finish(Gsasl_session *sctx _GL_UNUSED, void *mech_data)
Definition: scram/client.c:423
int scram_parse_server_final(const char *str, size_t len, struct scram_server_final *sl)
Definition: scram/parser.c:459
int scram_parse_server_first(const char *str, size_t len, struct scram_server_first *sf)
Definition: scram/parser.c:218
int scram_print_client_first(struct scram_client_first *cf, char **out)
Definition: scram/printer.c:77
int scram_print_client_final(struct scram_client_final *cl, char **out)
void gsasl_free(void *ptr)
Definition: src/free.c:41
struct scram_client_first cf
Definition: scram/client.c:55
struct scram_server_final sl
Definition: scram/client.c:58
struct scram_client_final cl
Definition: scram/client.c:57
struct scram_server_first sf
Definition: scram/client.c:56
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