Coverage Report

Created: 2026-03-11 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}