42
42
buf[i] = (buf[i] % 10) + '0';
43
43
buf[sizeof(buf)-1] = '\0';
45
return t_strdup_printf("<%s.%s@%s>", buf, dec2str(ioloop_time),
45
return t_strdup_printf("<%s.%s@%s>", (const char *)buf,
46
dec2str(ioloop_time), my_hostname);
49
static int verify_credentials(struct cram_auth_request *auth,
49
static int verify_credentials(struct cram_auth_request *request,
50
50
const char *credentials)
53
unsigned char digest[16], context_digest[32], *cdp;
54
struct md5_context ctxo, ctxi;
53
unsigned char digest[16], context_digest[32];
54
struct hmac_md5_context ctx;
55
55
buffer_t *context_digest_buf;
56
56
const char *response_hex;
58
if (credentials == NULL)
61
58
context_digest_buf =
62
buffer_create_data(data_stack_pool,
59
buffer_create_data(pool_datastack_create(),
63
60
context_digest, sizeof(context_digest));
65
if (hex_to_binary(credentials, context_digest_buf) <= 0)
62
if (hex_to_binary(credentials, context_digest_buf) < 0) {
63
auth_request_log_error(&request->auth_request, "cram-md5",
64
"passdb credentials are not in hex");
68
#define CDGET(p, c) STMT_START { \
71
(c) += (*p++ << 16); \
72
(c) += (*p++ << 24); \
85
ctxo.lo = ctxi.lo = 64;
86
ctxo.hi = ctxi.hi = 0;
88
md5_update(&ctxi, auth->challenge, strlen(auth->challenge));
89
md5_final(&ctxi, digest);
90
md5_update(&ctxo, digest, 16);
91
md5_final(&ctxo, digest);
68
hmac_md5_set_cram_context(&ctx, context_digest);
69
hmac_md5_update(&ctx, request->challenge, strlen(request->challenge));
70
hmac_md5_final(&ctx, digest);
92
72
response_hex = binary_to_hex(digest, 16);
94
if (memcmp(response_hex, auth->response, 32) != 0) {
96
i_info("cram-md5(%s): password mismatch",
74
if (memcmp(response_hex, request->response, 32) != 0) {
75
auth_request_log_info(&request->auth_request, "cram-md5",
105
static int parse_cram_response(struct cram_auth_request *auth,
83
static int parse_cram_response(struct cram_auth_request *request,
106
84
const unsigned char *data, size_t size,
107
85
const char **error_r)
113
for (i = 0; i < size; i++) {
91
/* <username> SPACE <response>. Username may contain spaces, so assume
92
the rightmost space is the response separator. */
93
for (i = space = 0; i < size; i++) {
114
94
if (data[i] == ' ')
119
99
*error_r = "missing digest";
123
auth->username = p_strndup(auth->pool, data, i);
125
auth->response = p_strndup(auth->pool, data + i, size - i);
103
request->username = p_strndup(request->pool, data, space);
106
p_strndup(request->pool, data + space, size - space);
129
static void credentials_callback(const char *result,
130
struct auth_request *request)
110
static void credentials_callback(enum passdb_result result,
111
const char *credentials,
112
struct auth_request *auth_request)
132
struct cram_auth_request *auth =
133
(struct cram_auth_request *) request;
114
struct cram_auth_request *request =
115
(struct cram_auth_request *)auth_request;
135
if (verify_credentials(auth, result)) {
137
i_info("cram-md5(%s): authenticated",
138
auth->username == NULL ? "" : auth->username);
140
mech_auth_finish(request, NULL, 0, TRUE);
143
i_info("cram-md5(%s): authentication failed",
144
auth->username == NULL ? "" : auth->username);
146
mech_auth_finish(request, NULL, 0, FALSE);
118
case PASSDB_RESULT_OK:
119
if (verify_credentials(request, credentials))
120
auth_request_success(auth_request, NULL, 0);
122
auth_request_fail(auth_request);
124
case PASSDB_RESULT_INTERNAL_FAILURE:
125
auth_request_internal_failure(auth_request);
128
auth_request_fail(auth_request);
151
134
mech_cram_md5_auth_continue(struct auth_request *auth_request,
152
struct auth_login_request_continue *request __attr_unused__,
153
const unsigned char *data,
154
mech_callback_t *callback)
135
const unsigned char *data, size_t data_size)
156
struct cram_auth_request *auth =
137
struct cram_auth_request *request =
157
138
(struct cram_auth_request *)auth_request;
158
139
const char *error;
160
if (parse_cram_response(auth, data, request->data_size, &error)) {
161
auth_request->callback = callback;
164
p_strdup(auth_request->pool, auth->username);
166
if (mech_is_valid_username(auth_request->user)) {
167
passdb->lookup_credentials(&auth->auth_request,
168
PASSDB_CREDENTIALS_CRAM_MD5,
169
credentials_callback);
141
if (parse_cram_response(request, data, data_size, &error)) {
142
if (auth_request_set_username(auth_request, request->username,
144
auth_request_lookup_credentials(auth_request,
145
PASSDB_CREDENTIALS_CRAM_MD5,
146
credentials_callback);
173
error = "invalid username";
176
151
if (error == NULL)
177
152
error = "authentication failed";
180
i_info("cram-md5(%s): %s",
181
auth->username == NULL ? "" : auth->username, error);
185
mech_auth_finish(auth_request, NULL, 0, FALSE);
189
static void mech_cram_md5_auth_free(struct auth_request *auth_request)
191
pool_unref(auth_request->pool);
194
static struct auth_request *
195
mech_cram_md5_auth_new(struct login_connection *conn,
196
unsigned int id, mech_callback_t *callback)
198
struct auth_login_reply reply;
199
struct cram_auth_request *auth;
154
auth_request_log_info(auth_request, "cram-md5", "%s", error);
155
auth_request_fail(auth_request);
159
mech_cram_md5_auth_initial(struct auth_request *auth_request,
160
const unsigned char *data __attr_unused__,
161
size_t data_size __attr_unused__)
163
struct cram_auth_request *request =
164
(struct cram_auth_request *)auth_request;
166
request->challenge = p_strdup(request->pool, get_cram_challenge());
167
auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE,
168
request->challenge, strlen(request->challenge));
171
static void mech_cram_md5_auth_free(struct auth_request *request)
173
pool_unref(request->pool);
176
static struct auth_request *mech_cram_md5_auth_new(void)
178
struct cram_auth_request *request;
202
181
pool = pool_alloconly_create("cram_md5_auth_request", 2048);
203
auth = p_new(pool, struct cram_auth_request, 1);
206
auth->auth_request.pool = pool;
207
auth->auth_request.auth_continue = mech_cram_md5_auth_continue;
208
auth->auth_request.auth_free = mech_cram_md5_auth_free;
210
auth->challenge = p_strdup(auth->pool, get_cram_challenge());
212
/* initialize reply */
213
mech_init_login_reply(&reply);
215
reply.result = AUTH_LOGIN_RESULT_CONTINUE;
217
/* send the initial challenge */
219
reply.data_size = strlen(auth->challenge);
220
callback(&reply, auth->challenge, conn);
222
return &auth->auth_request;
182
request = p_new(pool, struct cram_auth_request, 1);
183
request->pool = pool;
185
request->auth_request.pool = pool;
186
return &request->auth_request;
225
189
struct mech_module mech_cram_md5 = {
227
mech_cram_md5_auth_new
192
MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE,
194
MEMBER(passdb_need_plain) FALSE,
195
MEMBER(passdb_need_credentials) TRUE,
197
mech_cram_md5_auth_new,
198
mech_cram_md5_auth_initial,
199
mech_cram_md5_auth_continue,
200
mech_cram_md5_auth_free