File: ikev1_host_pair.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 (399 lines) | stat: -rw-r--r-- 11,684 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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
/* information about connections between hosts
 *
 * Copyright (C) 1998-2002,2013 D. Hugh Redelmeier <hugh@mimosa.com>
 * Copyright (C) 2007 Michael Richardson <mcr@xelerance.com>
 * Copyright (C) 2007 Ken Bantoft <ken@xelerance.com>
 * Copyright (C) 2008-2010 Paul Wouters <paul@xelerance.com>
 * Copyright (C) 2010 Tuomo Soini <tis@foobar.fi>
 * Copyright (C) 2011 Avesh Agarwal <avagarwa@redhat.com>
 * Copyright (C) 2012 Paul Wouters <paul@libreswan.org>
 * Copyright (C) 2013-2019 Paul Wouters <pwouters@redhat.com>
 * Copyright (C) 2019 Andrew Cagney <cagney@gnu.org>
 *
 * 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 "ikev1_host_pair.h"
#include "log.h"
#include "connections.h"
#include "demux.h"
#include "iface.h"
#include "ikev1_spdb.h"
#include "instantiate.h"
#include "orient.h"

struct host_pair_policy {
	struct authby authby;
	bool xauth;
	bool aggressive;
	const struct id *peer_id;
};

typedef bool match_host_pair_policy_fn(const struct connection *d,
				       const struct host_pair_policy *context,
				       struct logger *logger);

static match_host_pair_policy_fn match_v1_connection;

static struct connection *find_host_pair_connection_on_responder(const struct ike_info *ike_info,
								 const ip_address local,
								 const ip_address remote,
								 match_host_pair_policy_fn *match_policy,
								 const struct host_pair_policy *context,
								 struct logger *logger);

static bool match_v1_connection(const struct connection *c,
				const struct host_pair_policy *hpc,
				struct logger *logger)
{
	PEXPECT(c->logger, c->config->ike_version == IKEv1);
	PEXPECT(c->logger, oriented(c));
	PEXPECT(logger, !is_group(c));

	if (is_instance(c) && c->remote->host.id.kind == ID_NULL) {
		connection_buf cb;
		ldbg(logger, "  skipping "PRI_CONNECTION", ID_NULL instance",
		     pri_connection(c, &cb));
		return false;
	}

	if (never_negotiate(c)) {
		/* are we a block or clear connection? */
		enum shunt_policy shunt = c->config->never_negotiate_shunt;
		PASSERT(logger, shunt != SHUNT_UNSET); /* since never-negotiate */
		/*
		 * We need to match block/clear so we can send back
		 * NO_PROPOSAL_CHOSEN, otherwise not match so we can
		 * hit packetdefault to do real IKE.
		 *
		 * clear and block do not have POLICY_OPPORTUNISTIC,
		 * but clear-or-private and private-or-clear do, but
		 * they don't do IKE themselves but allow
		 * packetdefault to be hit and do the work.  if not
		 * policy_oppo -> we hit clear/block so this is right
		 * c
		 *
		 * XXX: er, this isn't a skip!
		 *
		 * shunt match - stop the search for another conn if
		 * we are groupinstance.
		 */
		if (is_group_instance(c)) {
			connection_buf cb;
			ldbg(logger, "  choosing "PRI_CONNECTION", never negotiate + group instance",
			     pri_connection(c, &cb));
			return true;
		}

		connection_buf cb;
		ldbg(logger, "  skipping "PRI_CONNECTION", never negotiate",
		     pri_connection(c, &cb));
		return false;
	}

	/*
	 * Success may require exact match of:
	 * (1) XAUTH (POLICY_XAUTH)
	 * (2) kind of IKEV1 (POLICY_AGGRESSIVE)
	 * (3) IKE_VERSION
	 * So if any bits are on in the exclusive OR, we fail.
	 * Each of our callers knows what is known so specifies
	 * the policy_exact_mask.
	 */
	if (hpc->xauth != is_xauth(c)) {
		connection_buf cb;
		ldbg(logger, "  skipping "PRI_CONNECTION", exact match POLICY_XAUTH failed",
		     pri_connection(c, &cb));
		return false;
	}
	if (hpc->aggressive != c->config->aggressive) {
		connection_buf cb;
		ldbg(logger, "  skipping "PRI_CONNECTION", exact match POLICY_AGGRESSIVE failed",
		     pri_connection(c, &cb));
		return false;
	}

	if (hpc->peer_id != NULL && !same_id(hpc->peer_id, &c->remote->host.id) &&
	    (c->remote->host.id.kind != ID_FROMCERT && !id_is_any(&c->remote->host.id))) {
		connection_buf cb;
		ldbg(logger, "  skipping "PRI_CONNECTION", peer_id failed",
		     pri_connection(c, &cb));
		return false; /* incompatible ID */
	}

	/*
	 * Check that the proposed authby matches the connection's
	 * auth (IKEv1 only does one auth per connection) so match
	 * needs to be exact.
	 *
	 * Order matters.  First match, be it RSA or PSK is accepted.
	 */
	switch (c->remote->host.config->auth) {
	case AUTH_RSASIG:
		if (!hpc->authby.rsasig) {
			connection_buf cb;
			ldbg(logger, "  skipping "PRI_CONNECTION", RSASIG was not proposed",
			     pri_connection(c, &cb));
			return false;
		}
		break;
	case AUTH_PSK:
		if (!hpc->authby.psk) {
			connection_buf cb;
			ldbg(logger, "  skipping "PRI_CONNECTION", PSK was not proposed",
			     pri_connection(c, &cb));
			return false;
		}
		break;
	default:
	{
		connection_buf cb;
		enum_buf eb;
		ldbg(logger, "  skipping "PRI_CONNECTION", %s is never proposed",
		     pri_connection(c, &cb),
		     str_enum(&keyword_auth_names, c->remote->host.config->auth, &eb));
		return false;
	}
	}

	return true;
}

struct connection *find_host_pair_connection_on_responder(const struct ike_info *ike_info,
							  const ip_address local_address,
							  const ip_address remote_address,
							  match_host_pair_policy_fn *match_connection_policy,
							  const struct host_pair_policy *context,
							  struct logger *logger)
{
	address_buf lb;
	address_buf rb;
	ldbg(logger, "%s() %s %s->%s", __func__,
	     ike_info->version_name,
	     str_address(&remote_address, &rb),
	     str_address(&local_address, &lb));

	struct connection *c = NULL;

	struct connection_filter hpf = {
		.host_pair = {
			.local = &local_address,
			.remote = &remote_address,
		},
		.ike_version = ike_info->version,
		.search = {
			.order = OLD2NEW,
			.verbose.logger = logger,
			.where = HERE,
		},
	};
	while (next_connection(&hpf)) {
		struct connection *d = hpf.c;

		if (!match_connection_policy(d, context, logger)){
			continue;
		}

		/*
		 * This could be a shared ISAKMP SA connection, in
		 * which case we prefer to find the connection that
		 * has the ISAKMP SA.
		 */
		if (d->established_ike_sa != SOS_NOBODY) {
			/* instant winner */
			c = d;
			break;
		}
		if (c == NULL) {
			c = d;
		}
	}

	return c;
}

/*
 * find_host_connection: find the first satisfactory connection
 *	with this pair of hosts.
 *
 * find_next_host_connection: find the next satisfactory connection
 *	Starts where find_host_connection left off.
 *	NOTE: it will return its argument; if you want to
 *	advance, use c->hp_next.
 *
 * We start with the list that find_host_pair_connections would yield
 * but we narrow the selection.
 *
 * We only yield a connection that can negotiate.
 *
 * The caller can specify policy requirements as
 * req_policy and policy_exact_mask.
 *
 * All policy bits found in req_policy must be in the
 * policy of the connection.
 *
 * For all bits in policy_exact mask, the req_policy
 * and connection's policy must be equal.  Likely candidates:
 * - XAUTH (POLICY_XAUTH)
 * - kind of IKEV1 (POLICY_AGGRESSIVE)
 * These should only be used if the caller actually knows
 * the exact value and has included it in req_policy.
 */

/*
 * Always returns a new reference.
 */
struct connection *find_v1_aggr_mode_connection(struct msg_digest *md,
						struct authby authby,
						bool policy_xauth,
						const struct id *peer_id)
{
	struct connection *c;

	struct host_pair_policy host_pair_policy = {
		.authby = authby,
		.xauth = policy_xauth,
		.aggressive = true,
		.peer_id = peer_id,
	};

	ip_address local_address = md->iface->ip_dev->local_address;
	ip_address remote_address = endpoint_address(md->sender);
	c = find_host_pair_connection_on_responder(&ikev1_info,
						   local_address, remote_address,
						   match_v1_connection,
						   &host_pair_policy,
						   md->logger);
	if (c != NULL) {
		return connection_addref(c, md->logger);
	}

	c = find_host_pair_connection_on_responder(&ikev1_info,
						   local_address, unset_address,
						   match_v1_connection,
						   &host_pair_policy,
						   md->logger);
	if (c != NULL) {
		/*
		 * Create a temporary connection that is a copy of
		 * this one.  Peers ID isn't declared yet.
		 */
		return rw_responder_instantiate(c, remote_address, HERE);
	}

	endpoint_buf b;
	authby_buf pb;
	llog(RC_LOG, md->logger,
	     "initial Aggressive Mode message from %s but no (wildcard) connection has been configured with authby %s",
	     str_endpoint(&md->sender, &b),
	     str_authby(authby, &pb));

	return NULL;
}

struct connection *find_v1_main_mode_connection(struct msg_digest *md)
{
	struct connection *c;

	/* random source ports are handled by find_host_connection */

	struct payload_digest *const sa_pd = md->chain[ISAKMP_NEXT_SA];
	struct authby authby = {0};
	bool policy_xauth = false;
	diag_t d = preparse_isakmp_sa_body(sa_pd->pbs, &authby, &policy_xauth);
	if (d != NULL) {
		llog(RC_LOG, md->logger,
		     "initial Main Mode message has corrupt SA payload: %s",
		     str_diag(d));
		pfree_diag(&d);
		return NULL;
	}

	/*
	 * This call does not try to match authentication
	 * (preparse_isakmp_sa_body() isn't called).  Hence LEMPTY fir
	 * policy and FALSE for exact_match_POLICY_XAUTH - neither of
	 * these are known.
	 *
	 * Why?
	 */

	struct host_pair_policy host_pair_policy = {
		.authby = authby,
		.xauth = policy_xauth,
		.aggressive = false,
		.peer_id = NULL,  /* peer ID not known yet */
	};

	ip_address local_address = md->iface->ip_dev->local_address;
	ip_address remote_address = endpoint_address(md->sender);
	c = find_host_pair_connection_on_responder(&ikev1_info,
						   local_address, remote_address,
						   match_v1_connection,
						   &host_pair_policy,
						   md->logger);
	if (c != NULL) {
		/*
		 * we found a non %any conn. double check if it needs
		 * instantiation anyway (eg vnet=)
		 */
		if (is_template(c)) {
			ldbg(md->logger, "local endpoint needs instantiation");
			return rw_responder_instantiate(c, remote_address, HERE);
		}

		return connection_addref(c, md->logger);
	}

	/*
	 * Other IKE clients, such as strongswan, send the XAUTH VID
	 * even for connections they do not want to run XAUTH on.  We
	 * need to depend on the policy negotiation, not the VID.  So
	 * we ignore md->quirks.xauth_vid
	 */

	/*
	 * See if a wildcarded connection can be found.  We cannot
	 * pick the right connection, so we're making a guess.  All
	 * Road Warrior connections are fair game: we pick the first
	 * we come across (if any).
	 */

	c = find_host_pair_connection_on_responder(&ikev1_info,
						   local_address, unset_address,
						   match_v1_connection,
						   &host_pair_policy,
						   md->logger);
	if (c == NULL) {
		authby_buf ab;
		llog(RC_LOG, md->logger,
		     "initial Main Mode message received but no connection has been authorized with authby=%s and xauth=%s",
		     str_authby(authby, &ab), bool_str(policy_xauth));
		/* XXX notification is in order! */
		return NULL;
	}

	if (!PEXPECT(md->logger, is_template(c))) {
		return NULL;
	}

	/*
	 * Create a temporary connection that is a copy of this
	 * template.
	 *
	 * The initiator's ID isn't yet known.
	 */
	connection_buf cib;
	ldbg(md->logger, "instantiating "PRI_CONNECTION" for initial Main Mode message",
	     pri_connection(c, &cib));
	return rw_responder_instantiate(c, remote_address, HERE);
}