Coverage Report

Created: 2026-10-17 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/strongswan/src/libcharon/config/ike_cfg.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2012-2019 Tobias Brunner
3
 * Copyright (C) 2005-2007 Martin Willi
4
 * Copyright (C) 2005 Jan Hutter
5
 *
6
 * Copyright (C) secunet Security Networks AG
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU General Public License as published by the
10
 * Free Software Foundation; either version 2 of the License, or (at your
11
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
12
 *
13
 * This program is distributed in the hope that it will be useful, but
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16
 * for more details.
17
 */
18
19
#define _GNU_SOURCE /* for stdndup() */
20
#include <string.h>
21
22
#include "ike_cfg.h"
23
24
#include <daemon.h>
25
26
ENUM(ike_version_names, IKE_ANY, IKEV2,
27
  "IKEv1/2",
28
  "IKEv1",
29
  "IKEv2",
30
);
31
32
typedef struct private_ike_cfg_t private_ike_cfg_t;
33
34
/**
35
 * Private data of an ike_cfg_t object
36
 */
37
struct private_ike_cfg_t {
38
39
  /**
40
   * Public part
41
   */
42
  ike_cfg_t public;
43
44
  /**
45
   * Number of references hold by others to this ike_cfg
46
   */
47
  refcount_t refcount;
48
49
  /**
50
   * IKE version to use
51
   */
52
  ike_version_t version;
53
54
  /**
55
   * Address list string for local host
56
   */
57
  char *me;
58
59
  /**
60
   * Address list string for remote host
61
   */
62
  char *other;
63
64
  /**
65
   * Local single host or DNS names, as allocated char*
66
   */
67
  linked_list_t *my_hosts;
68
69
  /**
70
   * Remote single host or DNS names, as allocated char*
71
   */
72
  linked_list_t *other_hosts;
73
74
  /**
75
   * Local ranges/subnets this config matches to, as traffic_selector_t*
76
   */
77
  linked_list_t *my_ranges;
78
79
  /**
80
   * Remote ranges/subnets this config matches to, as traffic_selector_t*
81
   */
82
  linked_list_t *other_ranges;
83
84
  /**
85
   * our source port
86
   */
87
  uint16_t my_port;
88
89
  /**
90
   * destination port
91
   */
92
  uint16_t other_port;
93
94
  /**
95
   * should we send a certificate request?
96
   */
97
  bool certreq;
98
99
  /**
100
   * should we send an OCSP status request?
101
   */
102
  bool ocsp_certreq;
103
104
  /**
105
   * enforce UDP encapsulation
106
   */
107
  bool force_encap;
108
109
  /**
110
   * use IKE fragmentation
111
   */
112
  fragmentation_t fragmentation;
113
114
  /**
115
   * childless IKE_SAs
116
   */
117
  childless_t childless;
118
119
  /**
120
   * DSCP value to use on sent IKE packets
121
   */
122
  uint8_t dscp;
123
124
  /**
125
   * List of proposals to use
126
   */
127
  linked_list_t *proposals;
128
};
129
130
METHOD(ike_cfg_t, get_version, ike_version_t,
131
  private_ike_cfg_t *this)
132
0
{
133
0
  return this->version;
134
0
}
135
136
METHOD(ike_cfg_t, send_certreq, bool,
137
  private_ike_cfg_t *this)
138
0
{
139
0
  return this->certreq;
140
0
}
141
142
METHOD(ike_cfg_t, send_ocsp_certreq, bool,
143
  private_ike_cfg_t *this)
144
0
{
145
0
  return this->ocsp_certreq;
146
0
}
147
148
METHOD(ike_cfg_t, force_encap_, bool,
149
  private_ike_cfg_t *this)
150
0
{
151
0
  return this->force_encap;
152
0
}
153
154
METHOD(ike_cfg_t, fragmentation, fragmentation_t,
155
  private_ike_cfg_t *this)
156
0
{
157
0
  return this->fragmentation;
158
0
}
159
160
METHOD(ike_cfg_t, childless, childless_t,
161
  private_ike_cfg_t *this)
162
0
{
163
0
  return this->childless;
164
0
}
165
166
/**
167
 * Common function for resolve_me/other
168
 */
169
static host_t* resolve(linked_list_t *hosts, int family, uint16_t port)
170
0
{
171
0
  enumerator_t *enumerator;
172
0
  host_t *host = NULL;
173
0
  bool tried = FALSE;
174
0
  char *str;
175
176
0
  enumerator = hosts->create_enumerator(hosts);
177
0
  while (enumerator->enumerate(enumerator, &str))
178
0
  {
179
0
    host = host_create_from_dns(str, family, port);
180
0
    if (host)
181
0
    {
182
0
      break;
183
0
    }
184
0
    tried = TRUE;
185
0
  }
186
0
  enumerator->destroy(enumerator);
187
188
0
  if (!host && !tried)
189
0
  {
190
    /* we have no single host configured, return %any */
191
0
    host = host_create_any(family ?: AF_INET);
192
0
    host->set_port(host, port);
193
0
  }
194
0
  return host;
195
0
}
196
197
METHOD(ike_cfg_t, resolve_me, host_t*,
198
  private_ike_cfg_t *this, int family)
199
0
{
200
0
  return resolve(this->my_hosts, family, this->my_port);
201
0
}
202
203
METHOD(ike_cfg_t, resolve_other, host_t*,
204
  private_ike_cfg_t *this, int family)
205
0
{
206
0
  return resolve(this->other_hosts, family, this->other_port);
207
0
}
208
209
/**
210
 * Common function for match_me/other
211
 */
212
static u_int match(linked_list_t *hosts, linked_list_t *ranges, uint16_t port,
213
    host_t *cand)
214
0
{
215
0
  enumerator_t *enumerator;
216
0
  traffic_selector_t *ts;
217
0
  char *str;
218
0
  host_t *host;
219
0
  uint8_t mask;
220
0
  u_int quality = 0;
221
222
  /* try single hosts first */
223
0
  enumerator = hosts->create_enumerator(hosts);
224
0
  while (enumerator->enumerate(enumerator, &str))
225
0
  {
226
0
    host = host_create_from_dns(str, cand->get_family(cand), 0);
227
0
    if (host)
228
0
    {
229
0
      if (host->ip_equals(host, cand))
230
0
      {
231
0
        quality = max(quality, 128 + 1);
232
0
      }
233
0
      else if (host->is_anyaddr(host))
234
0
      {
235
0
        quality = max(quality, 1);
236
0
      }
237
0
      host->destroy(host);
238
0
    }
239
0
  }
240
0
  enumerator->destroy(enumerator);
241
242
  /* then ranges/subnets */
243
0
  enumerator = ranges->create_enumerator(ranges);
244
0
  while (enumerator->enumerate(enumerator, &ts))
245
0
  {
246
0
    if (ts->includes(ts, cand))
247
0
    {
248
0
      if (ts->to_subnet(ts, &host, &mask))
249
0
      {
250
0
        quality = max(quality, mask + 1);
251
0
      }
252
0
      else
253
0
      {
254
0
        quality = max(quality, 1);
255
0
      }
256
0
      host->destroy(host);
257
0
    }
258
0
  }
259
0
  enumerator->destroy(enumerator);
260
261
  /* honor if port matches exactly */
262
0
  if (quality && port == cand->get_port(cand))
263
0
  {
264
0
    quality += 1;
265
0
  }
266
0
  return quality;
267
0
}
268
269
METHOD(ike_cfg_t, match_me, u_int,
270
  private_ike_cfg_t *this, host_t *host)
271
0
{
272
0
  return match(this->my_hosts, this->my_ranges, this->my_port, host);
273
0
}
274
275
METHOD(ike_cfg_t, match_other, u_int,
276
  private_ike_cfg_t *this, host_t *host)
277
0
{
278
0
  return match(this->other_hosts, this->other_ranges, this->other_port, host);
279
0
}
280
281
METHOD(ike_cfg_t, get_my_addr, char*,
282
  private_ike_cfg_t *this)
283
0
{
284
0
  return this->me;
285
0
}
286
287
METHOD(ike_cfg_t, get_other_addr, char*,
288
  private_ike_cfg_t *this)
289
0
{
290
0
  return this->other;
291
0
}
292
293
METHOD(ike_cfg_t, get_my_port, uint16_t,
294
  private_ike_cfg_t *this)
295
0
{
296
0
  return this->my_port;
297
0
}
298
299
METHOD(ike_cfg_t, get_other_port, uint16_t,
300
  private_ike_cfg_t *this)
301
0
{
302
0
  return this->other_port;
303
0
}
304
305
METHOD(ike_cfg_t, get_dscp, uint8_t,
306
  private_ike_cfg_t *this)
307
0
{
308
0
  return this->dscp;
309
0
}
310
311
METHOD(ike_cfg_t, add_proposal, void,
312
  private_ike_cfg_t *this, proposal_t *proposal)
313
0
{
314
0
  if (proposal)
315
0
  {
316
0
    this->proposals->insert_last(this->proposals, proposal);
317
0
  }
318
0
}
319
320
METHOD(ike_cfg_t, get_proposals, linked_list_t*,
321
  private_ike_cfg_t *this, bool log)
322
0
{
323
0
  enumerator_t *enumerator;
324
0
  proposal_t *current;
325
0
  linked_list_t *proposals;
326
327
0
  proposals = linked_list_create();
328
0
  enumerator = this->proposals->create_enumerator(this->proposals);
329
0
  while (enumerator->enumerate(enumerator, &current))
330
0
  {
331
0
    current = current->clone(current, 0);
332
0
    proposals->insert_last(proposals, current);
333
0
  }
334
0
  enumerator->destroy(enumerator);
335
336
0
  if (log)
337
0
  {
338
0
    DBG2(DBG_CFG, "configured proposals: %#P", proposals);
339
0
  }
340
0
  return proposals;
341
0
}
342
343
METHOD(ike_cfg_t, has_proposal, bool,
344
  private_ike_cfg_t *this, proposal_t *match, bool private)
345
0
{
346
0
  enumerator_t *enumerator;
347
0
  proposal_t *proposal;
348
349
0
  enumerator = this->proposals->create_enumerator(this->proposals);
350
0
  while (enumerator->enumerate(enumerator, &proposal))
351
0
  {
352
0
    if (proposal->matches(proposal, match,
353
0
                private ? 0 : PROPOSAL_SKIP_PRIVATE))
354
0
    {
355
0
      enumerator->destroy(enumerator);
356
0
      return TRUE;
357
0
    }
358
0
  }
359
0
  enumerator->destroy(enumerator);
360
0
  return FALSE;
361
0
}
362
363
METHOD(ike_cfg_t, select_proposal, proposal_t*,
364
  private_ike_cfg_t *this, linked_list_t *proposals,
365
  proposal_selection_flag_t flags)
366
0
{
367
0
  return proposal_select(this->proposals, proposals, flags);
368
0
}
369
370
METHOD(ike_cfg_t, get_algorithm, uint16_t,
371
  private_ike_cfg_t *this, transform_type_t type)
372
0
{
373
0
  enumerator_t *enumerator;
374
0
  proposal_t *proposal;
375
0
  uint16_t alg = 0;
376
377
0
  enumerator = this->proposals->create_enumerator(this->proposals);
378
0
  while (enumerator->enumerate(enumerator, &proposal))
379
0
  {
380
0
    if (proposal->get_algorithm(proposal, type, &alg, NULL))
381
0
    {
382
0
      break;
383
0
    }
384
0
  }
385
0
  enumerator->destroy(enumerator);
386
0
  return alg;
387
0
}
388
389
METHOD(ike_cfg_t, equals, bool,
390
  private_ike_cfg_t *this, ike_cfg_t *other_public)
391
0
{
392
0
  private_ike_cfg_t *other = (private_ike_cfg_t*)other_public;
393
394
0
  if (this == other)
395
0
  {
396
0
    return TRUE;
397
0
  }
398
0
  if (this->public.equals != other->public.equals)
399
0
  {
400
0
    return FALSE;
401
0
  }
402
0
  if (!this->proposals->equals_offset(this->proposals, other->proposals,
403
0
                    offsetof(proposal_t, equals)))
404
0
  {
405
0
    return FALSE;
406
0
  }
407
0
  return
408
0
    this->version == other->version &&
409
0
    this->certreq == other->certreq &&
410
0
    this->ocsp_certreq == other->ocsp_certreq &&
411
0
    this->force_encap == other->force_encap &&
412
0
    this->fragmentation == other->fragmentation &&
413
0
    this->childless == other->childless &&
414
0
    streq(this->me, other->me) &&
415
0
    streq(this->other, other->other) &&
416
0
    this->my_port == other->my_port &&
417
0
    this->other_port == other->other_port;
418
0
}
419
420
METHOD(ike_cfg_t, get_ref, ike_cfg_t*,
421
  private_ike_cfg_t *this)
422
0
{
423
0
  ref_get(&this->refcount);
424
0
  return &this->public;
425
0
}
426
427
METHOD(ike_cfg_t, destroy, void,
428
  private_ike_cfg_t *this)
429
0
{
430
0
  if (ref_put(&this->refcount))
431
0
  {
432
0
    this->proposals->destroy_offset(this->proposals,
433
0
                    offsetof(proposal_t, destroy));
434
0
    free(this->me);
435
0
    free(this->other);
436
0
    this->my_hosts->destroy_function(this->my_hosts, free);
437
0
    this->other_hosts->destroy_function(this->other_hosts, free);
438
0
    this->my_ranges->destroy_offset(this->my_ranges,
439
0
                    offsetof(traffic_selector_t, destroy));
440
0
    this->other_ranges->destroy_offset(this->other_ranges,
441
0
                    offsetof(traffic_selector_t, destroy));
442
0
    free(this);
443
0
  }
444
0
}
445
446
/**
447
 * Try to parse a string as subnet
448
 */
449
static traffic_selector_t* make_subnet(char *str)
450
0
{
451
0
  char *pos;
452
453
0
  pos = strchr(str, '/');
454
0
  if (!pos)
455
0
  {
456
0
    return NULL;
457
0
  }
458
0
  return traffic_selector_create_from_cidr(str, 0, 0, 0);
459
0
}
460
461
/**
462
 * Try to parse a string as an IP range
463
 */
464
static traffic_selector_t* make_range(char *str)
465
0
{
466
0
  traffic_selector_t *ts;
467
0
  ts_type_t type;
468
0
  host_t *from, *to;
469
470
0
  if (!host_create_from_range(str, &from, &to))
471
0
  {
472
0
    return NULL;
473
0
  }
474
0
  if (to->get_family(to) == AF_INET)
475
0
  {
476
0
    type = TS_IPV4_ADDR_RANGE;
477
0
  }
478
0
  else
479
0
  {
480
0
    type = TS_IPV6_ADDR_RANGE;
481
0
  }
482
0
  ts = traffic_selector_create_from_bytes(0, type,
483
0
                      from->get_address(from), 0,
484
0
                      to->get_address(to), 0);
485
0
  from->destroy(from);
486
0
  to->destroy(to);
487
0
  return ts;
488
0
}
489
490
/**
491
 * Parse address string into lists of single hosts and ranges/subnets
492
 */
493
static void parse_addresses(char *str, linked_list_t *hosts,
494
              linked_list_t *ranges)
495
0
{
496
0
  enumerator_t *enumerator;
497
0
  traffic_selector_t *ts;
498
499
0
  enumerator = enumerator_create_token(str, ",", " ");
500
0
  while (enumerator->enumerate(enumerator, &str))
501
0
  {
502
0
    ts = make_subnet(str);
503
0
    if (ts)
504
0
    {
505
0
      ranges->insert_last(ranges, ts);
506
0
      continue;
507
0
    }
508
0
    ts = make_range(str);
509
0
    if (ts)
510
0
    {
511
0
      ranges->insert_last(ranges, ts);
512
0
      continue;
513
0
    }
514
0
    hosts->insert_last(hosts, strdup(str));
515
0
  }
516
0
  enumerator->destroy(enumerator);
517
0
}
518
519
/**
520
 * Described in header.
521
 */
522
int ike_cfg_get_family(ike_cfg_t *cfg, bool local)
523
0
{
524
0
  private_ike_cfg_t *this = (private_ike_cfg_t*)cfg;
525
0
  enumerator_t *enumerator;
526
0
  host_t *host;
527
0
  char *str;
528
0
  int family = AF_UNSPEC;
529
530
0
  if (local)
531
0
  {
532
0
    enumerator = this->my_hosts->create_enumerator(this->my_hosts);
533
0
  }
534
0
  else
535
0
  {
536
0
    enumerator = this->other_hosts->create_enumerator(this->other_hosts);
537
0
  }
538
0
  while (enumerator->enumerate(enumerator, &str))
539
0
  {
540
0
    if (streq(str, "%any"))
541
0
    { /* ignore %any as its family is undetermined */
542
0
      continue;
543
0
    }
544
0
    host = host_create_from_string(str, 0);
545
0
    if (host)
546
0
    {
547
0
      if (family == AF_UNSPEC)
548
0
      {
549
0
        family = host->get_family(host);
550
0
      }
551
0
      else if (family != host->get_family(host))
552
0
      {
553
        /* more than one address family defined */
554
0
        family = AF_UNSPEC;
555
0
        host->destroy(host);
556
0
        break;
557
0
      }
558
0
    }
559
0
    DESTROY_IF(host);
560
0
  }
561
0
  enumerator->destroy(enumerator);
562
0
  return family;
563
0
}
564
565
/**
566
 * Described in header.
567
 */
568
bool ike_cfg_has_address(ike_cfg_t *cfg, host_t *addr, bool local)
569
0
{
570
0
  private_ike_cfg_t *this = (private_ike_cfg_t*)cfg;
571
0
  enumerator_t *enumerator;
572
0
  host_t *host;
573
0
  char *str;
574
0
  bool found = FALSE;
575
576
0
  if (local)
577
0
  {
578
0
    enumerator = this->my_hosts->create_enumerator(this->my_hosts);
579
0
  }
580
0
  else
581
0
  {
582
0
    enumerator = this->other_hosts->create_enumerator(this->other_hosts);
583
0
  }
584
0
  while (enumerator->enumerate(enumerator, &str))
585
0
  {
586
0
    host = host_create_from_string(str, 0);
587
0
    if (host && addr->ip_equals(addr, host))
588
0
    {
589
0
      host->destroy(host);
590
0
      found = TRUE;
591
0
      break;
592
0
    }
593
0
    DESTROY_IF(host);
594
0
  }
595
0
  enumerator->destroy(enumerator);
596
0
  return found;
597
0
}
598
599
/*
600
 * Described in header
601
 */
602
ike_cfg_t *ike_cfg_create(ike_cfg_create_t *data)
603
0
{
604
0
  private_ike_cfg_t *this;
605
606
0
  INIT(this,
607
0
    .public = {
608
0
      .get_version = _get_version,
609
0
      .send_certreq = _send_certreq,
610
0
      .send_ocsp_certreq = _send_ocsp_certreq,
611
0
      .force_encap = _force_encap_,
612
0
      .fragmentation = _fragmentation,
613
0
      .childless = _childless,
614
0
      .resolve_me = _resolve_me,
615
0
      .resolve_other = _resolve_other,
616
0
      .match_me = _match_me,
617
0
      .match_other = _match_other,
618
0
      .get_my_addr = _get_my_addr,
619
0
      .get_other_addr = _get_other_addr,
620
0
      .get_my_port = _get_my_port,
621
0
      .get_other_port = _get_other_port,
622
0
      .get_dscp = _get_dscp,
623
0
      .add_proposal = _add_proposal,
624
0
      .get_proposals = _get_proposals,
625
0
      .select_proposal = _select_proposal,
626
0
      .has_proposal = _has_proposal,
627
0
      .get_algorithm = _get_algorithm,
628
0
      .equals = _equals,
629
0
      .get_ref = _get_ref,
630
0
      .destroy = _destroy,
631
0
    },
632
0
    .refcount = 1,
633
0
    .version = data->version,
634
0
    .certreq = !data->no_certreq,
635
0
    .ocsp_certreq = data->ocsp_certreq,
636
0
    .force_encap = data->force_encap,
637
0
    .fragmentation = data->fragmentation,
638
0
    .childless = data->childless,
639
0
    .me = strdup(data->local),
640
0
    .my_ranges = linked_list_create(),
641
0
    .my_hosts = linked_list_create(),
642
0
    .other = strdup(data->remote),
643
0
    .other_ranges = linked_list_create(),
644
0
    .other_hosts = linked_list_create(),
645
0
    .my_port = data->local_port,
646
0
    .other_port = data->remote_port,
647
0
    .dscp = data->dscp,
648
0
    .proposals = linked_list_create(),
649
0
  );
650
651
0
  parse_addresses(data->local, this->my_hosts, this->my_ranges);
652
0
  parse_addresses(data->remote, this->other_hosts, this->other_ranges);
653
654
0
  return &this->public;
655
0
}