Coverage Report

Created: 2026-03-11 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-auth/auth-scram-server.c
Line
Count
Source
1
/*
2
 * SCRAM-SHA-1 SASL authentication, see RFC-5802
3
 *
4
 * Copyright (c) 2011-2016 Florian Zeitz <florob@babelmonkeys.de>
5
 * Copyright (c) 2022-2023 Dovecot Oy
6
 *
7
 * This software is released under the MIT license.
8
 */
9
10
#include "lib.h"
11
#include "base64.h"
12
#include "buffer.h"
13
#include "hmac.h"
14
#include "randgen.h"
15
#include "safe-memset.h"
16
#include "str.h"
17
#include "strfuncs.h"
18
#include "strnum.h"
19
20
#include "auth-gs2.h"
21
#include "auth-scram-server.h"
22
23
/* s-nonce length */
24
#define SCRAM_SERVER_NONCE_LEN 64
25
26
static bool
27
auth_scram_server_set_username(struct auth_scram_server *server,
28
             const char *username)
29
1.44k
{
30
1.44k
  return server->backend->set_username(server, username);
31
1.44k
}
32
static bool
33
auth_scram_server_set_login_username(struct auth_scram_server *server,
34
             const char *username)
35
666
{
36
666
  return server->backend->set_login_username(server, username);
37
666
}
38
39
static void
40
auth_scram_server_start_channel_binding(struct auth_scram_server *server,
41
          const char *type)
42
526
{
43
526
  i_assert(server->backend->start_channel_binding != NULL);
44
526
  server->backend->start_channel_binding(server, type);
45
526
}
46
47
static int
48
auth_scram_server_accept_channel_binding(struct auth_scram_server *server,
49
           buffer_t **data_r)
50
383
{
51
383
  i_assert(server->backend->accept_channel_binding != NULL);
52
383
  return server->backend->accept_channel_binding(server, data_r);
53
383
}
54
55
static int
56
auth_scram_server_credentials_lookup(struct auth_scram_server *server)
57
1.39k
{
58
1.39k
  const struct hash_method *hmethod = server->set.hash_method;
59
1.39k
  struct auth_scram_key_data *kdata = &server->key_data;
60
1.39k
  pool_t pool = server->pool;
61
62
1.39k
  i_zero(kdata);
63
1.39k
  kdata->pool = pool;
64
1.39k
  kdata->hmethod = hmethod;
65
1.39k
  kdata->stored_key = p_malloc(pool, hmethod->digest_size);
66
1.39k
  kdata->server_key = p_malloc(pool, hmethod->digest_size);
67
68
1.39k
  i_assert(server->backend->credentials_lookup != NULL);
69
1.39k
  return server->backend->credentials_lookup(server, kdata);
70
1.39k
}
71
72
void auth_scram_server_init(struct auth_scram_server *server_r, pool_t pool,
73
          const struct auth_scram_server_settings *set,
74
          const struct auth_scram_server_backend *backend)
75
1.68k
{
76
1.68k
  pool_ref(pool);
77
78
1.68k
  i_assert(set->hash_method != NULL);
79
80
1.68k
  i_zero(server_r);
81
1.68k
  server_r->pool = pool;
82
1.68k
  server_r->set = *set;
83
1.68k
  server_r->backend = backend;
84
1.68k
}
85
86
void auth_scram_server_deinit(struct auth_scram_server *server)
87
1.68k
{
88
1.68k
  i_assert(server->set.hash_method != NULL);
89
1.68k
  if (server->proof != NULL)
90
764
    buffer_clear_safe(server->proof);
91
1.68k
  auth_scram_key_data_clear(&server->key_data);
92
1.68k
  pool_unref(&server->pool);
93
1.68k
}
94
95
static int
96
auth_scram_parse_client_first(struct auth_scram_server *server,
97
            const unsigned char *data, size_t size,
98
            const char **username_r,
99
            const char **login_username_r,
100
            const char **error_r)
101
1.56k
{
102
1.56k
  struct auth_gs2_header gs2_header;
103
1.56k
  const unsigned char *gs2_header_end;
104
1.56k
  const char *cfm_bare, *login_username = NULL, *username, *nonce;
105
1.56k
  const char *const *fields;
106
1.56k
  const char *error;
107
108
  /* RFC 5802, Section 7:
109
110
     client-first-message = gs2-header client-first-message-bare
111
     gs2-header      = gs2-cbind-flag "," [ authzid ] ","
112
     gs2-cbind-flag  = ("p=" cb-name) / "n" / "y"
113
114
     client-first-message-bare = [reserved-mext ","]
115
                       username "," nonce ["," extensions]
116
     reserved-mext   = "m=" 1*(value-char)
117
118
     username        = "n=" saslname
119
     nonce           = "r=" c-nonce [s-nonce]
120
121
     extensions      = attr-val *("," attr-val)
122
                       ;; All extensions are optional,
123
                       ;; i.e., unrecognized attributes
124
                       ;; not defined in this document
125
                       ;; MUST be ignored.
126
     attr-val        = ALPHA "=" value
127
   */
128
129
1.56k
  if (auth_gs2_header_decode(data, size, FALSE,
130
1.56k
           &gs2_header, &gs2_header_end, &error) < 0) {
131
69
    *error_r = t_strdup_printf("Invalid initial client message: %s",
132
69
             error);
133
69
    return -1;
134
69
  }
135
136
1.50k
  size_t gs2_header_size = gs2_header_end - data;
137
1.50k
  size_t cfm_bare_size = size - gs2_header_size;
138
139
1.50k
  cfm_bare = t_strndup(gs2_header_end, cfm_bare_size);
140
1.50k
  fields = t_strsplit(cfm_bare, ",");
141
1.50k
  if (str_array_length(fields) < 2) {
142
2
    *error_r = "Invalid initial client message: "
143
2
      "Missing nonce field";
144
2
    return -1;
145
2
  }
146
1.49k
  username = fields[0];
147
1.49k
  nonce = fields[1];
148
149
  /* gs2-cbind-flag  = ("p=" cb-name) / "n" / "y"
150
   */
151
1.49k
  enum auth_scram_cbind_server_support cbind_support =
152
1.49k
    server->set.cbind_support;
153
154
1.49k
  switch (gs2_header.cbind.status) {
155
526
  case AUTH_GS2_CBIND_STATUS_PROVIDED:
156
526
    if (cbind_support == AUTH_SCRAM_CBIND_SERVER_SUPPORT_NONE) {
157
0
      *error_r = "Channel binding not supported";
158
0
      return -1;
159
0
    }
160
526
    auth_scram_server_start_channel_binding(
161
526
      server, gs2_header.cbind.name);
162
526
    break;
163
1
  case AUTH_GS2_CBIND_STATUS_NO_SERVER_SUPPORT:
164
    /* RFC 5802, Section 6:
165
166
       If the flag is set to "y" and the server supports channel
167
       binding, the server MUST fail authentication. This is because
168
       if the client sets the channel binding flag to "y", then the
169
       client must have believed that the server did not support
170
       channel binding -- if the server did in fact support channel
171
       binding, then this is an indication that there has been a
172
       downgrade attack (e.g., an attacker changed the server's
173
       mechanism list to exclude the -PLUS suffixed SCRAM mechanism
174
       name(s)).
175
    */
176
1
    if (cbind_support != AUTH_SCRAM_CBIND_SERVER_SUPPORT_NONE) {
177
1
      *error_r = "Potential downgrade attack detected";
178
1
      return -1;
179
1
    }
180
0
    break;
181
971
  case AUTH_GS2_CBIND_STATUS_NO_CLIENT_SUPPORT:
182
971
    if (cbind_support == AUTH_SCRAM_CBIND_SERVER_SUPPORT_REQUIRED) {
183
0
      *error_r = "Channel binding required";
184
0
      return -1;
185
0
    }
186
971
    break;
187
971
  default:
188
0
    *error_r = "Invalid GS2 header";
189
0
    return -1;
190
1.49k
  }
191
192
  /* authzid         = "a=" saslname
193
                       ;; Protocol specific.
194
   */
195
1.49k
  if (gs2_header.authzid != NULL)
196
690
    login_username = gs2_header.authzid;
197
198
  /* reserved-mext   = "m=" 1*(value-char)
199
   */
200
1.49k
  if (username[0] == 'm') {
201
1
    *error_r = "Mandatory extension(s) not supported";
202
1
    return -1;
203
1
  }
204
  /* username        = "n=" saslname
205
   */
206
1.49k
  if (username[0] == 'n' && username[1] == '=') {
207
1.48k
    const char *uname_enc = username + 2;
208
1.48k
    size_t uname_enc_size = strlen(uname_enc);
209
210
    /* Unescape username */
211
1.48k
    if (auth_gs2_decode_username((const unsigned char *)uname_enc,
212
1.48k
               uname_enc_size, &username) < 0) {
213
18
      *error_r = "Username escaping is invalid";
214
18
      return -1;
215
18
    }
216
1.48k
  } else {
217
12
    *error_r = "Invalid username field";
218
12
    return -1;
219
12
  }
220
221
  /* nonce           = "r=" c-nonce [s-nonce] */
222
1.46k
  if (nonce[0] == 'r' && nonce[1] == '=')
223
1.44k
    server->cnonce = p_strdup(server->pool, nonce+2);
224
19
  else {
225
19
    *error_r = "Invalid client nonce";
226
19
    return -1;
227
19
  }
228
229
1.44k
  *username_r = username;
230
1.44k
  *login_username_r = login_username;
231
232
1.44k
  server->gs2_header = p_strndup(server->pool, data, gs2_header_size);
233
1.44k
  server->client_first_message_bare = p_strdup(server->pool, cfm_bare);
234
1.44k
  return 0;
235
1.46k
}
236
237
static string_t *
238
auth_scram_get_server_first(struct auth_scram_server *server)
239
1.39k
{
240
1.39k
  const struct hash_method *hmethod = server->set.hash_method;
241
1.39k
  struct auth_scram_key_data *kdata = &server->key_data;
242
1.39k
  unsigned char snonce[SCRAM_SERVER_NONCE_LEN+1];
243
1.39k
  string_t *str;
244
1.39k
  size_t i;
245
246
  /* RFC 5802, Section 7:
247
248
     server-first-message =
249
                       [reserved-mext ","] nonce "," salt ","
250
                       iteration-count ["," extensions]
251
252
     nonce           = "r=" c-nonce [s-nonce]
253
254
     salt            = "s=" base64
255
256
     iteration-count = "i=" posit-number
257
                       ;; A positive number.
258
   */
259
260
1.39k
  i_assert(kdata->pool == server->pool);
261
1.39k
  i_assert(kdata->hmethod == hmethod);
262
1.39k
  i_assert(kdata->salt != NULL);
263
1.39k
  i_assert(kdata->iter_count != 0);
264
265
1.39k
  random_fill(snonce, sizeof(snonce)-1);
266
267
  /* Make sure snonce is printable and does not contain ',' */
268
90.4k
  for (i = 0; i < sizeof(snonce)-1; i++) {
269
89.0k
    snonce[i] = (snonce[i] % ('~' - '!')) + '!';
270
89.0k
    if (snonce[i] == ',')
271
0
      snonce[i] = '~';
272
89.0k
  }
273
1.39k
  snonce[sizeof(snonce)-1] = '\0';
274
1.39k
  server->snonce = p_strndup(server->pool, snonce, sizeof(snonce));
275
276
1.39k
  str = t_str_new(32 + strlen(server->cnonce) + sizeof(snonce) +
277
1.39k
      strlen(kdata->salt));
278
1.39k
  str_printfa(str, "r=%s%s,s=%s,i=%d", server->cnonce, server->snonce,
279
1.39k
        kdata->salt, kdata->iter_count);
280
281
1.39k
  server->server_first_message = p_strdup(server->pool, str_c(str));
282
283
1.39k
  return str;
284
1.39k
}
285
286
static bool
287
auth_scram_server_verify_credentials(struct auth_scram_server *server)
288
716
{
289
716
  const struct hash_method *hmethod = server->set.hash_method;
290
716
  struct auth_scram_key_data *kdata = &server->key_data;
291
716
  struct hmac_context ctx;
292
716
  const char *auth_message;
293
716
  unsigned char client_key[hmethod->digest_size];
294
716
  unsigned char client_signature[hmethod->digest_size];
295
716
  unsigned char stored_key[hmethod->digest_size];
296
716
  size_t i;
297
298
716
  i_assert(kdata->pool == server->pool);
299
716
  i_assert(kdata->hmethod == hmethod);
300
301
  /* RFC 5802, Section 3:
302
303
     AuthMessage     := client-first-message-bare + "," +
304
                        server-first-message + "," +
305
                        client-final-message-without-proof
306
     ClientSignature := HMAC(StoredKey, AuthMessage)
307
   */
308
716
  auth_message = t_strconcat(server->client_first_message_bare, ",",
309
716
      server->server_first_message, ",",
310
716
      server->client_final_message_without_proof, NULL);
311
312
716
  hmac_init(&ctx, kdata->stored_key, hmethod->digest_size, hmethod);
313
716
  hmac_update(&ctx, auth_message, strlen(auth_message));
314
716
  hmac_final(&ctx, client_signature);
315
316
  /* ClientProof     := ClientKey XOR ClientSignature */
317
716
  const unsigned char *proof_data = server->proof->data;
318
17.7k
  for (i = 0; i < sizeof(client_signature); i++)
319
16.9k
    client_key[i] = proof_data[i] ^ client_signature[i];
320
716
  buffer_clear_safe(server->proof);
321
322
  /* StoredKey       := H(ClientKey) */
323
716
  hash_method_get_digest(hmethod, client_key, sizeof(client_key),
324
716
             stored_key);
325
326
716
  safe_memset(client_key, 0, sizeof(client_key));
327
716
  safe_memset(client_signature, 0, sizeof(client_signature));
328
329
716
  return mem_equals_timing_safe(stored_key, kdata->stored_key,
330
716
              sizeof(stored_key));
331
716
}
332
333
static int
334
auth_scram_parse_client_final(struct auth_scram_server *server,
335
            const unsigned char *data, size_t size,
336
            const char **error_r)
337
903
{
338
903
  const struct hash_method *hmethod = server->set.hash_method;
339
903
  const char **fields, *nonce_str;
340
903
  const void *cbind_input;
341
903
  size_t cbind_input_size;
342
903
  unsigned int field_count;
343
903
  string_t *str;
344
345
  /* RFC 5802, Section 7:
346
347
     client-final-message-without-proof =
348
                       channel-binding "," nonce [","
349
                       extensions]
350
     client-final-message =
351
                       client-final-message-without-proof "," proof
352
   */
353
903
  fields = t_strsplit(t_strndup(data, size), ",");
354
903
  field_count = str_array_length(fields);
355
903
  if (field_count < 3) {
356
17
    *error_r = "Invalid final client message";
357
17
    return -1;
358
17
  }
359
360
  /* channel-binding = "c=" base64
361
                       ;; base64 encoding of cbind-input.
362
363
     cbind-data      = 1*OCTET
364
     cbind-input     = gs2-header [ cbind-data ]
365
                       ;; cbind-data MUST be present for
366
                       ;; gs2-cbind-flag of "p" and MUST be absent
367
                       ;; for "y" or "n".
368
   */
369
886
  if (server->gs2_header[0] != 'p') {
370
503
    cbind_input = server->gs2_header;
371
503
    cbind_input_size = strlen(server->gs2_header);
372
503
  } else {
373
383
    buffer_t *cbind_data;
374
375
383
    if (auth_scram_server_accept_channel_binding(server,
376
383
                   &cbind_data) < 0) {
377
0
      *error_r = "Channel binding failed";
378
0
      return -1;
379
0
    }
380
381
383
    size_t gs2_header_len = strlen(server->gs2_header);
382
383
    buffer_t *cbind_buf;
383
384
383
    cbind_buf = t_buffer_create(gs2_header_len + cbind_data->used);
385
383
    buffer_append(cbind_buf, server->gs2_header, gs2_header_len);
386
383
    buffer_append_buf(cbind_buf, cbind_data, 0, SIZE_MAX);
387
383
    cbind_input = cbind_buf->data;
388
383
    cbind_input_size = cbind_buf->used;
389
383
  }
390
886
  str = t_str_new(2 + MAX_BASE64_ENCODED_SIZE(cbind_input_size));
391
886
  str_append(str, "c=");
392
886
  base64_encode(cbind_input, cbind_input_size, str);
393
394
886
  if (!str_equals_timing_almost_safe(fields[0], str_c(str))) {
395
101
    *error_r = "Invalid channel binding data";
396
101
    return -1;
397
101
  }
398
399
  /* nonce           = "r=" c-nonce [s-nonce]
400
                       ;; Second part provided by server.
401
     c-nonce         = printable
402
     s-nonce         = printable
403
   */
404
785
  nonce_str = t_strconcat("r=", server->cnonce, server->snonce, NULL);
405
785
  if (!str_equals_timing_almost_safe(fields[1], nonce_str)) {
406
11
    *error_r = "Wrong nonce";
407
11
    return -1;
408
11
  }
409
410
  /* proof           = "p=" base64
411
   */
412
774
  if (fields[field_count-1][0] == 'p') {
413
764
    size_t len = strlen(&fields[field_count-1][2]);
414
415
764
    server->proof = buffer_create_dynamic(server->pool,
416
764
          MAX_BASE64_DECODED_SIZE(len));
417
764
    if (base64_decode(&fields[field_count-1][2], len,
418
764
          server->proof) < 0) {
419
41
      *error_r = "Invalid base64 encoding";
420
41
      return -1;
421
41
    }
422
723
    if (server->proof->used != hmethod->digest_size) {
423
7
      *error_r = "Invalid ClientProof length";
424
7
      return -1;
425
7
    }
426
723
  } else {
427
10
    *error_r = "Invalid ClientProof";
428
10
    return -1;
429
10
  }
430
431
716
  (void)str_array_remove(fields, fields[field_count-1]);
432
716
  server->client_final_message_without_proof =
433
716
    p_strdup(server->pool, t_strarray_join(fields, ","));
434
435
716
  return 0;
436
774
}
437
438
static string_t *
439
auth_scram_get_server_final(struct auth_scram_server *server)
440
373
{
441
373
  const struct hash_method *hmethod = server->set.hash_method;
442
373
  struct auth_scram_key_data *kdata = &server->key_data;
443
373
  struct hmac_context ctx;
444
373
  const char *auth_message;
445
373
  unsigned char server_signature[hmethod->digest_size];
446
373
  string_t *str;
447
448
  /* RFC 5802, Section 3:
449
450
     AuthMessage     := client-first-message-bare + "," +
451
                        server-first-message + "," +
452
                        client-final-message-without-proof
453
     ServerSignature := HMAC(ServerKey, AuthMessage)
454
   */
455
373
  auth_message = t_strconcat(server->client_first_message_bare, ",",
456
373
      server->server_first_message, ",",
457
373
      server->client_final_message_without_proof, NULL);
458
459
373
  hmac_init(&ctx, kdata->server_key, hmethod->digest_size, hmethod);
460
373
  hmac_update(&ctx, auth_message, strlen(auth_message));
461
373
  hmac_final(&ctx, server_signature);
462
463
  /* RFC 5802, Section 7:
464
465
     server-final-message = (server-error / verifier)
466
                       ["," extensions]
467
468
     verifier        = "v=" base64
469
                       ;; base-64 encoded ServerSignature.
470
471
   */
472
373
  str = t_str_new(2 + MAX_BASE64_ENCODED_SIZE(sizeof(server_signature)));
473
373
  str_append(str, "v=");
474
373
  base64_encode(server_signature, sizeof(server_signature), str);
475
476
373
  return str;
477
373
}
478
479
static int
480
auth_scram_parse_client_finish(struct auth_scram_server *server ATTR_UNUSED,
481
             const unsigned char *data ATTR_UNUSED,
482
             size_t size, const char **error_r)
483
0
{
484
0
  if (size != 0) {
485
0
    *error_r = "Spurious extra client message";
486
0
    return -1;
487
0
  }
488
0
  return 0;
489
0
}
490
491
bool auth_scram_server_acces_granted(struct auth_scram_server *server)
492
0
{
493
0
  return (server->state == AUTH_SCRAM_SERVER_STATE_SERVER_FINAL);
494
0
}
495
496
static int
497
auth_scram_server_input_client_first(struct auth_scram_server *server,
498
             const unsigned char *input,
499
             size_t input_len,
500
             enum auth_scram_server_error *error_code_r,
501
             const char **error_r)
502
1.56k
{
503
1.56k
  const char *username, *login_username;
504
1.56k
  int ret;
505
506
1.56k
  username = login_username = NULL;
507
  
508
  /* Parse client-first message */
509
1.56k
  ret = auth_scram_parse_client_first(server, input, input_len,
510
1.56k
              &username, &login_username,
511
1.56k
              error_r);
512
1.56k
  if (ret < 0) {
513
122
    *error_code_r = AUTH_SCRAM_SERVER_ERROR_PROTOCOL_VIOLATION;
514
122
    return -1;
515
122
  }
516
517
  /* Pass usernames to backend */
518
1.56k
  i_assert(username != NULL);
519
1.44k
  if (!auth_scram_server_set_username(server, username)) {
520
26
    *error_r = "Bad username";
521
26
    *error_code_r = AUTH_SCRAM_SERVER_ERROR_BAD_USERNAME;
522
26
    return -1;
523
26
  }
524
1.42k
  if (login_username != NULL &&
525
666
      !auth_scram_server_set_login_username(server, login_username)) {
526
26
    *error_r = "Bad login username";
527
26
    *error_code_r = AUTH_SCRAM_SERVER_ERROR_BAD_LOGIN_USERNAME;
528
26
    return -1;
529
26
  }
530
  
531
1.39k
  return 0;
532
1.42k
}
533
534
static int
535
auth_scram_server_input_client_final(struct auth_scram_server *server,
536
             const unsigned char *input,
537
             size_t input_len,
538
             enum auth_scram_server_error *error_code_r,
539
             const char **error_r)
540
903
{
541
903
  int ret;
542
  
543
  /* Parse client-final message */
544
903
  ret = auth_scram_parse_client_final(server, input, input_len, error_r);
545
903
  if (ret < 0) {
546
187
    *error_code_r = AUTH_SCRAM_SERVER_ERROR_PROTOCOL_VIOLATION;
547
187
    return -1;
548
187
  }
549
550
  /* Verify client credentials */
551
716
  if (!auth_scram_server_verify_credentials(server)) {
552
343
    *error_code_r = AUTH_SCRAM_SERVER_ERROR_VERIFICATION_FAILED;
553
343
    *error_r = "Password mismatch";
554
343
    return -1;
555
343
  }
556
557
373
  return 0;
558
716
}
559
560
int auth_scram_server_input(struct auth_scram_server *server,
561
          const unsigned char *input, size_t input_len,
562
          enum auth_scram_server_error *error_code_r,
563
          const char **error_r)
564
2.47k
{
565
2.47k
  struct auth_scram_key_data *kdata = &server->key_data;
566
2.47k
  int ret = 0;
567
568
2.47k
  *error_code_r = AUTH_SCRAM_SERVER_ERROR_NONE;
569
2.47k
  *error_r = NULL;
570
571
2.47k
  switch (server->state) {
572
1.56k
  case AUTH_SCRAM_SERVER_STATE_INIT:
573
1.56k
    server->state = AUTH_SCRAM_SERVER_STATE_CLIENT_FIRST;
574
    /* Fall through */
575
1.56k
  case AUTH_SCRAM_SERVER_STATE_CLIENT_FIRST:
576
    /* Handle client-first message */
577
1.56k
    ret = auth_scram_server_input_client_first(
578
1.56k
      server, input, input_len, error_code_r, error_r);
579
1.56k
    if (ret < 0) {
580
174
      server->state = AUTH_SCRAM_SERVER_STATE_ERROR;
581
174
      ret = -1;
582
174
      break;
583
174
    }
584
585
    /* Initiate credentials lookup */
586
1.39k
    server->state = AUTH_SCRAM_SERVER_STATE_CREDENTIALS_LOOKUP;
587
1.39k
    if (auth_scram_server_credentials_lookup(server) < 0) {
588
0
      *error_code_r = AUTH_SCRAM_SERVER_ERROR_LOOKUP_FAILED;
589
0
      *error_r = "Credentials lookup failed";
590
0
      server->state = AUTH_SCRAM_SERVER_STATE_ERROR;
591
0
      ret = -1;
592
0
      break;
593
0
    }
594
1.39k
    if (server->state ==
595
1.39k
        AUTH_SCRAM_SERVER_STATE_CREDENTIALS_LOOKUP) {
596
3
      server->state = AUTH_SCRAM_SERVER_STATE_SERVER_FIRST;
597
3
      ret = (kdata->salt != NULL ? 1 : 0);
598
3
      break;
599
3
    }
600
1.39k
    i_assert(server->state >= AUTH_SCRAM_SERVER_STATE_SERVER_FIRST);
601
1.39k
    ret = 0;
602
1.39k
    break;
603
0
  case AUTH_SCRAM_SERVER_STATE_CREDENTIALS_LOOKUP:
604
0
  case AUTH_SCRAM_SERVER_STATE_SERVER_FIRST:
605
0
    i_unreached();
606
903
  case AUTH_SCRAM_SERVER_STATE_CLIENT_FINAL:
607
    /* Handle client-final message */
608
903
    ret = auth_scram_server_input_client_final(
609
903
      server, input, input_len, error_code_r, error_r);
610
903
    if (ret < 0) {
611
530
      server->state = AUTH_SCRAM_SERVER_STATE_ERROR;
612
530
      break;
613
530
    }
614
373
    server->state = AUTH_SCRAM_SERVER_STATE_SERVER_FINAL;
615
373
    ret = 1;
616
373
    break;
617
0
  case AUTH_SCRAM_SERVER_STATE_SERVER_FINAL:
618
0
    i_unreached();
619
0
  case AUTH_SCRAM_SERVER_STATE_CLIENT_FINISH:
620
0
    server->state = AUTH_SCRAM_SERVER_STATE_END;
621
0
    ret = auth_scram_parse_client_finish(server, input, input_len,
622
0
                 error_r);
623
0
    if (ret < 0) {
624
0
      *error_code_r =
625
0
        AUTH_SCRAM_SERVER_ERROR_PROTOCOL_VIOLATION;
626
0
      server->state = AUTH_SCRAM_SERVER_STATE_ERROR;
627
0
    }
628
0
    break;
629
0
  case AUTH_SCRAM_SERVER_STATE_END:
630
0
  case AUTH_SCRAM_SERVER_STATE_ERROR:
631
0
    i_unreached();
632
2.47k
  }
633
634
2.47k
  return ret;
635
2.47k
}
636
637
bool auth_scram_server_output(struct auth_scram_server *server,
638
            const unsigned char **output_r,
639
            size_t *output_len_r)
640
1.76k
{
641
1.76k
  struct auth_scram_key_data *kdata = &server->key_data;
642
1.76k
  string_t *output;
643
1.76k
  bool result = FALSE;
644
645
1.76k
  switch (server->state) {
646
0
  case AUTH_SCRAM_SERVER_STATE_INIT:
647
0
    *output_r = uchar_empty_ptr;
648
0
    *output_len_r = 0;
649
0
    server->state = AUTH_SCRAM_SERVER_STATE_CLIENT_FIRST;
650
0
    break;
651
0
  case AUTH_SCRAM_SERVER_STATE_CLIENT_FIRST:
652
0
    i_unreached();
653
1.39k
  case AUTH_SCRAM_SERVER_STATE_CREDENTIALS_LOOKUP:
654
1.39k
    i_assert(kdata->salt != NULL);
655
1.39k
    server->state = AUTH_SCRAM_SERVER_STATE_SERVER_FIRST;
656
    /* Fall through */
657
1.39k
  case AUTH_SCRAM_SERVER_STATE_SERVER_FIRST:
658
    /* Compose server-first message */
659
1.39k
    output = auth_scram_get_server_first(server);
660
1.39k
    *output_r = str_data(output);
661
1.39k
    *output_len_r = str_len(output);
662
1.39k
    server->state = AUTH_SCRAM_SERVER_STATE_CLIENT_FINAL;
663
1.39k
    break;
664
0
  case AUTH_SCRAM_SERVER_STATE_CLIENT_FINAL:
665
0
    i_unreached();
666
373
  case AUTH_SCRAM_SERVER_STATE_SERVER_FINAL:
667
    /* Compose server-final message */
668
373
    output = auth_scram_get_server_final(server);
669
373
    *output_r = str_data(output);
670
373
    *output_len_r = str_len(output);
671
373
    server->state = AUTH_SCRAM_SERVER_STATE_CLIENT_FINISH;
672
373
    result = TRUE;
673
373
    break;
674
0
  case AUTH_SCRAM_SERVER_STATE_CLIENT_FINISH:
675
0
  case AUTH_SCRAM_SERVER_STATE_END:
676
0
  case AUTH_SCRAM_SERVER_STATE_ERROR:
677
0
    i_unreached();
678
1.76k
  }
679
680
1.76k
  return result;
681
1.76k
}