/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 | | }; |