Coverage Report

Created: 2026-08-13 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-sasl/dsasl-client-mech-cram-md5.c
Line
Count
Source
1
/* Copyright (c) Dovecot authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "str.h"
5
#include "hex-binary.h"
6
#include "hmac.h"
7
#include "md5.h"
8
9
#include "dsasl-client-private.h"
10
11
struct cram_md5_dsasl_client {
12
  struct dsasl_client client;
13
14
  const char *challenge;
15
};
16
17
static enum dsasl_client_result
18
mech_cram_md5_input(struct dsasl_client *client,
19
        const unsigned char *input, size_t input_len,
20
        const char **error_r)
21
257
{
22
257
  struct cram_md5_dsasl_client *cclient =
23
257
    container_of(client, struct cram_md5_dsasl_client, client);
24
25
257
  const unsigned char *p = input, *pend = input + input_len;
26
27
257
  if (p >= pend) {
28
4
    *error_r = "Server sent empty challenge";
29
4
    return DSASL_CLIENT_RESULT_ERR_PROTOCOL;
30
4
  }
31
253
  if (*p != '<') {
32
11
    *error_r = "Server sent invalid challenge begin";
33
11
    return DSASL_CLIENT_RESULT_ERR_PROTOCOL;
34
11
  }
35
242
  p++;
36
37
26.1k
  for (; (p + 1) < pend; p++) {
38
25.9k
    if (*p <= 32 || *p == 127 || *p == '>') {
39
9
      *error_r = "Server sent invalid challenge";
40
9
      return DSASL_CLIENT_RESULT_ERR_PROTOCOL;
41
9
    }
42
25.9k
  }
43
44
233
  if (p >= pend || *p != '>') {
45
12
    *error_r = "Server sent invalid challenge end";
46
12
    return DSASL_CLIENT_RESULT_ERR_PROTOCOL;
47
12
  }
48
221
  p++;
49
221
  i_assert(p == pend);
50
51
221
  if (input_len < 5) {
52
0
    *error_r = "Server sent invalid challenge";
53
0
    return DSASL_CLIENT_RESULT_ERR_PROTOCOL;
54
0
  }
55
56
221
  cclient->challenge = p_strndup(client->pool, input, input_len);
57
221
  return DSASL_CLIENT_RESULT_OK;
58
221
}
59
60
static enum dsasl_client_result
61
mech_cram_md5_output(struct dsasl_client *client,
62
         const unsigned char **output_r, size_t *output_len_r,
63
         const char **error_r)
64
235
{
65
235
  struct cram_md5_dsasl_client *cclient =
66
235
    container_of(client, struct cram_md5_dsasl_client, client);
67
235
  string_t *str;
68
69
235
  if (client->set.authid == NULL) {
70
0
    *error_r = "authid not set";
71
0
    return DSASL_CLIENT_RESULT_ERR_INTERNAL;
72
0
  }
73
235
  if (client->password == NULL) {
74
0
    *error_r = "password not set";
75
0
    return DSASL_CLIENT_RESULT_ERR_INTERNAL;
76
0
  }
77
78
235
  if (cclient->challenge == NULL) {
79
14
    *output_r = uchar_empty_ptr;
80
14
    *output_len_r = 0;
81
14
    return DSASL_CLIENT_RESULT_OK;
82
14
  }
83
84
221
  struct hmac_context ctx;
85
221
  unsigned char digest[MD5_RESULTLEN];
86
87
221
  hmac_init(&ctx, (const unsigned char *)client->password,
88
221
      strlen(client->password), &hash_method_md5);
89
221
  hmac_update(&ctx, cclient->challenge, strlen(cclient->challenge));
90
221
  hmac_final(&ctx, digest);
91
92
221
  str = str_new(client->pool, 256);
93
221
  str_append(str, client->set.authid);
94
221
  str_append_c(str, ' ');
95
221
  binary_to_hex_append(str, digest, sizeof(digest));
96
97
221
  *output_r = str_data(str);
98
221
  *output_len_r = str_len(str);
99
221
  return DSASL_CLIENT_RESULT_OK;
100
235
}
101
102
const struct dsasl_client_mech dsasl_client_mech_cram_md5 = {
103
  .name = SASL_MECH_NAME_CRAM_MD5,
104
  .struct_size = sizeof(struct cram_md5_dsasl_client),
105
106
  .input = mech_cram_md5_input,
107
  .output = mech_cram_md5_output,
108
};