GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/isakmpd/ui.c Lines: 0 301 0.0 %
Date: 2017-11-07 Branches: 0 195 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: ui.c,v 1.56 2014/12/01 23:05:18 tedu Exp $	 */
2
/* $EOM: ui.c,v 1.43 2000/10/05 09:25:12 niklas Exp $	 */
3
4
/*
5
 * Copyright (c) 1998, 1999, 2000 Niklas Hallqvist.  All rights reserved.
6
 * Copyright (c) 1999, 2000, 2001, 2002 Håkan Olsson.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
28
29
/*
30
 * This code was written under funding by Ericsson Radio Systems.
31
 */
32
33
#include <sys/types.h>
34
#include <sys/socket.h>
35
#include <sys/stat.h>
36
#include <netinet/in.h>
37
#include <arpa/inet.h>
38
#include <fcntl.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <unistd.h>
42
#include <errno.h>
43
44
#include "conf.h"
45
#include "connection.h"
46
#include "doi.h"
47
#include "exchange.h"
48
#include "init.h"
49
#include "isakmp.h"
50
#include "log.h"
51
#include "monitor.h"
52
#include "sa.h"
53
#include "timer.h"
54
#include "transport.h"
55
#include "ui.h"
56
#include "util.h"
57
58
#define BUF_SZ 256
59
60
/* from isakmpd.c */
61
void		 daemon_shutdown_now(int);
62
63
/* Report all SA configuration information. */
64
void		 ui_report_sa(char *);
65
66
static FILE	*ui_open_result(void);
67
68
char		*ui_fifo = FIFO;
69
int		 ui_socket;
70
struct event	*ui_cr_event = NULL;
71
int		 ui_daemon_passive = 0;
72
73
/* Create and open the FIFO used for user control.  */
74
void
75
ui_init(void)
76
{
77
	struct stat     st;
78
79
	/* -f- means control messages comes in via stdin.  */
80
	if (strcmp(ui_fifo, "-") == 0) {
81
		ui_socket = 0;
82
		return;
83
	}
84
85
	/* Don't overwrite a file, i.e '-f /etc/isakmpd/isakmpd.conf'.  */
86
	if (lstat(ui_fifo, &st) == 0) {
87
		if (S_ISREG(st.st_mode)) {
88
			errno = EEXIST;
89
			log_fatal("ui_init: could not create FIFO \"%s\"",
90
			    ui_fifo);
91
		}
92
	}
93
94
	/* No need to know about errors.  */
95
	unlink(ui_fifo);
96
	if (mkfifo(ui_fifo, 0600) == -1)
97
		log_fatal("ui_init: mkfifo (\"%s\", 0600) failed", ui_fifo);
98
99
	ui_socket = open(ui_fifo, O_RDWR | O_NONBLOCK, 0);
100
	if (ui_socket == -1)
101
		log_fatal("ui_init: open (\"%s\", O_RDWR | O_NONBLOCK, 0) "
102
		    "failed", ui_fifo);
103
}
104
105
/*
106
 * Setup a phase 2 connection.
107
 * XXX Maybe phase 1 works too, but teardown won't work then, fix?
108
 */
109
static void
110
ui_connect(char *cmd)
111
{
112
	char	name[201];
113
114
	if (sscanf(cmd, "c %200s", name) != 1) {
115
		log_print("ui_connect: command \"%s\" malformed", cmd);
116
		return;
117
	}
118
	LOG_DBG((LOG_UI, 10, "ui_connect: setup connection \"%s\"", name));
119
	connection_setup(name);
120
}
121
122
/* Tear down a connection, can be phase 1 or 2.  */
123
static void
124
ui_teardown(char *cmd)
125
{
126
	struct sockaddr_in	 addr;
127
	struct sockaddr_in6	 addr6;
128
	struct sa		*sa;
129
	int			 phase;
130
	char			 name[201];
131
132
	/* If no phase is given, we default to phase 2. */
133
	phase = 2;
134
	if (sscanf(cmd, "t main %200s", name) == 1)
135
		phase = 1;
136
	else if (sscanf(cmd, "t quick %200s", name) == 1)
137
		phase = 2;
138
	else if (sscanf(cmd, "t %200s", name) != 1) {
139
		log_print("ui_teardown: command \"%s\" malformed", cmd);
140
		return;
141
	}
142
	LOG_DBG((LOG_UI, 10, "ui_teardown: teardown connection \"%s\", "
143
	    "phase %d", name, phase));
144
145
	bzero(&addr, sizeof(addr));
146
	bzero(&addr6, sizeof(addr6));
147
148
	if (inet_pton(AF_INET, name, &addr.sin_addr) == 1) {
149
		addr.sin_len = sizeof(addr);
150
		addr.sin_family = AF_INET;
151
152
		while ((sa = sa_lookup_by_peer((struct sockaddr *)&addr,
153
		    SA_LEN((struct sockaddr *)&addr), phase)) != 0) {
154
			if (sa->name)
155
				connection_teardown(sa->name);
156
			sa_delete(sa, 1);
157
		}
158
	} else if (inet_pton(AF_INET6, name, &addr6.sin6_addr) == 1) {
159
		addr6.sin6_len = sizeof(addr6);
160
		addr6.sin6_family = AF_INET6;
161
162
		while ((sa = sa_lookup_by_peer((struct sockaddr *)&addr6,
163
		    SA_LEN((struct sockaddr *)&addr6), phase)) != 0) {
164
			if (sa->name)
165
				connection_teardown(sa->name);
166
			sa_delete(sa, 1);
167
		}
168
	} else {
169
		if (phase == 2)
170
			connection_teardown(name);
171
		while ((sa = sa_lookup_by_name(name, phase)) != 0)
172
			sa_delete(sa, 1);
173
	}
174
}
175
176
/* Tear down all phase 2 connections.  */
177
static void
178
ui_teardown_all(char *cmd)
179
{
180
	/* Skip 'cmd' as arg. */
181
	sa_teardown_all();
182
}
183
184
static void
185
ui_conn_reinit_event(void *v)
186
{
187
	/*
188
	 * This event is required for isakmpd to reinitialize the connection
189
	 * and passive-connection lists. Otherwise a change to the
190
	 * "[Phase 2]:Connections" tag will not have any effect.
191
	 */
192
	connection_reinit();
193
194
	ui_cr_event = NULL;
195
}
196
197
static void
198
ui_conn_reinit(void)
199
{
200
	struct timeval tv;
201
202
	if (ui_cr_event)
203
		timer_remove_event(ui_cr_event);
204
205
	gettimeofday(&tv, 0);
206
	tv.tv_sec += 5;
207
208
	ui_cr_event = timer_add_event("ui_conn_reinit", ui_conn_reinit_event,
209
	    0, &tv);
210
	if (!ui_cr_event)
211
		log_print("ui_conn_reinit: timer_add_event() failed. "
212
		    "Connections will not be updated.");
213
}
214
215
/*
216
 * Call the configuration API.
217
 * XXX Error handling!  How to do multi-line transactions?
218
 */
219
static void
220
ui_config(char *cmd)
221
{
222
	struct conf_list *vlist;
223
	struct conf_list_node *vnode;
224
	char	 subcmd[201], section[201], tag[201], value[201], tmp[201];
225
	char	*v, *nv;
226
	int	 trans = 0, items, skip = 0, ret;
227
	FILE	*fp;
228
229
	if (sscanf(cmd, "C %200s", subcmd) != 1)
230
		goto fail;
231
232
	if (strcasecmp(subcmd, "get") == 0) {
233
		if (sscanf(cmd, "C %*s [%200[^]]]:%200s", section, tag) != 2)
234
			goto fail;
235
		v = conf_get_str(section, tag);
236
		fp = ui_open_result();
237
		if (fp) {
238
			if (v)
239
				fprintf(fp, "%s\n", v);
240
			fclose(fp);
241
		}
242
		LOG_DBG((LOG_UI, 30, "ui_config: \"%s\"", cmd));
243
		return;
244
	}
245
246
	trans = conf_begin();
247
	if (strcasecmp(subcmd, "set") == 0) {
248
		items = sscanf(cmd, "C %*s [%200[^]]]:%200[^=]=%200s %200s",
249
		    section, tag, value, tmp);
250
		if (!(items == 3 || items == 4))
251
			goto fail;
252
		conf_set(trans, section, tag, value, items == 4 ? 1 : 0, 0);
253
		if (strcasecmp(section, "Phase 2") == 0 &&
254
		    (strcasecmp(tag, "Connections") == 0 ||
255
			strcasecmp(tag, "Passive-connections") == 0))
256
			ui_conn_reinit();
257
	} else if (strcasecmp(subcmd, "add") == 0) {
258
		items = sscanf(cmd, "C %*s [%200[^]]]:%200[^=]=%200s %200s",
259
		    section, tag, value, tmp);
260
		if (!(items == 3 || items == 4))
261
			goto fail;
262
		v = conf_get_str(section, tag);
263
		if (!v)
264
			conf_set(trans, section, tag, value, 1, 0);
265
		else {
266
			vlist = conf_get_list(section, tag);
267
			if (vlist) {
268
				for (vnode = TAILQ_FIRST(&vlist->fields);
269
				    vnode;
270
				    vnode = TAILQ_NEXT(vnode, link)) {
271
					if (strcmp(vnode->field, value) == 0) {
272
						skip = 1;
273
						break;
274
					}
275
				}
276
				conf_free_list(vlist);
277
			}
278
			/* Add the new value to the end of the 'v' list.  */
279
			if (skip == 0) {
280
				if (asprintf(&nv,
281
				    v[strlen(v) - 1] == ',' ? "%s%s" : "%s,%s",
282
				    v, value) == -1) {
283
					log_error("ui_config: malloc() failed");
284
					if (trans)
285
						conf_end(trans, 0);
286
					return;
287
				}
288
				conf_set(trans, section, tag, nv, 1, 0);
289
				free(nv);
290
			}
291
		}
292
		if (strcasecmp(section, "Phase 2") == 0 &&
293
		    (strcasecmp(tag, "Connections") == 0 ||
294
			strcasecmp(tag, "Passive-connections") == 0))
295
			ui_conn_reinit();
296
	} else if (strcasecmp(subcmd, "rmv") == 0) {
297
		items = sscanf(cmd, "C %*s [%200[^]]]:%200[^=]=%200s %200s",
298
		    section, tag, value, tmp);
299
		if (!(items == 3 || items == 4))
300
			goto fail;
301
		vlist = conf_get_list(section, tag);
302
		if (vlist) {
303
			nv = v = NULL;
304
			for (vnode = TAILQ_FIRST(&vlist->fields);
305
			    vnode;
306
			    vnode = TAILQ_NEXT(vnode, link)) {
307
				if (strcmp(vnode->field, value) == 0)
308
					continue;
309
				ret = v ?
310
				    asprintf(&nv, "%s,%s", v, vnode->field) :
311
				    asprintf(&nv, "%s", vnode->field);
312
				free(v);
313
				if (ret == -1) {
314
					log_error("ui_config: malloc() failed");
315
					if (trans)
316
						conf_end(trans, 0);
317
					return;
318
				}
319
				v = nv;
320
			}
321
			conf_free_list(vlist);
322
			if (nv) {
323
				conf_set(trans, section, tag, nv, 1, 0);
324
				free(nv);
325
			} else {
326
				conf_remove(trans, section, tag);
327
			}
328
		}
329
		if (strcasecmp(section, "Phase 2") == 0 &&
330
		    (strcasecmp(tag, "Connections") == 0 ||
331
			strcasecmp(tag, "Passive-connections") == 0))
332
			ui_conn_reinit();
333
	} else if (strcasecmp(subcmd, "rm") == 0) {
334
		if (sscanf(cmd, "C %*s [%200[^]]]:%200s", section, tag) != 2)
335
			goto fail;
336
		conf_remove(trans, section, tag);
337
	} else if (strcasecmp(subcmd, "rms") == 0) {
338
		if (sscanf(cmd, "C %*s [%200[^]]]", section) != 1)
339
			goto fail;
340
		conf_remove_section(trans, section);
341
	} else
342
		goto fail;
343
344
	LOG_DBG((LOG_UI, 30, "ui_config: \"%s\"", cmd));
345
	conf_end(trans, 1);
346
	return;
347
348
fail:
349
	if (trans)
350
		conf_end(trans, 0);
351
	log_print("ui_config: command \"%s\" malformed", cmd);
352
}
353
354
static void
355
ui_delete(char *cmd)
356
{
357
	char            cookies_str[ISAKMP_HDR_COOKIES_LEN * 2 + 1];
358
	char            message_id_str[ISAKMP_HDR_MESSAGE_ID_LEN * 2 + 1];
359
	u_int8_t        cookies[ISAKMP_HDR_COOKIES_LEN];
360
	u_int8_t        message_id_buf[ISAKMP_HDR_MESSAGE_ID_LEN];
361
	u_int8_t       *message_id = message_id_buf;
362
	struct sa      *sa;
363
364
	if (sscanf(cmd, "d %32s %8s", cookies_str, message_id_str) != 2) {
365
		log_print("ui_delete: command \"%s\" malformed", cmd);
366
		return;
367
	}
368
	if (strcmp(message_id_str, "-") == 0)
369
		message_id = 0;
370
371
	if (hex2raw(cookies_str, cookies, ISAKMP_HDR_COOKIES_LEN) == -1 ||
372
	    (message_id && hex2raw(message_id_str, message_id_buf,
373
	    ISAKMP_HDR_MESSAGE_ID_LEN) == -1)) {
374
		log_print("ui_delete: command \"%s\" has bad arguments", cmd);
375
		return;
376
	}
377
	sa = sa_lookup(cookies, message_id);
378
	if (!sa) {
379
		log_print("ui_delete: command \"%s\" found no SA", cmd);
380
		return;
381
	}
382
	LOG_DBG((LOG_UI, 20,
383
	    "ui_delete: deleting SA for cookie \"%s\" msgid \"%s\"",
384
	    cookies_str, message_id_str));
385
	sa_delete(sa, 1);
386
}
387
388
/* Parse the debug command found in CMD.  */
389
static void
390
ui_debug(char *cmd)
391
{
392
	int             cls, level;
393
	char            subcmd[3];
394
395
	if (sscanf(cmd, "D %d %d", &cls, &level) == 2) {
396
		log_debug_cmd(cls, level);
397
		return;
398
	} else if (sscanf(cmd, "D %2s %d", subcmd, &level) == 2) {
399
		switch (subcmd[0]) {
400
		case 'A':
401
			for (cls = 0; cls < LOG_ENDCLASS; cls++)
402
				log_debug_cmd(cls, level);
403
			return;
404
		}
405
	} else if (sscanf(cmd, "D %2s", subcmd) == 1) {
406
		switch (subcmd[0]) {
407
		case 'T':
408
			log_debug_toggle();
409
			return;
410
		}
411
	}
412
	log_print("ui_debug: command \"%s\" malformed", cmd);
413
}
414
415
static void
416
ui_packetlog(char *cmd)
417
{
418
	char	subcmd[201];
419
420
	if (sscanf(cmd, "p %200s", subcmd) != 1)
421
		goto fail;
422
423
	if (strncasecmp(subcmd, "on=", 3) == 0) {
424
		/* Start capture to a new file.  */
425
		if (subcmd[strlen(subcmd) - 1] == '\n')
426
			subcmd[strlen(subcmd) - 1] = 0;
427
		log_packet_restart(subcmd + 3);
428
	} else if (strcasecmp(subcmd, "on") == 0)
429
		log_packet_restart(NULL);
430
	else if (strcasecmp(subcmd, "off") == 0)
431
		log_packet_stop();
432
	return;
433
434
fail:
435
	log_print("ui_packetlog: command \"%s\" malformed", cmd);
436
}
437
438
static void
439
ui_shutdown_daemon(char *cmd)
440
{
441
	if (strlen(cmd) == 1) {
442
		log_print("ui_shutdown_daemon: received shutdown command");
443
		daemon_shutdown_now(0);
444
	} else
445
		log_print("ui_shutdown_daemon: command \"%s\" malformed", cmd);
446
}
447
448
/* Report SAs and ongoing exchanges.  */
449
void
450
ui_report(char *cmd)
451
{
452
	/* XXX Skip 'cmd' as arg? */
453
	sa_report();
454
	exchange_report();
455
	transport_report();
456
	connection_report();
457
	timer_report();
458
	conf_report();
459
}
460
461
/* Report all SA configuration information.  */
462
void
463
ui_report_sa(char *cmd)
464
{
465
	FILE *fp = ui_open_result();
466
467
	/* Skip 'cmd' as arg? */
468
	if (!fp)
469
		return;
470
471
	sa_report_all(fp);
472
473
	fclose(fp);
474
}
475
476
static void
477
ui_setmode(char *cmd)
478
{
479
	char	arg[11];
480
481
	if (sscanf(cmd, "M %10s", arg) != 1)
482
		goto fail;
483
	if (strncmp(arg, "active", 6) == 0) {
484
		if (ui_daemon_passive)
485
			LOG_DBG((LOG_UI, 20,
486
			    "ui_setmode: switching to active mode"));
487
		ui_daemon_passive = 0;
488
	} else if (strncmp(arg, "passive", 7) == 0) {
489
		if (!ui_daemon_passive)
490
			LOG_DBG((LOG_UI, 20,
491
			    "ui_setmode: switching to passive mode"));
492
		ui_daemon_passive = 1;
493
	} else
494
		goto fail;
495
	return;
496
497
  fail:
498
	log_print("ui_setmode: command \"%s\" malformed", cmd);
499
}
500
501
502
/*
503
 * Call the relevant command handler based on the first character of the
504
 * line (the command).
505
 */
506
static void
507
ui_handle_command(char *line)
508
{
509
	/* Find out what one-letter command was sent.  */
510
	switch (line[0]) {
511
	case 'c':
512
		ui_connect(line);
513
		break;
514
515
	case 'C':
516
		ui_config(line);
517
		break;
518
519
	case 'd':
520
		ui_delete(line);
521
		break;
522
523
	case 'D':
524
		ui_debug(line);
525
		break;
526
527
	case 'M':
528
		ui_setmode(line);
529
		break;
530
531
	case 'p':
532
		ui_packetlog(line);
533
		break;
534
535
	case 'Q':
536
		ui_shutdown_daemon(line);
537
		break;
538
539
	case 'R':
540
		reinit();
541
		break;
542
543
	case 'S':
544
		ui_report_sa(line);
545
		break;
546
547
	case 'r':
548
		ui_report(line);
549
		break;
550
551
	case 't':
552
		ui_teardown(line);
553
		break;
554
555
	case 'T':
556
		ui_teardown_all(line);
557
		break;
558
559
	default:
560
		log_print("ui_handle_messages: unrecognized command: '%c'",
561
		    line[0]);
562
	}
563
}
564
565
/*
566
 * A half-complex implementation of reading from a file descriptor
567
 * line by line without resorting to stdio which apparently have
568
 * troubles with non-blocking fifos.
569
 */
570
void
571
ui_handler(void)
572
{
573
	static char    *buf = 0;
574
	static char    *p;
575
	static size_t   sz;
576
	static size_t   resid;
577
	ssize_t         n;
578
	char           *new_buf;
579
580
	/* If no buffer, set it up.  */
581
	if (!buf) {
582
		sz = BUF_SZ;
583
		buf = malloc(sz);
584
		if (!buf) {
585
			log_print("ui_handler: malloc (%lu) failed",
586
			    (unsigned long)sz);
587
			return;
588
		}
589
		p = buf;
590
		resid = sz;
591
	}
592
	/* If no place left in the buffer reallocate twice as large.  */
593
	if (!resid) {
594
		new_buf = reallocarray(buf, sz, 2);
595
		if (!new_buf) {
596
			log_print("ui_handler: realloc (%p, %lu) failed", buf,
597
			    (unsigned long)sz * 2);
598
			free(buf);
599
			buf = 0;
600
			return;
601
		}
602
		buf = new_buf;
603
		p = buf + sz;
604
		resid = sz;
605
		sz *= 2;
606
	}
607
	n = read(ui_socket, p, resid);
608
	if (n == -1) {
609
		log_error("ui_handler: read (%d, %p, %lu)", ui_socket, p,
610
		    (unsigned long)resid);
611
		return;
612
	}
613
	if (!n)
614
		return;
615
	resid -= n;
616
	while (n--) {
617
		/*
618
		 * When we find a newline, cut off the line and feed it to the
619
		 * command processor.  Then move the rest up-front.
620
		 */
621
		if (*p == '\n') {
622
			*p = '\0';
623
			ui_handle_command(buf);
624
			memmove(buf, p + 1, n);
625
			p = buf;
626
			resid = sz - n;
627
			continue;
628
		}
629
		p++;
630
	}
631
}
632
633
static FILE *
634
ui_open_result(void)
635
{
636
	FILE *fp = monitor_fopen(RESULT_FILE, "w");
637
638
	if (!fp)
639
		log_error("ui_open_result: fopen() failed");
640
	return fp;
641
}