/src/dovecot/src/lib-sasl/sasl-server-mech.c
Line | Count | Source |
1 | | /* Copyright (c) 2023 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "llist.h" |
5 | | |
6 | | #include "sasl-server-private.h" |
7 | | |
8 | | /* |
9 | | * Accessors |
10 | | */ |
11 | | |
12 | | const char *sasl_server_mech_get_name(const struct sasl_server_mech *mech) |
13 | 0 | { |
14 | 0 | return mech->def->name; |
15 | 0 | } |
16 | | |
17 | | enum sasl_mech_security_flags |
18 | | sasl_server_mech_get_security_flags(const struct sasl_server_mech *mech) |
19 | 0 | { |
20 | 0 | return mech->def->flags; |
21 | 0 | } |
22 | | |
23 | | enum sasl_mech_passdb_need |
24 | | sasl_server_mech_get_passdb_need(const struct sasl_server_mech *mech) |
25 | 0 | { |
26 | 0 | return mech->reg->set.passdb_need; |
27 | 0 | } |
28 | | |
29 | | /* |
30 | | * Common functions |
31 | | */ |
32 | | |
33 | | void sasl_server_mech_generic_auth_initial( |
34 | | struct sasl_server_mech_request *mreq, |
35 | | const unsigned char *data, size_t data_size) |
36 | 5.05k | { |
37 | 5.05k | const struct sasl_server_mech *mech = mreq->mech; |
38 | | |
39 | 5.05k | if (data == NULL) { |
40 | 4.57k | sasl_server_request_output(mreq, uchar_empty_ptr, 0); |
41 | 4.57k | } else { |
42 | | /* initial reply given, even if it was 0 bytes */ |
43 | 479 | i_assert(mech->def->funcs->auth_continue != NULL); |
44 | 479 | mech->def->funcs->auth_continue(mreq, data, data_size); |
45 | 479 | } |
46 | 5.05k | } |
47 | | |
48 | | void sasl_server_mech_plain_verify_callback( |
49 | | struct sasl_server_mech_request *request, |
50 | | const struct sasl_passdb_result *result) |
51 | 625 | { |
52 | 625 | switch (result->status) { |
53 | 199 | case SASL_PASSDB_RESULT_OK: |
54 | 199 | sasl_server_request_success(request, "", 0); |
55 | 199 | break; |
56 | 0 | case SASL_PASSDB_RESULT_INTERNAL_FAILURE: |
57 | 0 | sasl_server_request_internal_failure(request); |
58 | 0 | break; |
59 | 426 | default: |
60 | 426 | sasl_server_request_failure(request); |
61 | 426 | break; |
62 | 625 | } |
63 | 625 | } |
64 | | |
65 | | /* |
66 | | * Global data |
67 | | */ |
68 | | |
69 | | static struct sasl_server_mech_data * |
70 | | sasl_server_mech_data_init(struct sasl_server *server, |
71 | | struct sasl_server_mech_def_reg *mech_dreg) |
72 | 99.2k | { |
73 | 99.2k | struct sasl_server_mech_data *mdata; |
74 | 99.2k | const struct sasl_server_mech_def *mech_def = mech_dreg->def; |
75 | | |
76 | 99.2k | if (mech_def->funcs->data_new == NULL) |
77 | 90.9k | return NULL; |
78 | 8.26k | if (mech_dreg->data != NULL) |
79 | 0 | return mech_dreg->data; |
80 | | |
81 | 8.26k | mech_dreg->data = mdata = mech_def->funcs->data_new(server->pool); |
82 | 8.26k | mdata->pool = server->pool; |
83 | 8.26k | mdata->server = server; |
84 | 8.26k | mdata->def = mech_def; |
85 | | |
86 | 8.26k | return mdata; |
87 | 8.26k | } |
88 | | |
89 | | static void |
90 | | sasl_server_mech_data_deinit(struct sasl_server_mech_def_reg *mech_dreg) |
91 | 99.2k | { |
92 | 99.2k | struct sasl_server_mech_data *mdata = mech_dreg->data; |
93 | | |
94 | 99.2k | if (mdata == NULL) |
95 | 90.9k | return; |
96 | 8.26k | mech_dreg->data = NULL; |
97 | | |
98 | 8.26k | if (mdata->def->funcs->data_free == NULL) |
99 | 0 | return; |
100 | 8.26k | mdata->def->funcs->data_free(mdata); |
101 | 8.26k | } |
102 | | |
103 | | /* |
104 | | * Registry |
105 | | */ |
106 | | |
107 | | static struct sasl_server_mech_def_reg * |
108 | | sasl_server_mech_find_def(struct sasl_server *server, |
109 | | const struct sasl_server_mech_def *def) |
110 | 99.2k | { |
111 | 99.2k | struct sasl_server_mech_def_reg *mech_dreg; |
112 | | |
113 | 99.2k | mech_dreg = server->mechs_head; |
114 | 644k | while (mech_dreg != NULL) { |
115 | 545k | if (mech_dreg->def == def) |
116 | 0 | break; |
117 | 545k | mech_dreg = mech_dreg->next; |
118 | 545k | } |
119 | | |
120 | 99.2k | return mech_dreg; |
121 | 99.2k | } |
122 | | |
123 | | static struct sasl_server_mech_def_reg * |
124 | | sasl_server_mech_find_def_by_name(struct sasl_server *server, |
125 | | const char *mech_name) |
126 | 99.2k | { |
127 | 99.2k | struct sasl_server_mech_def_reg *mech_dreg; |
128 | | |
129 | 99.2k | mech_dreg = server->mechs_head; |
130 | 644k | while (mech_dreg != NULL) { |
131 | 545k | if (strcmp(mech_dreg->def->name, mech_name) == 0) |
132 | 0 | break; |
133 | 545k | mech_dreg = mech_dreg->next; |
134 | 545k | } |
135 | | |
136 | 99.2k | return mech_dreg; |
137 | 99.2k | } |
138 | | |
139 | | static void |
140 | | sasl_server_mech_def_merge_settings( |
141 | | const struct sasl_server_mech_def *def, |
142 | | struct sasl_server_mech_settings *set, |
143 | | const struct sasl_server_mech_settings *new_set) |
144 | 198k | { |
145 | 198k | if (new_set == NULL) { |
146 | 198k | if (def->passdb_need > set->passdb_need) |
147 | 148k | set->passdb_need = def->passdb_need; |
148 | 198k | return; |
149 | 198k | } |
150 | 0 | if (new_set->passdb_need > set->passdb_need && |
151 | 0 | new_set->passdb_need > def->passdb_need) |
152 | 0 | set->passdb_need = new_set->passdb_need; |
153 | 0 | else |
154 | 0 | set->passdb_need = def->passdb_need; |
155 | 0 | } |
156 | | |
157 | | static struct sasl_server_mech_def_reg * |
158 | | sasl_server_mech_register_def(struct sasl_server *server, |
159 | | const struct sasl_server_mech_def *def, |
160 | | const struct sasl_server_mech_settings *set) |
161 | 99.2k | { |
162 | 99.2k | struct sasl_server_mech_def_reg *mech_dreg; |
163 | | |
164 | 99.2k | i_assert(def->funcs != NULL); |
165 | 99.2k | i_assert(strcmp(def->name, t_str_ucase(def->name)) == 0); |
166 | | |
167 | 99.2k | mech_dreg = sasl_server_mech_find_def(server, def); |
168 | 99.2k | if (mech_dreg != NULL) { |
169 | 0 | i_assert(mech_dreg->refcount > 0); |
170 | 0 | mech_dreg->refcount++; |
171 | |
|
172 | 0 | sasl_server_mech_def_merge_settings(def, &mech_dreg->set, set); |
173 | 0 | return mech_dreg; |
174 | 0 | } |
175 | | |
176 | 99.2k | i_assert(sasl_server_mech_find_def_by_name(server, def->name) == NULL); |
177 | | |
178 | 99.2k | mech_dreg = p_new(server->pool, struct sasl_server_mech_def_reg, 1); |
179 | 99.2k | mech_dreg->def = def; |
180 | 99.2k | mech_dreg->refcount = 1; |
181 | 99.2k | sasl_server_mech_def_merge_settings(def, &mech_dreg->set, set); |
182 | | |
183 | 99.2k | DLLIST2_APPEND(&server->mechs_head, &server->mechs_tail, mech_dreg); |
184 | 99.2k | return mech_dreg; |
185 | 99.2k | } |
186 | | |
187 | | static struct sasl_server_mech_reg * |
188 | | sasl_server_mech_reg_find(struct sasl_server_instance *sinst, const char *name) |
189 | 110k | { |
190 | 110k | struct sasl_server_mech_reg *mech_reg; |
191 | 110k | name = t_str_ucase(name); |
192 | | |
193 | 719k | for (mech_reg = sinst->mechs_head; mech_reg != NULL; |
194 | 620k | mech_reg = mech_reg->next) { |
195 | 620k | if (strcmp(mech_reg->def_reg->def->name, name) == 0) |
196 | 11.4k | return mech_reg; |
197 | 620k | } |
198 | 99.3k | return NULL; |
199 | 110k | } |
200 | | |
201 | | static struct sasl_server_mech * |
202 | | sasl_server_mech_create(struct sasl_server_instance *sinst, |
203 | | const struct sasl_server_mech_def *def) |
204 | 99.2k | { |
205 | 99.2k | struct sasl_server_mech *mech; |
206 | | |
207 | 99.2k | if (def->funcs->mech_new != NULL) |
208 | 49.6k | mech = def->funcs->mech_new(sinst->pool); |
209 | 49.6k | else |
210 | 49.6k | mech = p_new(sinst->pool, struct sasl_server_mech, 1); |
211 | 99.2k | mech->pool = sinst->pool; |
212 | 99.2k | mech->sinst = sinst; |
213 | 99.2k | mech->def = def; |
214 | | |
215 | 99.2k | mech->event = event_create(sinst->event); |
216 | 99.2k | event_drop_parent_log_prefixes(mech->event, 1); |
217 | 99.2k | event_set_append_log_prefix(mech->event, |
218 | 99.2k | t_strdup_printf("sasl(%s): ", t_str_lcase(def->name))); |
219 | | |
220 | 99.2k | return mech; |
221 | 99.2k | } |
222 | | |
223 | | static void sasl_server_mech_free(struct sasl_server_mech *mech) |
224 | 99.2k | { |
225 | 99.2k | event_unref(&mech->event); |
226 | 99.2k | mech->def = NULL; |
227 | 99.2k | } |
228 | | |
229 | | static struct sasl_server_mech * |
230 | | sasl_server_mech_register_common(struct sasl_server_instance *sinst, |
231 | | const struct sasl_server_mech_def *def, |
232 | | const struct sasl_server_mech_settings *set) |
233 | 99.2k | { |
234 | 99.2k | struct sasl_server_mech_def_reg *mech_dreg; |
235 | 99.2k | struct sasl_server_mech_reg *mech_reg; |
236 | 99.2k | struct sasl_server_mech *mech; |
237 | | |
238 | 99.2k | i_assert(sasl_server_mech_reg_find(sinst, def->name) == NULL); |
239 | | |
240 | 99.2k | mech_dreg = sasl_server_mech_register_def(sinst->server, def, set); |
241 | | |
242 | 99.2k | mech_reg = p_new(sinst->pool, struct sasl_server_mech_reg, 1); |
243 | 99.2k | mech_reg->def_reg = mech_dreg; |
244 | 99.2k | sasl_server_mech_def_merge_settings(def, &mech_reg->set, set); |
245 | | |
246 | 99.2k | DLLIST_PREPEND_FULL(&mech_dreg->insts, mech_reg, def_prev, def_next); |
247 | | |
248 | 99.2k | mech = sasl_server_mech_create(sinst, def); |
249 | 99.2k | mech->reg = mech_reg; |
250 | 99.2k | mech->data = sasl_server_mech_data_init(sinst->server, mech_dreg); |
251 | 99.2k | mech_reg->mech = mech; |
252 | | |
253 | 99.2k | return mech; |
254 | 99.2k | } |
255 | | |
256 | | struct sasl_server_mech * |
257 | | sasl_server_mech_register(struct sasl_server_instance *sinst, |
258 | | const struct sasl_server_mech_def *def, |
259 | | const struct sasl_server_mech_settings *set) |
260 | 99.2k | { |
261 | 99.2k | struct sasl_server_mech *mech; |
262 | | |
263 | 99.2k | mech = sasl_server_mech_register_common(sinst, def, set); |
264 | 99.2k | DLLIST2_APPEND(&sinst->mechs_head, &sinst->mechs_tail, mech->reg); |
265 | | |
266 | 99.2k | return mech; |
267 | 99.2k | } |
268 | | |
269 | | struct sasl_server_mech * |
270 | | sasl_server_mech_register_hidden(struct sasl_server_instance *sinst, |
271 | | const struct sasl_server_mech_def *def, |
272 | | const struct sasl_server_mech_settings *set) |
273 | 0 | { |
274 | 0 | struct sasl_server_mech *mech; |
275 | |
|
276 | 0 | mech = sasl_server_mech_register_common(sinst, def, set); |
277 | 0 | DLLIST_PREPEND(&sinst->mechs_hidden, mech->reg); |
278 | |
|
279 | 0 | return mech; |
280 | 0 | } |
281 | | |
282 | | const struct sasl_server_mech * |
283 | | sasl_server_mech_find(struct sasl_server_instance *sinst, const char *name) |
284 | 11.6k | { |
285 | 11.6k | struct sasl_server_mech_reg *mech_reg; |
286 | | |
287 | 11.6k | mech_reg = sasl_server_mech_reg_find(sinst, name); |
288 | 11.6k | if (mech_reg == NULL) |
289 | 149 | return NULL; |
290 | 11.4k | return mech_reg->mech; |
291 | 11.6k | } |
292 | | |
293 | | static void sasl_server_mech_reg_free(struct sasl_server_mech_reg *mech_reg) |
294 | 99.2k | { |
295 | 99.2k | struct sasl_server_mech *mech = mech_reg->mech; |
296 | | |
297 | 99.2k | if (mech->def->funcs->mech_free != NULL) |
298 | 0 | mech->def->funcs->mech_free(mech); |
299 | | |
300 | 99.2k | struct sasl_server_mech_def_reg *mech_dreg = mech_reg->def_reg; |
301 | | |
302 | 99.2k | i_assert(mech_dreg->def == mech->def); |
303 | 99.2k | DLLIST_REMOVE_FULL(&mech_dreg->insts, mech_reg, def_prev, def_next); |
304 | 99.2k | mech_reg->mech = NULL; |
305 | 99.2k | sasl_server_mech_free(mech); |
306 | | |
307 | 99.2k | if (mech_dreg->insts == NULL) { |
308 | 99.2k | struct sasl_server *server = mech->sinst->server; |
309 | | |
310 | 99.2k | DLLIST2_REMOVE(&server->mechs_head, &server->mechs_tail, |
311 | 99.2k | mech_dreg); |
312 | 99.2k | sasl_server_mech_data_deinit(mech_dreg); |
313 | 99.2k | mech_dreg->def = NULL; |
314 | 99.2k | } |
315 | 99.2k | } |
316 | | |
317 | | static struct sasl_server_mech_reg * |
318 | | sasl_server_mech_reg_list_find(struct sasl_server_mech_reg *mech_reg_list, |
319 | | const struct sasl_server_mech_def *def) |
320 | 0 | { |
321 | 0 | struct sasl_server_mech_reg *mech_reg; |
322 | |
|
323 | 0 | mech_reg = mech_reg_list; |
324 | 0 | while (mech_reg != NULL) { |
325 | 0 | struct sasl_server_mech_reg *mech_reg_next = mech_reg->next; |
326 | |
|
327 | 0 | if (mech_reg->mech->def == def) |
328 | 0 | return mech_reg; |
329 | 0 | mech_reg = mech_reg_next; |
330 | 0 | } |
331 | 0 | return NULL; |
332 | 0 | } |
333 | | |
334 | | void sasl_server_mech_unregister(struct sasl_server_instance *sinst, |
335 | | const struct sasl_server_mech_def *def) |
336 | 0 | { |
337 | 0 | struct sasl_server_mech_reg *mech_reg; |
338 | |
|
339 | 0 | mech_reg = sasl_server_mech_reg_list_find(sinst->mechs_head, def); |
340 | 0 | if (mech_reg != NULL) { |
341 | 0 | DLLIST2_REMOVE(&sinst->mechs_head, |
342 | 0 | &sinst->mechs_tail, mech_reg); |
343 | 0 | } else { |
344 | 0 | mech_reg = sasl_server_mech_reg_list_find( |
345 | 0 | sinst->mechs_hidden, def); |
346 | 0 | if (mech_reg != NULL) |
347 | 0 | DLLIST_REMOVE(&sinst->mechs_hidden, mech_reg); |
348 | 0 | } |
349 | |
|
350 | 0 | if (mech_reg == NULL) |
351 | 0 | return; |
352 | | |
353 | 0 | sasl_server_mech_reg_free(mech_reg); |
354 | 0 | } |
355 | | |
356 | | static struct sasl_server_mech_reg * |
357 | | sasl_server_mech_reg_list_free(struct sasl_server_mech_reg *mech_reg_list) |
358 | 16.5k | { |
359 | 16.5k | struct sasl_server_mech_reg *mech_reg; |
360 | | |
361 | 16.5k | mech_reg = mech_reg_list; |
362 | 115k | while (mech_reg != NULL) { |
363 | 99.2k | struct sasl_server_mech_reg *mech_reg_next = mech_reg->next; |
364 | | |
365 | 99.2k | sasl_server_mech_reg_free(mech_reg); |
366 | 99.2k | mech_reg = mech_reg_next; |
367 | 99.2k | } |
368 | 16.5k | return NULL; |
369 | 16.5k | } |
370 | | |
371 | | void sasl_server_instance_mech_registry_free( |
372 | | struct sasl_server_instance *sinst) |
373 | 8.26k | { |
374 | 8.26k | sasl_server_mech_reg_list_free(sinst->mechs_head); |
375 | 8.26k | sasl_server_mech_reg_list_free(sinst->mechs_hidden); |
376 | 8.26k | } |
377 | | |
378 | | void sasl_server_mech_registry_free(struct sasl_server *server) |
379 | 8.26k | { |
380 | 8.26k | i_assert(server->mechs_head == NULL); |
381 | 8.26k | } |
382 | | |
383 | | /* |
384 | | * Iterator |
385 | | */ |
386 | | |
387 | | struct sasl_server_mech_iter_prv { |
388 | | struct sasl_server_mech_iter iter; |
389 | | |
390 | | union { |
391 | | struct sasl_server_mech_reg *reg; |
392 | | struct sasl_server_mech_def_reg *def_reg; |
393 | | }; |
394 | | |
395 | | bool instance:1; |
396 | | bool ended:1; |
397 | | }; |
398 | | |
399 | | struct sasl_server_mech_iter * |
400 | | sasl_server_mech_iter_new(struct sasl_server *server) |
401 | 0 | { |
402 | 0 | struct sasl_server_mech_iter_prv *iterp; |
403 | |
|
404 | 0 | iterp = i_new(struct sasl_server_mech_iter_prv, 1); |
405 | 0 | iterp->def_reg = server->mechs_head; |
406 | |
|
407 | 0 | return &iterp->iter; |
408 | 0 | } |
409 | | |
410 | | struct sasl_server_mech_iter * |
411 | | sasl_server_instance_mech_iter_new(struct sasl_server_instance *sinst) |
412 | 0 | { |
413 | 0 | struct sasl_server_mech_iter_prv *iterp; |
414 | |
|
415 | 0 | iterp = i_new(struct sasl_server_mech_iter_prv, 1); |
416 | 0 | iterp->reg = sinst->mechs_head; |
417 | 0 | iterp->instance = TRUE; |
418 | |
|
419 | 0 | return &iterp->iter; |
420 | 0 | } |
421 | | |
422 | | bool sasl_server_mech_iter_next(struct sasl_server_mech_iter *iter) |
423 | 0 | { |
424 | 0 | struct sasl_server_mech_iter_prv *iterp = |
425 | 0 | container_of(iter, struct sasl_server_mech_iter_prv, iter); |
426 | 0 | const struct sasl_server_mech_def *def; |
427 | 0 | const struct sasl_server_mech_settings *set; |
428 | |
|
429 | 0 | if (!iterp->instance) { |
430 | 0 | if (iterp->def_reg == NULL) { |
431 | 0 | iterp->ended = TRUE; |
432 | 0 | return FALSE; |
433 | 0 | } |
434 | 0 | def = iterp->def_reg->def; |
435 | 0 | set = &iterp->def_reg->set; |
436 | 0 | iterp->def_reg = iterp->def_reg->next; |
437 | 0 | } else { |
438 | 0 | if (iterp->reg == NULL) { |
439 | 0 | iterp->ended = TRUE; |
440 | 0 | return FALSE; |
441 | 0 | } |
442 | 0 | def = iterp->reg->mech->def; |
443 | 0 | set = &iterp->reg->set; |
444 | 0 | iterp->reg = iterp->reg->next; |
445 | 0 | } |
446 | | |
447 | 0 | iterp->iter.name = def->name; |
448 | 0 | iterp->iter.flags = def->flags; |
449 | 0 | iterp->iter.passdb_need = set->passdb_need; |
450 | |
|
451 | 0 | return TRUE; |
452 | 0 | } |
453 | | |
454 | | bool sasl_server_mech_iter_ended(struct sasl_server_mech_iter *iter) |
455 | 0 | { |
456 | 0 | struct sasl_server_mech_iter_prv *iterp = |
457 | 0 | container_of(iter, struct sasl_server_mech_iter_prv, iter); |
458 | |
|
459 | 0 | return iterp->ended; |
460 | 0 | } |
461 | | |
462 | | void sasl_server_mech_iter_free(struct sasl_server_mech_iter **_iter) |
463 | 0 | { |
464 | 0 | struct sasl_server_mech_iter *iter = *_iter; |
465 | |
|
466 | 0 | if (iter == NULL) |
467 | 0 | return; |
468 | 0 | *_iter = NULL; |
469 | |
|
470 | 0 | struct sasl_server_mech_iter_prv *iterp = |
471 | 0 | container_of(iter, struct sasl_server_mech_iter_prv, iter); |
472 | |
|
473 | 0 | i_free(iterp); |
474 | 0 | } |