/src/dovecot/src/lib-sasl/sasl-server-request.c
Line | Count | Source |
1 | | /* Copyright (c) 2023 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "str-sanitize.h" |
5 | | #include "buffer.h" |
6 | | |
7 | | #include "sasl-server-private.h" |
8 | | |
9 | | /* |
10 | | * Public API |
11 | | */ |
12 | | |
13 | | void sasl_server_request_create(struct sasl_server_req_ctx *rctx, |
14 | | const struct sasl_server_mech *mech, |
15 | | const char *protocol, |
16 | | struct event *event_parent) |
17 | 7.86k | { |
18 | 7.86k | struct sasl_server_instance *sinst = mech->sinst; |
19 | 7.86k | struct sasl_server *server = sinst->server; |
20 | 7.86k | struct sasl_server_request *req; |
21 | 7.86k | pool_t pool; |
22 | | |
23 | 7.86k | i_assert(mech->def != NULL); |
24 | 7.86k | i_assert(mech->def->funcs != NULL); |
25 | | |
26 | 7.86k | i_zero(rctx); |
27 | | |
28 | 7.86k | pool = pool_alloconly_create( |
29 | 7.86k | MEMPOOL_GROWING"sasl_server_request", 2048); |
30 | 7.86k | req = p_new(pool, struct sasl_server_request, 1); |
31 | 7.86k | req->pool = pool; |
32 | 7.86k | req->refcount = 1; |
33 | 7.86k | req->sinst = sinst; |
34 | 7.86k | req->rctx = rctx; |
35 | | |
36 | 7.86k | sinst->requests++; |
37 | 7.86k | server->requests++; |
38 | | |
39 | 7.86k | if (event_parent == NULL) { |
40 | 7.86k | req->event = event_create(sinst->event); |
41 | 7.86k | event_drop_parent_log_prefixes(req->event, 1); |
42 | 7.86k | } else { |
43 | 0 | req->event = event_create(event_parent); |
44 | 0 | event_add_category(req->event, &event_category_sasl_server); |
45 | 0 | } |
46 | 7.86k | event_set_append_log_prefix(req->event, |
47 | 7.86k | t_strdup_printf("sasl(%s): ", t_str_lcase(mech->def->name))); |
48 | | |
49 | 7.86k | struct sasl_server_mech_request *mreq; |
50 | | |
51 | 7.86k | if (mech->def->funcs->auth_new != NULL) |
52 | 7.24k | mreq = mech->def->funcs->auth_new(mech, pool); |
53 | 615 | else |
54 | 615 | mreq = p_new(pool, struct sasl_server_mech_request, 1); |
55 | 7.86k | mreq->pool = pool; |
56 | 7.86k | mreq->req = req; |
57 | 7.86k | mreq->set = &sinst->set; |
58 | 7.86k | mreq->mech = mech; |
59 | 7.86k | mreq->event = req->event; |
60 | 7.86k | mreq->protocol = p_strdup(pool, protocol); |
61 | | |
62 | 7.86k | req->mech = mreq; |
63 | 7.86k | rctx->mech = mech; |
64 | 7.86k | rctx->mech_name = mech->def->name; |
65 | 7.86k | rctx->request = req; |
66 | | |
67 | 7.86k | e_debug(req->event, "Request create"); |
68 | 7.86k | } |
69 | | |
70 | | void sasl_server_mech_request_ref(struct sasl_server_mech_request *mreq) |
71 | 16.9k | { |
72 | 16.9k | i_assert(mreq->req->refcount > 0); |
73 | 16.9k | mreq->req->refcount++; |
74 | 16.9k | } |
75 | | |
76 | | void sasl_server_mech_request_unref(struct sasl_server_mech_request **_mreq) |
77 | 24.8k | { |
78 | 24.8k | struct sasl_server_mech_request *mreq = *_mreq; |
79 | | |
80 | 24.8k | *_mreq = NULL; |
81 | 24.8k | if (mreq == NULL) |
82 | 0 | return; |
83 | | |
84 | 24.8k | struct sasl_server_request *req = mreq->req; |
85 | | |
86 | 24.8k | i_assert(req->refcount > 0); |
87 | 24.8k | if (--req->refcount > 0) |
88 | 16.9k | return; |
89 | | |
90 | 7.86k | struct sasl_server_instance *sinst = req->sinst; |
91 | 7.86k | struct sasl_server *server = sinst->server; |
92 | 7.86k | const struct sasl_server_request_funcs *funcs = server->funcs; |
93 | | |
94 | 7.86k | i_assert(sinst->requests > 0); |
95 | 7.86k | sinst->requests--; |
96 | 7.86k | i_assert(server->requests > 0); |
97 | 7.86k | server->requests--; |
98 | | |
99 | 7.86k | if (funcs->request_free != NULL && req->rctx != NULL) |
100 | 0 | funcs->request_free(req->rctx); |
101 | 7.86k | if (mreq->mech->def->funcs->auth_free != NULL) |
102 | 4.38k | mreq->mech->def->funcs->auth_free(mreq); |
103 | | |
104 | 7.86k | e_debug(req->event, "Request destroy"); |
105 | | |
106 | 7.86k | if (req->rctx != NULL) |
107 | 0 | i_zero(req->rctx); |
108 | 7.86k | event_unref(&req->event); |
109 | 7.86k | pool_unref(&req->pool); |
110 | 7.86k | } |
111 | | |
112 | | void sasl_server_request_ref(struct sasl_server_req_ctx *rctx) |
113 | 0 | { |
114 | 0 | sasl_server_mech_request_ref(rctx->request->mech); |
115 | 0 | } |
116 | | |
117 | | void sasl_server_request_unref(struct sasl_server_req_ctx *rctx) |
118 | 7.86k | { |
119 | 7.86k | struct sasl_server_request *req = rctx->request; |
120 | | |
121 | 7.86k | i_zero(rctx); |
122 | 7.86k | if (req == NULL) |
123 | 0 | return; |
124 | | |
125 | 7.86k | struct sasl_server_mech_request *mreq = req->mech; |
126 | | |
127 | 7.86k | sasl_server_mech_request_unref(&mreq); |
128 | 7.86k | } |
129 | | |
130 | | void sasl_server_request_destroy(struct sasl_server_req_ctx *rctx) |
131 | 7.86k | { |
132 | 7.86k | struct sasl_server_request *req = rctx->request; |
133 | | |
134 | 7.86k | if (req == NULL) { |
135 | 0 | i_zero(rctx); |
136 | 0 | return; |
137 | 0 | } |
138 | | |
139 | 7.86k | req->rctx = NULL; |
140 | 7.86k | sasl_server_request_unref(rctx); |
141 | 7.86k | } |
142 | | |
143 | | static bool |
144 | | sasl_server_request_fail_on_size(struct sasl_server_request *req, |
145 | | size_t data_size) |
146 | 16.8k | { |
147 | 16.8k | if (data_size > (size_t)SASL_MAX_MESSAGE_SIZE) { |
148 | | /* We should normally never get here, because the limit enforced |
149 | | by the auth service is smaller. |
150 | | */ |
151 | 296 | e_debug(req->event, "Excessive response size (> %d)", |
152 | 296 | SASL_MAX_MESSAGE_SIZE); |
153 | 296 | sasl_server_request_failure(req->mech); |
154 | 296 | return TRUE; |
155 | 296 | } |
156 | 16.5k | return FALSE; |
157 | 16.8k | } |
158 | | |
159 | | static bool |
160 | | sasl_server_request_fail_on_nuls(struct sasl_server_request *req, |
161 | | const unsigned char *data, size_t data_size) |
162 | 16.5k | { |
163 | 16.5k | const struct sasl_server_mech *mech = req->mech->mech; |
164 | | |
165 | 16.5k | if ((mech->def->flags & SASL_MECH_SEC_ALLOW_NULS) != 0) |
166 | 6.21k | return FALSE; |
167 | 10.3k | if (memchr(data, '\0', data_size) != NULL) { |
168 | 39 | e_debug(req->event, "Unexpected NUL in auth data"); |
169 | 39 | sasl_server_request_failure(req->mech); |
170 | 39 | return TRUE; |
171 | 39 | } |
172 | 10.3k | return FALSE; |
173 | 10.3k | } |
174 | | |
175 | | void sasl_server_request_initial(struct sasl_server_req_ctx *rctx, |
176 | | const unsigned char *data, size_t data_size) |
177 | 7.86k | { |
178 | 7.86k | struct sasl_server_request *req = rctx->request; |
179 | 7.86k | struct sasl_server_mech_request *mreq = req->mech; |
180 | 7.86k | const struct sasl_server_mech *mech = mreq->mech; |
181 | | |
182 | 7.86k | i_assert(data != NULL || data_size == 0); |
183 | | |
184 | 7.86k | if (data == NULL) { |
185 | 7.03k | e_debug(req->event, |
186 | 7.03k | "Started interaction without initial response"); |
187 | 7.03k | } else { |
188 | 828 | e_debug(req->event, |
189 | 828 | "Started interaction with initial response (size=%zu)", |
190 | 828 | data_size); |
191 | 828 | } |
192 | | |
193 | 7.86k | i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW); |
194 | 7.86k | req->state = SASL_SERVER_REQUEST_STATE_SERVER; |
195 | | |
196 | 7.86k | if (sasl_server_request_fail_on_size(req, data_size)) |
197 | 81 | return; |
198 | 7.78k | if (sasl_server_request_fail_on_nuls(req, data, data_size)) |
199 | 10 | return; |
200 | | |
201 | 7.77k | sasl_server_mech_request_ref(mreq); |
202 | 7.77k | i_assert(mech->def->funcs->auth_initial != NULL); |
203 | 7.77k | mech->def->funcs->auth_initial(mreq, data, data_size); |
204 | 7.77k | sasl_server_mech_request_unref(&mreq); |
205 | 7.77k | } |
206 | | |
207 | | void sasl_server_request_input(struct sasl_server_req_ctx *rctx, |
208 | | const unsigned char *data, size_t data_size) |
209 | 9.03k | { |
210 | 9.03k | struct sasl_server_request *req = rctx->request; |
211 | 9.03k | struct sasl_server_mech_request *mreq = req->mech; |
212 | 9.03k | const struct sasl_server_mech *mech = mreq->mech; |
213 | | |
214 | 9.03k | i_assert(data != NULL || data_size == 0); |
215 | | |
216 | 9.03k | e_debug(req->event, |
217 | 9.03k | "Client continued interaction with response (size=%zu)", |
218 | 9.03k | data_size); |
219 | | |
220 | 9.03k | if (req->state == SASL_SERVER_REQUEST_STATE_FINISHED && |
221 | 0 | req->finished_with_data) { |
222 | 0 | req->state = SASL_SERVER_REQUEST_STATE_SERVER; |
223 | 0 | if (!req->failed) |
224 | 0 | sasl_server_request_success(mreq, "", 0); |
225 | 0 | else |
226 | 0 | sasl_server_request_failure(mreq); |
227 | 0 | return; |
228 | 0 | } |
229 | 9.03k | i_assert(req->state == SASL_SERVER_REQUEST_STATE_CLIENT); |
230 | 9.03k | i_assert(!req->finished_with_data); |
231 | 9.03k | req->state = SASL_SERVER_REQUEST_STATE_SERVER; |
232 | | |
233 | 9.03k | if (sasl_server_request_fail_on_size(req, data_size)) |
234 | 215 | return; |
235 | 8.81k | if (sasl_server_request_fail_on_nuls(req, data, data_size)) |
236 | 29 | return; |
237 | | |
238 | 8.78k | sasl_server_mech_request_ref(mreq); |
239 | 8.78k | i_assert(mech->def->funcs->auth_continue != NULL); |
240 | 8.78k | mech->def->funcs->auth_continue(mreq, data, data_size); |
241 | 8.78k | sasl_server_mech_request_unref(&mreq); |
242 | 8.78k | } |
243 | | |
244 | | bool sasl_server_request_has_failed(const struct sasl_server_req_ctx *rctx) |
245 | 0 | { |
246 | 0 | return rctx->request->failed; |
247 | 0 | } |
248 | | |
249 | | void sasl_server_request_test_set_authid(struct sasl_server_req_ctx *rctx, |
250 | | const char *authid) |
251 | 0 | { |
252 | 0 | struct sasl_server_request *req = rctx->request; |
253 | |
|
254 | 0 | req->mech->authid = p_strdup(req->mech->pool, authid); |
255 | 0 | } |
256 | | |
257 | | /* |
258 | | * Mechanism API |
259 | | */ |
260 | | |
261 | | bool sasl_server_request_set_authid(struct sasl_server_mech_request *mreq, |
262 | | enum sasl_server_authid_type authid_type, |
263 | | const char *authid) |
264 | 5.88k | { |
265 | 5.88k | struct sasl_server_request *req = mreq->req; |
266 | 5.88k | struct sasl_server *server = req->sinst->server; |
267 | 5.88k | const struct sasl_server_request_funcs *funcs = server->funcs; |
268 | | |
269 | 5.88k | if (strlen(authid) > (size_t)SASL_MAX_AUTHID_SIZE) { |
270 | 126 | e_debug(req->event, "Failed to set authid: " |
271 | 126 | "Maximum length exceeded (> %d)", SASL_MAX_AUTHID_SIZE); |
272 | 126 | req->failed = TRUE; |
273 | 126 | return FALSE; |
274 | 126 | } |
275 | | |
276 | 5.76k | mreq->authid = p_strdup(req->pool, authid); |
277 | | |
278 | 5.76k | i_assert(req->rctx != NULL); |
279 | 5.76k | i_assert(funcs->request_set_authid != NULL); |
280 | 5.76k | if (!funcs->request_set_authid(req->rctx, authid_type, authid)) { |
281 | 2 | e_debug(req->event, "Failed to set authid '%s'", |
282 | 2 | str_sanitize(authid, 256)); |
283 | 2 | req->failed = TRUE; |
284 | 2 | return FALSE; |
285 | 2 | } |
286 | | |
287 | 5.76k | e_debug(req->event, "Set authid '%s'", str_sanitize(authid, 256)); |
288 | 5.76k | return TRUE; |
289 | 5.76k | } |
290 | | |
291 | | bool sasl_server_request_set_authzid(struct sasl_server_mech_request *mreq, |
292 | | const char *authzid) |
293 | 1.81k | { |
294 | 1.81k | struct sasl_server_request *req = mreq->req; |
295 | 1.81k | struct sasl_server *server = req->sinst->server; |
296 | 1.81k | const struct sasl_server_request_funcs *funcs = server->funcs; |
297 | | |
298 | 1.81k | if (strlen(authzid) > (size_t)SASL_MAX_AUTHID_SIZE) { |
299 | 26 | e_debug(req->event, "Failed to set authzid: " |
300 | 26 | "Maximum length exceeded (> %d)", SASL_MAX_AUTHID_SIZE); |
301 | 26 | req->failed = TRUE; |
302 | 26 | return FALSE; |
303 | 26 | } |
304 | | |
305 | 1.81k | i_assert(req->rctx != NULL); |
306 | 1.79k | i_assert(funcs->request_set_authzid != NULL); |
307 | 1.79k | if (!funcs->request_set_authzid(req->rctx, authzid)) { |
308 | 2 | e_debug(req->event, "Failed to set authzid '%s'", |
309 | 2 | str_sanitize(authzid, 256)); |
310 | 2 | req->failed = TRUE; |
311 | 2 | return FALSE; |
312 | 2 | } |
313 | | |
314 | 1.79k | e_debug(req->event, "Set authzid '%s'", str_sanitize(authzid, 256)); |
315 | 1.78k | return TRUE; |
316 | 1.79k | } |
317 | | |
318 | | void sasl_server_request_set_realm(struct sasl_server_mech_request *mreq, |
319 | | const char *realm) |
320 | 77 | { |
321 | 77 | struct sasl_server_request *req = mreq->req; |
322 | 77 | struct sasl_server *server = req->sinst->server; |
323 | 77 | const struct sasl_server_request_funcs *funcs = server->funcs; |
324 | | |
325 | 77 | i_assert(mreq->realm == NULL); |
326 | 77 | mreq->realm = p_strdup(req->pool, realm); |
327 | | |
328 | 77 | i_assert(req->rctx != NULL); |
329 | 77 | i_assert(funcs->request_set_realm != NULL); |
330 | 77 | funcs->request_set_realm(req->rctx, realm); |
331 | | |
332 | 77 | e_debug(req->event, "Set realm '%s'", str_sanitize(realm, 256)); |
333 | 77 | } |
334 | | |
335 | | bool sasl_server_request_get_extra_field(struct sasl_server_mech_request *mreq, |
336 | | const char *name, |
337 | | const char **field_r) |
338 | 0 | { |
339 | 0 | struct sasl_server_request *req = mreq->req; |
340 | 0 | struct sasl_server *server = req->sinst->server; |
341 | 0 | const struct sasl_server_request_funcs *funcs = server->funcs; |
342 | |
|
343 | 0 | i_assert(req->rctx != NULL); |
344 | 0 | if (funcs->request_get_extra_field == NULL) { |
345 | 0 | *field_r = NULL; |
346 | 0 | return FALSE; |
347 | 0 | } |
348 | 0 | return funcs->request_get_extra_field(req->rctx, name, field_r); |
349 | 0 | } |
350 | | |
351 | | void sasl_server_request_start_channel_binding( |
352 | | struct sasl_server_mech_request *mreq, const char *type) |
353 | 506 | { |
354 | 506 | struct sasl_server_request *req = mreq->req; |
355 | 506 | struct sasl_server *server = req->sinst->server; |
356 | 506 | const struct sasl_server_request_funcs *funcs = server->funcs; |
357 | | |
358 | 506 | e_debug(req->event, "Request channel binding '%s'", |
359 | 506 | str_sanitize(type, 64)); |
360 | | |
361 | 506 | i_assert(req->rctx != NULL); |
362 | 506 | i_assert(funcs->request_start_channel_binding != NULL); |
363 | 506 | funcs->request_start_channel_binding(req->rctx, type); |
364 | 506 | } |
365 | | |
366 | | int sasl_server_request_accept_channel_binding( |
367 | | struct sasl_server_mech_request *mreq, buffer_t **data_r) |
368 | 362 | { |
369 | 362 | struct sasl_server_request *req = mreq->req; |
370 | 362 | struct sasl_server *server = req->sinst->server; |
371 | 362 | const struct sasl_server_request_funcs *funcs = server->funcs; |
372 | 362 | int ret; |
373 | | |
374 | 362 | i_assert(req->rctx != NULL); |
375 | 362 | i_assert(funcs->request_accept_channel_binding != NULL); |
376 | 362 | ret = funcs->request_accept_channel_binding(req->rctx, data_r); |
377 | 362 | if (ret < 0) { |
378 | 0 | e_debug(req->event, "Failed to obtain channel binding data"); |
379 | 0 | return -1; |
380 | 0 | } |
381 | | |
382 | 362 | e_debug(req->event, "Obtained channel binding data (size=%zu)", |
383 | 362 | (*data_r)->used); |
384 | 362 | return 0; |
385 | 362 | } |
386 | | |
387 | | void sasl_server_request_output(struct sasl_server_mech_request *mreq, |
388 | | const void *data, size_t data_size) |
389 | 10.4k | { |
390 | 10.4k | struct sasl_server_request *req = mreq->req; |
391 | 10.4k | struct sasl_server *server = req->sinst->server; |
392 | 10.4k | const struct sasl_server_request_funcs *funcs = server->funcs; |
393 | | |
394 | 10.4k | i_assert(req->rctx != NULL); |
395 | | |
396 | 10.4k | i_assert(!req->failed); |
397 | 10.4k | i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW || |
398 | 10.4k | req->state == SASL_SERVER_REQUEST_STATE_SERVER || |
399 | 10.4k | req->state == SASL_SERVER_REQUEST_STATE_PASSDB); |
400 | 10.4k | req->state = SASL_SERVER_REQUEST_STATE_CLIENT; |
401 | 10.4k | req->sequence++; |
402 | | |
403 | 10.4k | e_debug(req->event, |
404 | 10.4k | "Server continued interaction with challenge (size=%zu)", |
405 | 10.4k | data_size); |
406 | | |
407 | 10.4k | const struct sasl_server_output output = { |
408 | 10.4k | .status = SASL_SERVER_OUTPUT_CONTINUE, |
409 | 10.4k | .data = data, |
410 | 10.4k | .data_size = data_size, |
411 | 10.4k | }; |
412 | 10.4k | i_assert(funcs->request_output != NULL); |
413 | 10.4k | funcs->request_output(req->rctx, &output); |
414 | 10.4k | } |
415 | | |
416 | | void sasl_server_request_success(struct sasl_server_mech_request *mreq, |
417 | | const void *data, size_t data_size) |
418 | 1.35k | { |
419 | 1.35k | struct sasl_server_request *req = mreq->req; |
420 | 1.35k | struct sasl_server *server = req->sinst->server; |
421 | 1.35k | const struct sasl_server_request_funcs *funcs = server->funcs; |
422 | | |
423 | 1.35k | i_assert(req->rctx != NULL); |
424 | | |
425 | 1.35k | i_assert(!req->failed); |
426 | 1.35k | i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW || |
427 | 1.35k | req->state == SASL_SERVER_REQUEST_STATE_SERVER || |
428 | 1.35k | req->state == SASL_SERVER_REQUEST_STATE_PASSDB); |
429 | 1.35k | req->state = SASL_SERVER_REQUEST_STATE_FINISHED; |
430 | 1.35k | req->sequence++; |
431 | 1.35k | if (data_size > 0) { |
432 | 913 | e_debug(req->event, |
433 | 913 | "Interaction succeeded with final data (size=%zu)", |
434 | 913 | data_size); |
435 | 913 | i_assert(!req->finished_with_data); |
436 | 913 | req->finished_with_data = TRUE; |
437 | 913 | } else { |
438 | 444 | e_debug(req->event, "Interaction succeeded"); |
439 | 444 | } |
440 | | |
441 | 1.35k | const struct sasl_server_output output = { |
442 | 1.35k | .status = SASL_SERVER_OUTPUT_SUCCESS, |
443 | 1.35k | .data = data, |
444 | 1.35k | .data_size = data_size, |
445 | 1.35k | }; |
446 | 1.35k | i_assert(funcs->request_output != NULL); |
447 | 1.35k | funcs->request_output(req->rctx, &output); |
448 | 1.35k | } |
449 | | |
450 | | static void |
451 | | sasl_server_request_failure_common(struct sasl_server_mech_request *mreq, |
452 | | enum sasl_server_output_status status, |
453 | | const void *data, size_t data_size) |
454 | 5.08k | { |
455 | 5.08k | struct sasl_server_request *req = mreq->req; |
456 | 5.08k | struct sasl_server *server = req->sinst->server; |
457 | 5.08k | const struct sasl_server_request_funcs *funcs = server->funcs; |
458 | | |
459 | 5.08k | i_assert(req->rctx != NULL); |
460 | | |
461 | 5.08k | i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW || |
462 | 5.08k | req->state == SASL_SERVER_REQUEST_STATE_SERVER || |
463 | 5.08k | req->state == SASL_SERVER_REQUEST_STATE_PASSDB); |
464 | 5.08k | req->state = SASL_SERVER_REQUEST_STATE_FINISHED; |
465 | 5.08k | req->sequence++; |
466 | 5.08k | req->failed = TRUE; |
467 | 5.08k | if (data_size > 0) { |
468 | 684 | i_assert(status != SASL_SERVER_OUTPUT_INTERNAL_FAILURE); |
469 | 684 | i_assert(status != SASL_SERVER_OUTPUT_PASSWORD_MISMATCH); |
470 | 684 | i_assert(!req->finished_with_data); |
471 | 684 | req->finished_with_data = TRUE; |
472 | 684 | e_debug(req->event, |
473 | 684 | "Interaction failed with final data (size=%zu)", |
474 | 684 | data_size); |
475 | 4.39k | } else if (status == SASL_SERVER_OUTPUT_PASSWORD_MISMATCH) { |
476 | 553 | e_debug(req->event, "Interaction failed: Password mismatch"); |
477 | 3.84k | } else if (status == SASL_SERVER_OUTPUT_INTERNAL_FAILURE) { |
478 | 0 | e_debug(req->event, "Interaction failed (internal failure)"); |
479 | 3.84k | } else { |
480 | 3.84k | e_debug(req->event, "Interaction failed"); |
481 | 3.84k | } |
482 | | |
483 | 5.08k | const struct sasl_server_output output = { |
484 | 5.08k | .status = status, |
485 | 5.08k | .data = data, |
486 | 5.08k | .data_size = data_size, |
487 | 5.08k | }; |
488 | 5.08k | i_assert(funcs->request_output != NULL); |
489 | 5.08k | funcs->request_output(req->rctx, &output); |
490 | 5.08k | } |
491 | | |
492 | | void sasl_server_request_failure_with_reply( |
493 | | struct sasl_server_mech_request *mreq, |
494 | | const void *data, size_t data_size) |
495 | 684 | { |
496 | 684 | sasl_server_request_failure_common(mreq, SASL_SERVER_OUTPUT_FAILURE, |
497 | 684 | data, data_size); |
498 | 684 | } |
499 | | |
500 | | void sasl_server_request_failure(struct sasl_server_mech_request *mreq) |
501 | 3.84k | { |
502 | 3.84k | sasl_server_request_failure_common(mreq, SASL_SERVER_OUTPUT_FAILURE, |
503 | 3.84k | "", 0); |
504 | 3.84k | } |
505 | | |
506 | | void sasl_server_request_password_mismatch( |
507 | | struct sasl_server_mech_request *mreq) |
508 | 553 | { |
509 | 553 | sasl_server_request_failure_common( |
510 | 553 | mreq, SASL_SERVER_OUTPUT_PASSWORD_MISMATCH, "", 0); |
511 | 553 | } |
512 | | |
513 | | void sasl_server_request_internal_failure( |
514 | | struct sasl_server_mech_request *mreq) |
515 | 0 | { |
516 | 0 | sasl_server_request_failure_common( |
517 | 0 | mreq, SASL_SERVER_OUTPUT_INTERNAL_FAILURE, "", 0); |
518 | 0 | } |
519 | | |
520 | | static const char * |
521 | | sasl_passdb_result_to_string(enum sasl_passdb_result_status status) |
522 | 0 | { |
523 | 0 | switch (status) { |
524 | 0 | case SASL_PASSDB_RESULT_INTERNAL_FAILURE: |
525 | 0 | return "internal-failure"; |
526 | 0 | case SASL_PASSDB_RESULT_SCHEME_NOT_AVAILABLE: |
527 | 0 | return "scheme-not-available"; |
528 | 0 | case SASL_PASSDB_RESULT_USER_UNKNOWN: |
529 | 0 | return "user-unknown"; |
530 | 0 | case SASL_PASSDB_RESULT_USER_DISABLED: |
531 | 0 | return "scheme-disabled"; |
532 | 0 | case SASL_PASSDB_RESULT_PASS_EXPIRED: |
533 | 0 | return "pass-expired"; |
534 | 0 | case SASL_PASSDB_RESULT_PASSWORD_MISMATCH: |
535 | 0 | return "password-mismatch"; |
536 | 0 | case SASL_PASSDB_RESULT_OK: |
537 | 0 | return "ok"; |
538 | 0 | } |
539 | 0 | i_unreached(); |
540 | 0 | } |
541 | | |
542 | | static void |
543 | | verify_plain_callback(struct sasl_server_req_ctx *rctx, |
544 | | const struct sasl_passdb_result *result) |
545 | 578 | { |
546 | 578 | struct sasl_server_request *req = rctx->request; |
547 | | |
548 | 578 | i_assert(req->state == SASL_SERVER_REQUEST_STATE_PASSDB); |
549 | 578 | req->state = SASL_SERVER_REQUEST_STATE_SERVER; |
550 | 578 | if (result->status == SASL_PASSDB_RESULT_INTERNAL_FAILURE) |
551 | 0 | req->failed = TRUE; |
552 | | |
553 | 578 | e_debug(req->event, "Finished plain passdb verification (status=%s)", |
554 | 578 | sasl_passdb_result_to_string(result->status)); |
555 | | |
556 | 578 | i_assert(req->passdb_type == SASL_SERVER_PASSDB_TYPE_VERIFY_PLAIN); |
557 | 578 | req->passdb_callback(req->mech, result); |
558 | 578 | } |
559 | | |
560 | | void sasl_server_request_verify_plain( |
561 | | struct sasl_server_mech_request *mreq, const char *password, |
562 | | sasl_server_mech_passdb_callback_t *callback) |
563 | 578 | { |
564 | 578 | struct sasl_server_request *req = mreq->req; |
565 | 578 | struct sasl_server *server = req->sinst->server; |
566 | 578 | const struct sasl_server_request_funcs *funcs = server->funcs; |
567 | | |
568 | 578 | i_assert(req->rctx != NULL); |
569 | | |
570 | 578 | i_assert(!req->failed); |
571 | 578 | i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW || |
572 | 578 | req->state == SASL_SERVER_REQUEST_STATE_SERVER); |
573 | 578 | req->state = SASL_SERVER_REQUEST_STATE_PASSDB; |
574 | | |
575 | 578 | e_debug(req->event, "Performing plain passdb verification"); |
576 | | |
577 | 578 | req->passdb_type = SASL_SERVER_PASSDB_TYPE_VERIFY_PLAIN; |
578 | 578 | req->passdb_callback = callback; |
579 | | |
580 | 578 | i_assert(funcs->request_verify_plain != NULL); |
581 | 578 | funcs->request_verify_plain(req->rctx, password, |
582 | 578 | verify_plain_callback); |
583 | 578 | } |
584 | | |
585 | | static void |
586 | | lookup_credentials_callback(struct sasl_server_req_ctx *rctx, |
587 | | const struct sasl_passdb_result *result) |
588 | 4.74k | { |
589 | 4.74k | struct sasl_server_request *req = rctx->request; |
590 | | |
591 | 4.74k | i_assert(req->state == SASL_SERVER_REQUEST_STATE_PASSDB); |
592 | 4.74k | req->state = SASL_SERVER_REQUEST_STATE_SERVER; |
593 | 4.74k | if (result->status == SASL_PASSDB_RESULT_INTERNAL_FAILURE) |
594 | 0 | req->failed = TRUE; |
595 | | |
596 | 4.74k | e_debug(req->event, "Finished passdb credentials lookup (status=%s)", |
597 | 4.74k | sasl_passdb_result_to_string(result->status)); |
598 | | |
599 | 4.74k | i_assert(req->passdb_type == |
600 | 4.74k | SASL_SERVER_PASSDB_TYPE_LOOKUP_CREDENTIALS); |
601 | 4.74k | req->passdb_callback(req->mech, result); |
602 | 4.74k | } |
603 | | |
604 | | void sasl_server_request_lookup_credentials( |
605 | | struct sasl_server_mech_request *mreq, const char *scheme, |
606 | | sasl_server_mech_passdb_callback_t *callback) |
607 | 4.74k | { |
608 | 4.74k | struct sasl_server_request *req = mreq->req; |
609 | 4.74k | struct sasl_server *server = req->sinst->server; |
610 | 4.74k | const struct sasl_server_request_funcs *funcs = server->funcs; |
611 | | |
612 | 4.74k | i_assert(req->rctx != NULL); |
613 | | |
614 | 4.74k | i_assert(!req->failed); |
615 | 4.74k | i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW || |
616 | 4.74k | req->state == SASL_SERVER_REQUEST_STATE_SERVER); |
617 | 4.74k | req->state = SASL_SERVER_REQUEST_STATE_PASSDB; |
618 | | |
619 | 4.74k | e_debug(req->event, "Performing passdb credentials lookup (scheme=%s)", |
620 | 4.74k | scheme); |
621 | | |
622 | 4.74k | req->passdb_type = SASL_SERVER_PASSDB_TYPE_LOOKUP_CREDENTIALS; |
623 | 4.74k | req->passdb_callback = callback; |
624 | | |
625 | 4.74k | i_assert(funcs->request_lookup_credentials != NULL); |
626 | 4.74k | funcs->request_lookup_credentials(req->rctx, scheme, |
627 | 4.74k | lookup_credentials_callback); |
628 | 4.74k | } |
629 | | |
630 | | static void |
631 | | set_credentials_callback(struct sasl_server_req_ctx *rctx, |
632 | | const struct sasl_passdb_result *result) |
633 | 195 | { |
634 | 195 | struct sasl_server_request *req = rctx->request; |
635 | | |
636 | 195 | i_assert(req->state == SASL_SERVER_REQUEST_STATE_PASSDB); |
637 | 195 | req->state = SASL_SERVER_REQUEST_STATE_SERVER; |
638 | 195 | if (result->status == SASL_PASSDB_RESULT_INTERNAL_FAILURE) |
639 | 0 | req->failed = TRUE; |
640 | | |
641 | 195 | e_debug(req->event, "Finished updating passdb credentials (status=%s)", |
642 | 195 | sasl_passdb_result_to_string(result->status)); |
643 | | |
644 | 195 | i_assert(req->passdb_type == SASL_SERVER_PASSDB_TYPE_SET_CREDENTIALS); |
645 | 195 | req->passdb_callback(req->mech, result); |
646 | 195 | } |
647 | | |
648 | | void sasl_server_request_set_credentials( |
649 | | struct sasl_server_mech_request *mreq, |
650 | | const char *scheme, const char *data, |
651 | | sasl_server_mech_passdb_callback_t *callback) |
652 | 195 | { |
653 | 195 | struct sasl_server_request *req = mreq->req; |
654 | 195 | struct sasl_server *server = req->sinst->server; |
655 | 195 | const struct sasl_server_request_funcs *funcs = server->funcs; |
656 | | |
657 | 195 | i_assert(req->rctx != NULL); |
658 | | |
659 | 195 | i_assert(!req->failed); |
660 | 195 | i_assert(req->state == SASL_SERVER_REQUEST_STATE_NEW || |
661 | 195 | req->state == SASL_SERVER_REQUEST_STATE_SERVER); |
662 | 195 | req->state = SASL_SERVER_REQUEST_STATE_PASSDB; |
663 | | |
664 | 195 | e_debug(req->event, "Updating passdb credentials (scheme=%s)", |
665 | 195 | scheme); |
666 | | |
667 | 195 | req->passdb_type = SASL_SERVER_PASSDB_TYPE_SET_CREDENTIALS; |
668 | 195 | req->passdb_callback = callback; |
669 | | |
670 | 195 | i_assert(funcs->request_set_credentials != NULL); |
671 | 195 | funcs->request_set_credentials(req->rctx, scheme, data, |
672 | 195 | set_credentials_callback); |
673 | 195 | } |
674 | | |
675 | | struct sasl_server_mech_request * |
676 | | sasl_server_request_get_mech_request(struct sasl_server_req_ctx *rctx) |
677 | 216 | { |
678 | 216 | return rctx->request->mech; |
679 | 216 | } |
680 | | |
681 | | struct sasl_server_req_ctx * |
682 | | sasl_server_request_get_req_ctx(struct sasl_server_mech_request *mreq) |
683 | 216 | { |
684 | 216 | i_assert(mreq->req->rctx != NULL); |
685 | 216 | return mreq->req->rctx; |
686 | 216 | } |