File: timer.c

package info (click to toggle)
libreswan 4.3-1%2Bdeb11u4
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 62,688 kB
  • sloc: ansic: 108,293; sh: 25,973; xml: 11,756; python: 10,230; makefile: 1,580; javascript: 1,353; yacc: 825; sed: 647; perl: 584; lex: 159; awk: 156
file content (588 lines) | stat: -rw-r--r-- 16,546 bytes parent folder | download
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
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
/* timer event handling
 *
 * Copyright (C) 1997 Angelos D. Keromytis.
 * Copyright (C) 1998-2001  D. Hugh Redelmeier.
 * Copyright (C) 2005-2008 Michael Richardson <mcr@xelerance.com>
 * Copyright (C) 2008-2010 Paul Wouters <paul@xelerance.com>
 * Copyright (C) 2009 David McCullough <david_mccullough@securecomputing.com>
 * Copyright (C) 2012 Avesh Agarwal <avagarwa@redhat.com>
 * Copyright (C) 2012-2019 Paul Wouters <pwouters@redhat.com>
 * Copyright (C) 2013 Matt Rogers <mrogers@redhat.com>
 * Copyright (C) 2017 Antony Antony <antony@phenome.org>
 * Copyright (C) 2017-2020 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 <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <event2/event.h>
#include <event2/event_struct.h>

#include "sysdep.h"
#include "constants.h"
#include "defs.h"
#include "id.h"
#include "x509.h"
#include "certs.h"
#include "connections.h"	/* needs id.h */
#include "state.h"
#include "packet.h"
#include "demux.h"	/* needs packet.h */
#include "ipsec_doi.h"	/* needs demux.h and state.h */
#include "kernel.h"	/* needs connections.h */
#include "server.h"
#include "log.h"
#include "rnd.h"
#include "timer.h"
#include "whack.h"
#include "ikev1_dpd.h"
#include "ikev2.h"
#include "ikev2_redirect.h"
#include "pending.h" /* for flush_pending_by_connection */
#include "ikev1_xauth.h"
#include "pam_auth.h"
#include "kernel.h"		/* for kernel_ops */
#include "nat_traversal.h"
#include "pluto_sd.h"
#include "retry.h"
#include "fetch.h"		/* for check_crls() */
#include "pluto_stats.h"
#include "iface.h"
#include "ikev2_liveness.h"

struct pluto_event **state_event(struct state *st, enum event_type type)
{
	/*
	 * Return a pointer to the event in the state object.
	 *
	 * XXX: why not just have an array and index it by KIND?
	 */
	switch (type) {
	case EVENT_v2_ADDR_CHANGE:
		return &st->st_addr_change_event;
		break;

	case EVENT_DPD:
	case EVENT_DPD_TIMEOUT:
		return &st->st_dpd_event;

	case EVENT_v2_LIVENESS:
		return &st->st_liveness_event;

	case EVENT_v2_RELEASE_WHACK:
		return &st->st_rel_whack_event;

	case EVENT_v1_SEND_XAUTH:
		return &st->st_send_xauth_event;

	case EVENT_RETRANSMIT:
		return &st->st_retransmit_event;

	case EVENT_SO_DISCARD:
	case EVENT_SA_REKEY:
	case EVENT_SA_REPLACE:
	case EVENT_SA_EXPIRE:
	case EVENT_v1_SA_REPLACE_IF_USED:
	case EVENT_CRYPTO_TIMEOUT:
	case EVENT_PAM_TIMEOUT:
	case EVENT_v2_INITIATE_CHILD:
	case EVENT_v2_REDIRECT:
		/*
		 * Many of these don't make sense - however that's
		 * what happens when (the replaced) default: is used.
		 */
		return &st->st_event;

	case EVENT_RETAIN:
	case EVENT_NULL:
		return NULL;
	}
	bad_case(type);
}

/*
 * This file has the event handling routines. Events are
 * kept as a linked list of event structures. These structures
 * have information like event type, expiration time and a pointer
 * to event specific data (for example, to a state structure).
 */

static event_callback_routine timer_event_cb;
static void timer_event_cb(evutil_socket_t unused_fd UNUSED,
			   const short unused_event UNUSED,
			   void *arg)
{
	threadtime_t inception = threadtime_start();

	/*
	 * Get rid of the old timer event before calling the timer
	 * event processor (was deleting the old timer after calling
	 * the processor giving the impression that the processor's
	 * just created event was being deleted).
	 */
	struct state *st;
	enum event_type type;
	const char *event_name;
	{
		struct pluto_event *ev = arg;
		dbg("%s: processing event@%p", __func__, ev);
		passert(ev != NULL);
		type = ev->ev_type;
		event_name = enum_short_name(&timer_event_names, type);
		st = ev->ev_state;	/* note: *st might be changed; XXX: why? */
		passert(st != NULL);

		dbg("handling event %s for %s state #%lu",
		    enum_show(&timer_event_names, type),
		    (st->st_clonedfrom == SOS_NOBODY) ? "parent" : "child",
		    st->st_serialno);

#if 0
		/*
		 * XXX: this line, which is a merger of the above two
		 * lines, leaks into the expected test output causing
		 * failures.
		 */
		dbg("%s: processing %s-event@%p for %s SA #%lu in state %s",
		    __func__, event_name, ev,
		    IS_IKE_SA(st) ? "IKE" : "CHILD",
		    st->st_serialno, st->st_state->short_name);
#endif

		struct pluto_event **evp = state_event(st, type);
		if (evp == NULL) {
			pexpect_fail(st->st_logger, HERE,
				     "#%lu has no .st_event field for %s",
				     st->st_serialno, enum_name(&timer_event_names, type));
			return;
		}
		if (*evp != ev) {
			pexpect_fail(st->st_logger, HERE,
				     "#%lu .st_event is %p but should be %s-pe@%p",
				     st->st_serialno, *evp,
				     enum_name(&timer_event_names, (*evp)->ev_type),
				     ev);
			return;
		}
		delete_pluto_event(evp);
		arg = ev = *evp = NULL; /* all gone */
	}

	statetime_t start = statetime_backdate(st, &inception);

	/*
	 * Check that st is as expected for the event type.
	 *
	 * For an event type associated with a state, remove the
	 * backpointer from the appropriate slot of the state object.
	 *
	 * We'll eventually either schedule a new event, or delete the
	 * state.
	 */
	switch (type) {

	case EVENT_v2_ADDR_CHANGE:
		dbg("#%lu IKEv2 local address change", st->st_serialno);
		ikev2_addr_change(st);
		break;

	case EVENT_v2_RELEASE_WHACK:
		dbg("%s releasing whack for #%lu %s (sock="PRI_FD")",
		    enum_show(&timer_event_names, type),
		    st->st_serialno,
		    st->st_state->name,
		    pri_fd(st->st_logger->object_whackfd));
		release_pending_whacks(st, "release whack");
		break;

	case EVENT_RETRANSMIT:
		dbg("IKEv%d retransmit event", st->st_ike_version);
		switch (st->st_ike_version) {
		case IKEv2:
			retransmit_v2_msg(st);
			break;
#ifdef USE_IKEv1
		case IKEv1:
			retransmit_v1_msg(st);
			break;
#endif
		default:
			bad_case(st->st_ike_version);
		}
		break;

#ifdef USE_IKEv1
	case EVENT_v1_SEND_XAUTH:
		dbg("XAUTH: event EVENT_v1_SEND_XAUTH #%lu %s",
		    st->st_serialno, st->st_state->name);
		xauth_send_request(st);
		break;
#endif

	case EVENT_v2_INITIATE_CHILD:
		ikev2_child_outI(st);
		break;

	case EVENT_v2_LIVENESS:
		liveness_check(st);
		break;

	case EVENT_SA_REKEY:
		pexpect(st->st_ike_version == IKEv2);
		v2_event_sa_rekey(st);
		break;

	case EVENT_SA_REPLACE:
	case EVENT_v1_SA_REPLACE_IF_USED:
		switch (st->st_ike_version) {
		case IKEv2:
			pexpect(type == EVENT_SA_REPLACE);
			v2_event_sa_replace(st);
			break;
		case IKEv1:
			pexpect(type == EVENT_SA_REPLACE ||
				type == EVENT_v1_SA_REPLACE_IF_USED);
			struct connection *c = st->st_connection;
			const char *satype = IS_IKE_SA(st) ? "IKE" : "CHILD";

			so_serial_t newer_sa = get_newer_sa_from_connection(st);
			if (newer_sa != SOS_NOBODY) {
				/* not very interesting: no need to replace */
				dbg("not replacing stale %s SA %lu; #%lu will do",
				    satype, st->st_serialno, newer_sa);
			} else if (type == EVENT_v1_SA_REPLACE_IF_USED &&
				   !monobefore(mononow(), monotime_add(st->st_outbound_time, c->sa_rekey_margin))) {
				/*
				 * we observed no recent use: no need to replace
				 *
				 * The sampling effects mean that st_outbound_time
				 * could be up to SHUNT_SCAN_INTERVAL more recent
				 * than actual traffic because the sampler looks at
				 * change over that interval.
				 * st_outbound_time could also not yet reflect traffic
				 * in the last SHUNT_SCAN_INTERVAL.
				 * We expect that SHUNT_SCAN_INTERVAL is smaller than
				 * c->sa_rekey_margin so that the effects of this will
				 * be unimportant.
				 * This is just an optimization: correctness is not
				 * at stake.
				 */
				dbg("not replacing stale %s SA: inactive for %jds",
				    satype, deltasecs(monotimediff(mononow(), st->st_outbound_time)));
			} else {
				dbg("replacing stale %s SA",
				    IS_IKE_SA(st) ? "ISAKMP" : "IPsec");
				/*
				 * XXX: this call gets double billed -
				 * both to the state being deleted and
				 * to the new state being created.
				 */
				ipsecdoi_replace(st, 1);
			}

			event_delete(EVENT_v2_LIVENESS, st);
			event_delete(EVENT_DPD, st);
			event_schedule(EVENT_SA_EXPIRE, st->st_replace_margin, st);
			break;
		default:
			bad_case(st->st_ike_version);
		}
		break;

	case EVENT_SA_EXPIRE:
	{
		struct connection *c = st->st_connection;
		const char *satype = IS_IKE_SA(st) ? "IKE" : "CHILD";
		so_serial_t newer_sa = get_newer_sa_from_connection(st);

		if (newer_sa != SOS_NOBODY) {
			/* not very interesting: already superseded */
			dbg("%s SA expired (superseded by #%lu)",
			    satype, newer_sa);
		} else if (!IS_IKE_SA_ESTABLISHED(st)) {
			/* not very interesting: failed IKE attempt */
			dbg("un-established partial CHILD SA timeout (%s)",
			    type == EVENT_SA_EXPIRE ? "SA expired" : "Responder timeout");
			pstat_sa_failed(st, REASON_EXCHANGE_TIMEOUT);
		} else {
			log_state(RC_LOG, st, "%s %s (%s)", satype,
				      type == EVENT_SA_EXPIRE ? "SA expired" : "Responder timeout",
				      (c->policy & POLICY_DONT_REKEY) ?
				      "--dontrekey" : "LATEST!");
		}

		/* Delete this state object.  It must be in the hash table. */
		switch (st->st_ike_version) {
		case IKEv2:
			if (IS_IKE_SA(st)) {
				/* IKEv2 parent, delete children too */
				delete_ike_family(pexpect_ike_sa(st),
						  PROBABLY_SEND_DELETE);
				/* note: no md->st to clear */
			} else {
				struct ike_sa *ike = ike_sa(st, HERE);
				if (ike == NULL) {
					/*
					 * XXX: SNAFU with IKE SA
					 * replacing itself (but not
					 * deleting its children?)
					 * simultaneous to a CHILD SA
					 * failing to establish and
					 * attempting to delete /
					 * replace itself?
					 *
					 * Because these things are
					 * not serialized it is hard
					 * to say.
					 */
					log_state(RC_LOG_SERIOUS, st, "CHILD SA #%lu lost its IKE SA",
					       st->st_serialno);
					delete_state(st);
					st = NULL;
				} else {
					/* note: no md->st to clear */
					passert(st != &ike->sa);
					ikev2_schedule_next_child_delete(st, ike);
					st = NULL;
				}
			}
			break;
		case IKEv1:
			delete_state(st);
			/* note: no md->st to clear */
			/* st = NULL; */
			break;
		default:
			bad_case(st->st_ike_version);
		}
		break;
	}

	case EVENT_SO_DISCARD:
		/*
		 * The state failed to complete within a reasonable
		 * time, or the state failed but was left to live for
		 * a while so re-transmits could work.  Either way,
		 * time to delete it.
		 */
		passert(st != NULL);
		deltatime_t timeout = (st->st_ike_version == IKEv2 ? MAXIMUM_RESPONDER_WAIT_DELAY :
				       st->st_connection->r_timeout);
		deltatime_buf dtb;
		log_state(RC_LOG, st, "deleting incomplete state after %s seconds",
			      str_deltatime(timeout, &dtb));
		/*
		 * If no other reason has been given then this is a
		 * timeout.
		 */
		pstat_sa_failed(st, REASON_EXCHANGE_TIMEOUT);
		/*
		 * XXX: this is scary overkill - delete_state() likes
		 * to resurect things and/or send messages.  What's
		 * needed is a lower-level discard_state() that just
		 * does its job.
		 */
		delete_state(st);
		break;

	case EVENT_v2_REDIRECT:
		initiate_redirect(st);
		break;

#ifdef USE_IKEv1
	case EVENT_DPD:
		dpd_event(st);
		break;

	case EVENT_DPD_TIMEOUT:
		dpd_timeout(st);
		break;

#ifdef AUTH_HAVE_PAM
	case EVENT_PAM_TIMEOUT:
		dbg("PAM thread timeout on state #%lu", st->st_serialno);
		pamauth_abort(st);
		/*
		 * Things get cleaned up when the PAM process exits.
		 *
		 * Should this schedule an event for the case when the
		 * child process (which is SIGKILLed) doesn't exit!?!
		 */
		break;
#endif
#endif
	case EVENT_CRYPTO_TIMEOUT:
		dbg("event crypto_failed on state #%lu, aborting",
		    st->st_serialno);
		pstat_sa_failed(st, REASON_CRYPTO_TIMEOUT);
		delete_state(st);
		/* note: no md->st to clear */
		break;


	default:
		bad_case(type);
	}

	statetime_stop(&start, "%s() %s", __func__, event_name);
}

/*
 * Delete all of the lifetime events (if any).
 *
 * Most lifetime events (things that kill the state) try to share a
 * single .st_event.  However, there has been and likely will be
 * exceptions (for instance the retransmit timer), and the code below
 * is written to deal with it.
 *
 * XXX:
 *
 * The decision to have all the loosely lifetime related timers
 * (retransmit, rekey, replace, ...) share a single .st_event field is
 * ...  unfortunate.  The code has to constantly juggle the field
 * deciding which event is next.  Far easier to set and forget each
 * independently.  This is why the retransmit timer has been split
 * off.
 */
void delete_event(struct state *st)
{
	struct liveness {
		const char *name;
		struct pluto_event **event;
	} events[] = {
		{ "st_event", &st->st_event, },
	};
	for (unsigned e = 0; e < elemsof(events); e++) {
		struct liveness *l = &events[e];
		if (*(l->event) == NULL) {
			dbg("state #%lu has no .%s to delete", st->st_serialno,
			    l->name);
		} else {
			dbg("state #%lu deleting .%s %s",
			    st->st_serialno, l->name,
			    enum_show(&timer_event_names,
				      (*l->event)->ev_type));
			delete_pluto_event(l->event);
		}
	}
}

/*
 * This routine schedules a state event.
 */
void event_schedule(enum event_type type, deltatime_t delay, struct state *st)
{
	passert(st != NULL);
	/*
	 * Scheduling a month into the future is most likely a bug.
	 * pexpect() causes us to flag this in our test cases
	 */
	pexpect(deltasecs(delay) < secs_per_day * 31);

	const char *en = enum_name(&timer_event_names, type);

	struct pluto_event **evp = state_event(st, type);
	if (evp == NULL) {
		pexpect_fail(st->st_logger, HERE,
			     "#%lu has no .st_*event field for %s",
			     st->st_serialno,
			     enum_name(&timer_event_names, type));
		return;
	}
	if (*evp != NULL) {
		/* help debugging by stumbling on */
		pexpect_fail(st->st_logger, HERE,
			     "#%lu already has a scheduled %s; forcing replacement",
			     st->st_serialno,
			     enum_name(&timer_event_names, type));
		delete_pluto_event(evp);
	}

	struct pluto_event *ev = alloc_thing(struct pluto_event, en);
	dbg("%s: newref %s-pe@%p", __func__, en, ev);
	ev->ev_type = type;
	ev->ev_name = en;
	ev->ev_state = st;
	ev->ev_time = monotime_add(mononow(), delay);
	*evp = ev;

	deltatime_buf buf;
	dbg("inserting event %s, timeout in %s seconds for #%lu",
	    en, str_deltatime(delay, &buf),
	    ev->ev_state->st_serialno);

	fire_timer_photon_torpedo(&ev->ev, timer_event_cb, ev, delay);
}

/*
 * Delete a state backlinked event (if any); leave *evp == NULL.
 */
void event_delete(enum event_type type, struct state *st)
{
	struct pluto_event **evp = state_event(st, type);
	if (evp == NULL) {
		pexpect_fail(st->st_logger, HERE,
			     "#%lu has no .st_event field for %s",
			     st->st_serialno, enum_name(&timer_event_names, type));
		return;
	}
	if (*evp != NULL) {
		dbg("#%lu requesting %s-pe@%p be deleted",
		    st->st_serialno, enum_name(&timer_event_names, (*evp)->ev_type), *evp);
		pexpect(st == (*evp)->ev_state);
		delete_pluto_event(evp);
		pexpect((*evp) == NULL);
	};
}

void event_force(enum event_type type, struct state *st)
{
	event_delete(type, st);
	deltatime_t delay = deltatime(0);
	event_schedule(type, delay, st);
}

void call_state_event_inline(struct logger *logger, struct state *st,
			     enum event_type event)
{
	/* sanity checks */
	struct pluto_event **evp = state_event(st, event);
	if (evp == NULL) {
		llog(RC_COMMENT, logger, "%s is not a valid event",
			    enum_name(&timer_event_names, event));
		return;
	}
	if (*evp == NULL) {
		llog(RC_COMMENT, logger, "no handler for %s",
			    enum_name(&timer_event_names, event));
		return;
	}
	if ((*evp)->ev_type != event) {
		llog(RC_COMMENT, logger, "handler for %s is actually %s",
			    enum_name(&timer_event_names, event),
			    enum_name(&timer_event_names, (*evp)->ev_type));
		return;
	}
	/*
	 * XXX: can this kill off the old event when it is still
	 * pending?
	 */
	llog(RC_COMMENT, logger, "calling %s",
		    enum_name(&timer_event_names, event));
	timer_event_cb(0/*sock*/, 0/*event*/, *evp);
}