Coverage Report

Created: 2026-04-12 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/strongswan/src/libcharon/config/backend_manager.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2018 Tobias Brunner
3
 * Copyright (C) 2007-2009 Martin Willi
4
 *
5
 * Copyright (C) secunet Security Networks AG
6
 *
7
 * This program is free software; you can redistribute it and/or modify it
8
 * under the terms of the GNU General Public License as published by the
9
 * Free Software Foundation; either version 2 of the License, or (at your
10
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
11
 *
12
 * This program is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15
 * for more details.
16
 */
17
18
#include "backend_manager.h"
19
20
#include <sys/types.h>
21
22
#include <daemon.h>
23
#include <collections/linked_list.h>
24
#include <threading/rwlock.h>
25
26
27
typedef struct private_backend_manager_t private_backend_manager_t;
28
29
/**
30
 * Private data of an backend_manager_t object.
31
 */
32
struct private_backend_manager_t {
33
34
  /**
35
   * Public part of backend_manager_t object.
36
   */
37
  backend_manager_t public;
38
39
  /**
40
   * list of registered backends
41
   */
42
  linked_list_t *backends;
43
44
  /**
45
   * rwlock for backends
46
   */
47
  rwlock_t *lock;
48
};
49
50
/**
51
 * match of an ike_cfg
52
 */
53
typedef enum ike_cfg_match_t {
54
  /* doesn't match at all */
55
  MATCH_NONE    = 0x00,
56
  /* match for a %any host. For both hosts, hence skip 0x02 */
57
  MATCH_ANY   = 0x01,
58
  /* IKE version matches exactly (config is not for any version) */
59
  MATCH_VERSION = 0x04,
60
  /* local identity matches */
61
  MATCH_ME    = 0x08,
62
  /* remote identity matches */
63
  MATCH_OTHER   = 0x10,
64
} ike_cfg_match_t;
65
66
/**
67
 * data to pass nested IKE enumerator
68
 */
69
typedef struct {
70
  private_backend_manager_t *this;
71
  host_t *me;
72
  host_t *other;
73
} ike_data_t;
74
75
/**
76
 * inner enumerator constructor for IKE cfgs
77
 */
78
static enumerator_t *ike_enum_create(backend_t *backend, ike_data_t *data)
79
0
{
80
0
  return backend->create_ike_cfg_enumerator(backend, data->me, data->other);
81
0
}
82
83
/**
84
 * get a match of a candidate ike_cfg for two hosts
85
 */
86
static ike_cfg_match_t get_ike_match(ike_cfg_t *cand, host_t *me, host_t *other,
87
                   ike_version_t version)
88
0
{
89
0
  ike_cfg_match_t match = MATCH_NONE;
90
0
  int quality;
91
92
0
  if (cand->get_version(cand) != IKE_ANY &&
93
0
    version != cand->get_version(cand))
94
0
  {
95
0
    return MATCH_NONE;
96
0
  }
97
98
0
  if (me)
99
0
  {
100
0
    quality = cand->match_me(cand, me);
101
0
    if (!quality)
102
0
    {
103
0
      return MATCH_NONE;
104
0
    }
105
0
    match += quality * MATCH_ME;
106
0
  }
107
0
  else
108
0
  {
109
0
    match += MATCH_ANY;
110
0
  }
111
112
0
  if (other)
113
0
  {
114
0
    quality = cand->match_other(cand, other);
115
0
    if (!quality)
116
0
    {
117
0
      return MATCH_NONE;
118
0
    }
119
0
    match += quality * MATCH_OTHER;
120
0
  }
121
0
  else
122
0
  {
123
0
    match += MATCH_ANY;
124
0
  }
125
126
0
  if (match != MATCH_NONE &&
127
0
    cand->get_version(cand) != IKE_ANY)
128
0
  { /* if we have a match, improve it if candidate version specified */
129
0
    match += MATCH_VERSION;
130
0
  }
131
0
  return match;
132
0
}
133
134
/**
135
 * list element to help sorting
136
 */
137
typedef struct {
138
  ike_cfg_match_t match;
139
  ike_cfg_t *cfg;
140
} ike_match_entry_t;
141
142
CALLBACK(ike_enum_filter, bool,
143
  linked_list_t *configs, enumerator_t *orig, va_list args)
144
0
{
145
0
  ike_match_entry_t *entry;
146
0
  ike_cfg_t **out;
147
148
0
  VA_ARGS_VGET(args, out);
149
150
0
  if (orig->enumerate(orig, &entry))
151
0
  {
152
0
    *out = entry->cfg;
153
0
    return TRUE;
154
0
  }
155
0
  return FALSE;
156
0
}
157
158
CALLBACK(ike_match_entry_list_destroy, void,
159
  linked_list_t *configs)
160
0
{
161
0
  ike_match_entry_t *entry;
162
163
0
  while (configs->remove_last(configs, (void**)&entry) == SUCCESS)
164
0
  {
165
0
    entry->cfg->destroy(entry->cfg);
166
0
    free(entry);
167
0
  }
168
0
  configs->destroy(configs);
169
0
}
170
171
/**
172
 * Insert entry into match-sorted list
173
 */
174
static void insert_sorted_ike(ike_match_entry_t *entry, linked_list_t *list)
175
0
{
176
0
  enumerator_t *enumerator;
177
0
  ike_match_entry_t *current;
178
179
0
  enumerator = list->create_enumerator(list);
180
0
  while (enumerator->enumerate(enumerator, &current))
181
0
  {
182
0
    if (entry->match > current->match)
183
0
    {
184
0
      break;
185
0
    }
186
0
  }
187
0
  list->insert_before(list, enumerator, entry);
188
0
  enumerator->destroy(enumerator);
189
0
}
190
191
/**
192
 * Create a sorted list of all matching IKE configs
193
 */
194
static linked_list_t *get_matching_ike_cfgs(private_backend_manager_t *this,
195
                      host_t *me, host_t *other,
196
                      ike_version_t version)
197
0
{
198
0
  ike_cfg_t *current;
199
0
  enumerator_t *enumerator;
200
0
  ike_data_t *data;
201
0
  linked_list_t *configs;
202
0
  ike_cfg_match_t match;
203
0
  ike_match_entry_t *entry;
204
205
0
  INIT(data,
206
0
    .this = this,
207
0
    .me = me,
208
0
    .other = other,
209
0
  );
210
211
0
  configs = linked_list_create();
212
213
0
  this->lock->read_lock(this->lock);
214
0
  enumerator = enumerator_create_nested(
215
0
            this->backends->create_enumerator(this->backends),
216
0
            (void*)ike_enum_create, data, (void*)free);
217
218
0
  while (enumerator->enumerate(enumerator, &current))
219
0
  {
220
#if DEBUG_LEVEL >= 2
221
    char *my_addr = current->get_my_addr(current);
222
    char *other_addr = current->get_other_addr(current);
223
#endif
224
0
    match = get_ike_match(current, me, other, version);
225
0
    DBG3(DBG_CFG, "ike config match: %d (%s...%s %N)", match, my_addr,
226
0
       other_addr, ike_version_names, current->get_version(current));
227
228
0
    if (match)
229
0
    {
230
0
      DBG2(DBG_CFG, "  candidate: %s...%s, prio %d",
231
0
         my_addr, other_addr, match);
232
233
0
      INIT(entry,
234
0
        .match = match,
235
0
        .cfg = current->get_ref(current),
236
0
      );
237
0
      insert_sorted_ike(entry, configs);
238
0
    }
239
0
  }
240
0
  enumerator->destroy(enumerator);
241
0
  this->lock->unlock(this->lock);
242
243
0
  return configs;
244
0
}
245
246
METHOD(backend_manager_t, get_ike_cfg, ike_cfg_t*,
247
  private_backend_manager_t *this, host_t *me, host_t *other,
248
  ike_version_t version)
249
0
{
250
0
  linked_list_t *configs;
251
0
  ike_match_entry_t *entry;
252
0
  ike_cfg_t *found = NULL;
253
254
0
  DBG2(DBG_CFG, "looking for an %N config for %H...%H", ike_version_names,
255
0
     version, me, other);
256
257
0
  configs = get_matching_ike_cfgs(this, me, other, version);
258
0
  if (configs->get_first(configs, (void**)&entry) == SUCCESS)
259
0
  {
260
0
    found = entry->cfg->get_ref(entry->cfg);
261
0
    DBG2(DBG_CFG, "found matching ike config: %s...%s with prio %d",
262
0
       found->get_my_addr(found), found->get_other_addr(found),
263
0
       entry->match);
264
0
  }
265
0
  ike_match_entry_list_destroy(configs);
266
267
0
  return found;
268
0
}
269
270
METHOD(backend_manager_t, create_ike_cfg_enumerator, enumerator_t*,
271
  private_backend_manager_t *this, host_t *me, host_t *other,
272
  ike_version_t version)
273
0
{
274
0
  linked_list_t *configs;
275
276
0
  DBG2(DBG_CFG, "looking for %N configs for %H...%H", ike_version_names,
277
0
     version, me, other);
278
279
0
  configs = get_matching_ike_cfgs(this, me, other, version);
280
281
0
  return enumerator_create_filter(configs->create_enumerator(configs),
282
0
                  ike_enum_filter, configs,
283
0
                  ike_match_entry_list_destroy);
284
0
}
285
286
/**
287
 * Get the best ID match in one of the configs auth_cfg
288
 */
289
static id_match_t get_peer_match(identification_t *id,
290
                 peer_cfg_t *cfg, bool local)
291
0
{
292
0
  enumerator_t *enumerator;
293
0
  auth_cfg_t *auth;
294
0
  identification_t *candidate;
295
0
  id_match_t match = ID_MATCH_NONE;
296
0
  char *where DBG_UNUSED = local ? "local" : "remote";
297
298
0
  if (!id)
299
0
  {
300
0
    DBG3(DBG_CFG, "  %s id match: %d (%N)",
301
0
       where, ID_MATCH_ANY, id_type_names, ID_ANY);
302
0
    return ID_MATCH_ANY;
303
0
  }
304
305
  /* compare first auth config only */
306
0
  enumerator = cfg->create_auth_cfg_enumerator(cfg, local);
307
0
  if (enumerator->enumerate(enumerator, &auth))
308
0
  {
309
0
    candidate = auth->get(auth, AUTH_RULE_IDENTITY);
310
0
    if (candidate)
311
0
    {
312
0
      match = id->matches(id, candidate);
313
      /* match vice-versa, as the proposed IDr might be ANY */
314
0
      if (!match)
315
0
      {
316
0
        match = candidate->matches(candidate, id);
317
0
      }
318
0
    }
319
0
    else
320
0
    {
321
0
      match = ID_MATCH_ANY;
322
0
    }
323
0
  }
324
0
  enumerator->destroy(enumerator);
325
326
#if DEBUG_LEVEL >= 3
327
  chunk_t data = id->get_encoding(id);
328
  DBG3(DBG_CFG, "  %s id match: %d (%N: %#B)",
329
     where, match, id_type_names, id->get_type(id), &data);
330
#endif
331
0
  return match;
332
0
}
333
334
/**
335
 * data to pass nested peer enumerator
336
 */
337
typedef struct {
338
  rwlock_t *lock;
339
  identification_t *me;
340
  identification_t *other;
341
} peer_data_t;
342
343
/**
344
 * list element to help sorting
345
 */
346
typedef struct {
347
  id_match_t match_peer;
348
  ike_cfg_match_t match_ike;
349
  peer_cfg_t *cfg;
350
} match_entry_t;
351
352
/**
353
 * inner enumerator constructor for peer cfgs
354
 */
355
static enumerator_t *peer_enum_create(backend_t *backend, peer_data_t *data)
356
0
{
357
0
  return backend->create_peer_cfg_enumerator(backend, data->me, data->other);
358
0
}
359
360
/**
361
 * unlock/cleanup peer enumerator
362
 */
363
static void peer_enum_destroy(peer_data_t *data)
364
0
{
365
0
  data->lock->unlock(data->lock);
366
0
  free(data);
367
0
}
368
369
CALLBACK(peer_enum_filter, bool,
370
  linked_list_t *configs, enumerator_t *orig, va_list args)
371
0
{
372
0
  match_entry_t *entry;
373
0
  peer_cfg_t **out;
374
375
0
  VA_ARGS_VGET(args, out);
376
377
0
  if (orig->enumerate(orig, &entry))
378
0
  {
379
0
    *out = entry->cfg;
380
0
    return TRUE;
381
0
  }
382
0
  return FALSE;
383
0
}
384
385
CALLBACK(peer_enum_filter_destroy, void,
386
  linked_list_t *configs)
387
0
{
388
0
  match_entry_t *entry;
389
390
0
  while (configs->remove_last(configs, (void**)&entry) == SUCCESS)
391
0
  {
392
0
    entry->cfg->destroy(entry->cfg);
393
0
    free(entry);
394
0
  }
395
0
  configs->destroy(configs);
396
0
}
397
398
/**
399
 * Insert entry into match-sorted list
400
 */
401
static void insert_sorted(match_entry_t *entry, linked_list_t *list)
402
0
{
403
0
  enumerator_t *enumerator;
404
0
  match_entry_t *current;
405
406
0
  enumerator = list->create_enumerator(list);
407
0
  while (enumerator->enumerate(enumerator, &current))
408
0
  {
409
0
    if ((entry->match_ike > current->match_ike &&
410
0
       entry->match_peer >= current->match_peer) ||
411
0
      (entry->match_ike >= current->match_ike &&
412
0
        entry->match_peer > current->match_peer))
413
0
    {
414
0
      break;
415
0
    }
416
0
  }
417
0
  list->insert_before(list, enumerator, entry);
418
0
  enumerator->destroy(enumerator);
419
0
}
420
421
METHOD(backend_manager_t, create_peer_cfg_enumerator, enumerator_t*,
422
  private_backend_manager_t *this, host_t *me, host_t *other,
423
  identification_t *my_id, identification_t *other_id, ike_version_t version)
424
0
{
425
0
  enumerator_t *enumerator;
426
0
  peer_data_t *data;
427
0
  peer_cfg_t *cfg;
428
0
  linked_list_t *configs;
429
430
0
  INIT(data,
431
0
    .lock = this->lock,
432
0
    .me = my_id,
433
0
    .other = other_id,
434
0
  );
435
436
  /* create a sorted list with all matches */
437
0
  this->lock->read_lock(this->lock);
438
0
  enumerator = enumerator_create_nested(
439
0
          this->backends->create_enumerator(this->backends),
440
0
          (void*)peer_enum_create, data, (void*)peer_enum_destroy);
441
442
0
  if (!me && !other && !my_id && !other_id)
443
0
  { /* shortcut if we are doing a "listall" */
444
0
    return enumerator;
445
0
  }
446
447
0
  configs = linked_list_create();
448
0
  while (enumerator->enumerate(enumerator, &cfg))
449
0
  {
450
0
    ike_cfg_t *ike_cfg = cfg->get_ike_cfg(cfg);
451
0
    ike_cfg_match_t match_ike;
452
0
    id_match_t match_peer_me, match_peer_other;
453
0
    match_entry_t *entry;
454
455
0
    match_ike = get_ike_match(ike_cfg, me, other, version);
456
0
    DBG3(DBG_CFG, "peer config \"%s\", ike match: %d (%s...%s %N)",
457
0
       cfg->get_name(cfg), match_ike, ike_cfg->get_my_addr(ike_cfg),
458
0
       ike_cfg->get_other_addr(ike_cfg), ike_version_names,
459
0
       ike_cfg->get_version(ike_cfg));
460
461
0
    if (!match_ike)
462
0
    {
463
0
      continue;
464
0
    }
465
466
0
    match_peer_me = get_peer_match(my_id, cfg, TRUE);
467
0
    if (!match_peer_me)
468
0
    {
469
0
      continue;
470
0
    }
471
0
    match_peer_other = get_peer_match(other_id, cfg, FALSE);
472
473
0
    if (match_peer_other)
474
0
    {
475
0
      DBG2(DBG_CFG, "  candidate \"%s\", match: %d/%d/%d (me/other/ike)",
476
0
         cfg->get_name(cfg), match_peer_me, match_peer_other, match_ike);
477
0
      INIT(entry,
478
0
        .match_peer = match_peer_me + match_peer_other,
479
0
        .match_ike = match_ike,
480
0
        .cfg = cfg->get_ref(cfg),
481
0
      );
482
0
      insert_sorted(entry, configs);
483
0
    }
484
0
  }
485
0
  enumerator->destroy(enumerator);
486
487
0
  return enumerator_create_filter(configs->create_enumerator(configs),
488
0
                  peer_enum_filter, configs,
489
0
                  peer_enum_filter_destroy);
490
0
}
491
492
METHOD(backend_manager_t, get_peer_cfg_by_name, peer_cfg_t*,
493
  private_backend_manager_t *this, char *name)
494
0
{
495
0
  backend_t *backend;
496
0
  peer_cfg_t *config = NULL;
497
0
  enumerator_t *enumerator;
498
499
0
  this->lock->read_lock(this->lock);
500
0
  enumerator = this->backends->create_enumerator(this->backends);
501
0
  while (config == NULL && enumerator->enumerate(enumerator, (void**)&backend))
502
0
  {
503
0
    config = backend->get_peer_cfg_by_name(backend, name);
504
0
  }
505
0
  enumerator->destroy(enumerator);
506
0
  this->lock->unlock(this->lock);
507
0
  return config;
508
0
}
509
510
METHOD(backend_manager_t, remove_backend, void,
511
  private_backend_manager_t *this, backend_t *backend)
512
0
{
513
0
  this->lock->write_lock(this->lock);
514
0
  this->backends->remove(this->backends, backend, NULL);
515
0
  this->lock->unlock(this->lock);
516
0
}
517
518
METHOD(backend_manager_t, add_backend, void,
519
  private_backend_manager_t *this, backend_t *backend)
520
0
{
521
0
  this->lock->write_lock(this->lock);
522
0
  this->backends->insert_last(this->backends, backend);
523
0
  this->lock->unlock(this->lock);
524
0
}
525
526
METHOD(backend_manager_t, destroy, void,
527
  private_backend_manager_t *this)
528
0
{
529
0
  this->backends->destroy(this->backends);
530
0
  this->lock->destroy(this->lock);
531
0
  free(this);
532
0
}
533
534
/*
535
 * Described in header
536
 */
537
backend_manager_t *backend_manager_create()
538
2
{
539
2
  private_backend_manager_t *this;
540
541
2
  INIT(this,
542
2
    .public = {
543
2
      .get_ike_cfg = _get_ike_cfg,
544
2
      .create_ike_cfg_enumerator = _create_ike_cfg_enumerator,
545
2
      .get_peer_cfg_by_name = _get_peer_cfg_by_name,
546
2
      .create_peer_cfg_enumerator = _create_peer_cfg_enumerator,
547
2
      .add_backend = _add_backend,
548
2
      .remove_backend = _remove_backend,
549
2
      .destroy = _destroy,
550
2
    },
551
2
    .backends = linked_list_create(),
552
2
    .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
553
2
  );
554
555
2
  return &this->public;
556
2
}