File: ikev2_replace.c

package info (click to toggle)
libreswan 5.2-2.3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 81,644 kB
  • sloc: ansic: 129,988; sh: 32,018; xml: 20,646; python: 10,303; makefile: 3,022; javascript: 1,506; sed: 574; yacc: 511; perl: 264; awk: 52
file content (263 lines) | stat: -rw-r--r-- 7,880 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/* IKEv2 replace, for libreswan
 *
 * Copyright (C) 1997 Angelos D. Keromytis.
 * Copyright (C) 1998-2002,2010-2017 D. Hugh Redelmeier <hugh@mimosa.com>
 * Copyright (C) 2003-2006  Michael Richardson <mcr@xelerance.com>
 * Copyright (C) 2003-2011 Paul Wouters <paul@xelerance.com>
 * Copyright (C) 2010-2011 Tuomo Soini <tis@foobar.fi>
 * Copyright (C) 2009 Avesh Agarwal <avagarwa@redhat.com>
 * Copyright (C) 2012-2018 Paul Wouters <pwouters@redhat.com>
 * Copyright (C) 2013 David McCullough <ucdevel@gmail.com>
 * Copyright (C) 2013 Matt Rogers <mrogers@redhat.com>
 * Copyright (C) 2014-2022 Andrew Cagney
 * Copyright (C) 2017-2018 Antony Antony <antony@phenome.org>
 * Copyright (C) 2017 Mayank Totale <mtotale@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.  See <https://www.gnu.org/licenses/gpl2.txt>.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

#include "defs.h"
#include "state.h"
#include "ipsec_doi.h"
#include "log.h"
#include "ikev2.h"
#include "ikev2_replace.h"
#include "timer.h"
#include "connections.h"
#include "ikev2_ike_sa_init.h"
#include "initiate.h"
#include "ikev2_create_child_sa.h"
#include "kernel.h"		/* for was_eroute_idle() */

/*
 * For opportunistic IPsec, we want to delete idle connections, so we
 * are not gaining an infinite amount of unused IPsec SAs.
 *
 * NOTE: Soon we will accept an idletime= configuration option that
 * replaces this check.
 *
 * Only replace the SA when it's been in use (checking for in-use is a
 * separate operation).
 */

static bool expire_ike_because_child_not_used(struct state *st)
{
	if (!(IS_IKE_SA_ESTABLISHED(st) ||
	      IS_CHILD_SA_ESTABLISHED(st))) {
		/* for instance, too many retransmits trigger replace */
		return false;
	}

	struct connection *c = st->st_connection;

	if (!is_opportunistic(c)) {
		/* killing idle IPsec SA's is only for opportunistic SA's */
		return false;
	}

	if (nr_child_leases(c->remote) > 0) {
		llog_pexpect(st->logger, HERE,
			     "#%lu has lease; should not be trying to replace",
			     st->st_serialno);
		return true;
	}

	/* see of (most recent) child is busy */
	struct child_sa *child;
	struct ike_sa *ike;
	if (IS_IKE_SA(st)) {
		ike = pexpect_ike_sa(st);
		child = child_sa_by_serialno(c->established_child_sa);
		if (child == NULL) {
			llog_pexpect(st->logger, HERE,
				     "can't check usage as IKE SA #%lu has no newest child",
				     ike->sa.st_serialno);
			return true;
		}
	} else {
		child = pexpect_child_sa(st);
		ike = ike_sa(st, HERE);
	}

	dbg(PRI_SO" check last used on newest CHILD SA "PRI_SO,
	    ike->sa.st_serialno, child->sa.st_serialno);

	/* not sure why idleness is set to rekey margin? */
	if (was_eroute_idle(child, c->config->sa_rekey_margin)) {
		/* we observed no traffic, let IPSEC SA and IKE SA expire */
		dbg("expiring IKE SA "PRI_SO" as CHILD SA "PRI_SO" has been idle for more than %jds",
		    ike->sa.st_serialno,
		    child->sa.st_serialno,
		    deltasecs(c->config->sa_rekey_margin));
		return true;
	}
	return false;
}

static bool v2_state_is_expired(struct state *st, const char *verb)
{
	struct ike_sa *ike = ike_sa(st, HERE);
	if (ike == NULL) {
		/*
		 * An IKE SA must return itself so NULL implies a
		 * parentless child.
		 *
		 * Even it is decided that Child SAs can linger after
		 * the IKE SA has gone they shouldn't be getting
		 * rekeys!
		 */
		llog_pexpect(st->logger, HERE,
			     "not %s Child SA #%lu; as IKE SA #%lu has diasppeared",
			     verb, st->st_serialno, st->st_clonedfrom);
		event_force(EVENT_v2_EXPIRE, st);
		return true;
	}

	if (expire_ike_because_child_not_used(st)) {
		struct ike_sa *ike = ike_sa(st, HERE);
		event_force(EVENT_v2_EXPIRE, &ike->sa);
		return true;
	}

	so_serial_t newer_sa = get_newer_sa_from_connection(st);
	if (newer_sa != SOS_NOBODY) {
		/*
		 * A newer SA implies that this SA has already been
		 * successfully replaced (it's only set when the newer
		 * SA establishes).
		 *
		 * Two ways this can happen:
		 *
		 * + the SA should have been expired at the same time
		 * as the new SA was established; but wasn't
		 *
		 * + this and the peer established the same SA in
		 * parallel, aka crossing the streams; the two SAs are
		 * allowed to linger until one is clearly obsolete;
		 * see github/699
		 *
		 * either way expire the SA now
		 */
		const char *satype = IS_IKE_SA(st) ? "IKE" : "Child";
#if 0
		llog_pexpect(st->logger, HERE,
			     "not %s stale %s SA #%lu; as already got a newer #%lu",
			     verb, satype, st->st_serialno, newer_sa);
#else
		log_state(RC_LOG, st,
			  "not %s stale %s SA #%lu; as already got a newer #%lu",
			  verb, satype, st->st_serialno, newer_sa);
#endif
		event_force(EVENT_v2_EXPIRE, st);
		return true;
	}

	return false;
}

/* Replace SA with a fresh one that is similar
 *
 * Shares some logic with ipsecdoi_initiate, but not the same!
 * - we must not reuse the ISAKMP SA if we are trying to replace it!
 * - if trying to replace IPSEC SA, use ipsecdoi_initiate to build
 *   ISAKMP SA if needed.
 * - duplicate whack fd, if live.
 * Does not delete the old state -- someone else will do that.
 */

void ikev2_replace(struct state *st, bool detach_whack)
{
	/*
	 * start billing the new state.  The old state also gets
	 * billed for this function call, oops.
	 */
	threadtime_t inception = threadtime_start();

	if (IS_IKE_SA(st)) {
		/*
		 * Should this call capture_child_rekey_policy(st) or
		 * child_sa_policy(c) to capture the Child SA's
		 * policy?
		 *
		 * Probably not.
		 *
		 * When the IKE (ISAKMP) SA initiator code sees
		 * policy=LEMPTY it skips scheduling the connection as
		 * a Child SA to be initiated once the IKE SA
		 * establishes.  Instead the revival code will
		 * schedule the connection as a child.
		 */
		struct connection *c = st->st_connection;
		const struct child_policy policy = {0};
		if (IS_IKE_SA_ESTABLISHED(st)) {
			log_state(RC_LOG, st, "initiate reauthentication of IKE SA");
		}
		initiate_v2_IKE_SA_INIT_request(c, st, &policy, &inception,
						HUNK_AS_SHUNK(c->child.sec_label),
						detach_whack);

	} else {

		/*
		 * Start from policy in (ipsec) state, not connection.
		 * This ensures that rekeying doesn't downgrade
		 * security.  I admit that this doesn't capture
		 * everything.
		 */
		const struct child_policy policy = capture_child_rekey_policy(st);
		initiate(st->st_connection, &policy, st->st_serialno, &inception,
			 null_shunk, detach_whack, st->logger,
			 INITIATED_BY_REPLACE, HERE);
	}
}

void event_v2_replace(struct state *st, bool detach_whack)
{
	if (v2_state_is_expired(st, "replace")) {
		return;
	}

	ldbg(st->logger, "replacing stale %s", state_sa_name(st));

	ikev2_replace(st, detach_whack);

	/*
	 * XXX: this kills the existing state, which means the
	 * 'replace' is never smooth.
	 */
	event_force(EVENT_v2_EXPIRE, st);
}

void event_v2_rekey(struct state *st, bool detach_whack)
{
	if (v2_state_is_expired(st, "rekey")) {
		return;
	}

	struct ike_sa *ike = ike_sa(st, HERE);
	if (PBAD(st->logger, ike == NULL)) {
		return;
	}

	struct child_sa *larval_sa;
	if (IS_IKE_SA(st)) {
		larval_sa = submit_v2_CREATE_CHILD_SA_rekey_ike(ike, detach_whack);
	} else {
		larval_sa = submit_v2_CREATE_CHILD_SA_rekey_child(ike, pexpect_child_sa(st),
								  detach_whack);
	}

	llog(RC_LOG, larval_sa->sa.logger,
	     "initiating rekey to replace %s "PRI_SO" using IKE SA "PRI_SO,
	     state_sa_name(st),
	     pri_so(st->st_serialno),
	     pri_so(ike->sa.st_serialno));
}