1 /* $OpenBSD: ui.c,v 1.45 2005/04/08 22:32:10 cloder 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/stat.h>
35 #include <fcntl.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <errno.h>
40 
41 #include "conf.h"
42 #include "connection.h"
43 #include "doi.h"
44 #include "exchange.h"
45 #include "init.h"
46 #include "isakmp.h"
47 #include "log.h"
48 #include "monitor.h"
49 #include "sa.h"
50 #include "timer.h"
51 #include "transport.h"
52 #include "ui.h"
53 #include "util.h"
54 
55 #define BUF_SZ 256
56 
57 /* from isakmpd.c */
58 void		 daemon_shutdown_now(int);
59 
60 /* Report all SA configuration information. */
61 void		 ui_report_sa(char *);
62 
63 static FILE	*ui_open_result(void);
64 
65 char		*ui_fifo = FIFO;
66 int		 ui_socket;
67 struct event	*ui_cr_event = NULL;
68 
69 /* Create and open the FIFO used for user control.  */
70 void
ui_init(void)71 ui_init(void)
72 {
73 	struct stat     st;
74 
75 	/* -f- means control messages comes in via stdin.  */
76 	if (strcmp(ui_fifo, "-") == 0) {
77 		ui_socket = 0;
78 		return;
79 	}
80 
81 	/* Don't overwrite a file, i.e '-f /etc/isakmpd/isakmpd.conf'.  */
82 	if (lstat(ui_fifo, &st) == 0) {
83 		if ((st.st_mode & S_IFMT) == S_IFREG) {
84 			errno = EEXIST;
85 			log_fatal("ui_init: could not create FIFO \"%s\"",
86 			    ui_fifo);
87 		}
88 	}
89 
90 	/* No need to know about errors.  */
91 	unlink(ui_fifo);
92 	if (mkfifo(ui_fifo, 0600) == -1)
93 		log_fatal("ui_init: mkfifo (\"%s\", 0600) failed", ui_fifo);
94 
95 	ui_socket = open(ui_fifo, O_RDWR | O_NONBLOCK, 0);
96 	if (ui_socket == -1)
97 		log_fatal("ui_init: open (\"%s\", O_RDWR | O_NONBLOCK, 0) "
98 		    "failed", ui_fifo);
99 }
100 
101 /*
102  * Setup a phase 2 connection.
103  * XXX Maybe phase 1 works too, but teardown won't work then, fix?
104  */
105 static void
ui_connect(char * cmd)106 ui_connect(char *cmd)
107 {
108 	char	name[81];
109 
110 	if (sscanf(cmd, "c %80s", name) != 1) {
111 		log_print("ui_connect: command \"%s\" malformed", cmd);
112 		return;
113 	}
114 	LOG_DBG((LOG_UI, 10, "ui_connect: setup connection \"%s\"", name));
115 	connection_setup(name);
116 }
117 
118 /* Tear down a phase 2 connection.  */
119 static void
ui_teardown(char * cmd)120 ui_teardown(char *cmd)
121 {
122 	char            name[81];
123 	struct sa      *sa;
124 
125 	if (sscanf(cmd, "t %80s", name) != 1) {
126 		log_print("ui_teardown: command \"%s\" malformed", cmd);
127 		return;
128 	}
129 	LOG_DBG((LOG_UI, 10, "ui_teardown: teardown connection \"%s\"", name));
130 	connection_teardown(name);
131 	while ((sa = sa_lookup_by_name(name, 2)) != 0)
132 		sa_delete(sa, 1);
133 }
134 
135 /* Tear down all phase 2 connections.  */
136 static void
ui_teardown_all(char * cmd)137 ui_teardown_all(char *cmd)
138 {
139 	/* Skip 'cmd' as arg. */
140 	sa_teardown_all();
141 }
142 
143 static void
ui_conn_reinit_event(void * v)144 ui_conn_reinit_event(void *v)
145 {
146 	/*
147 	 * This event is required for isakmpd to reinitialize the connection
148 	 * and passive-connection lists. Otherwise a change to the
149 	 * "[Phase 2]:Connections" tag will not have any effect.
150 	 */
151 	connection_reinit();
152 
153 	ui_cr_event = NULL;
154 }
155 
156 static void
ui_conn_reinit(void)157 ui_conn_reinit(void)
158 {
159 	struct timeval tv;
160 
161 	if (ui_cr_event)
162 		timer_remove_event(ui_cr_event);
163 
164 	gettimeofday(&tv, 0);
165 	tv.tv_sec += 5;
166 
167 	ui_cr_event = timer_add_event("ui_conn_reinit", ui_conn_reinit_event,
168 	    0, &tv);
169 	if (!ui_cr_event)
170 		log_print("ui_conn_reinit: timer_add_event() failed. "
171 		    "Connections will not be updated.");
172 }
173 
174 /*
175  * Call the configuration API.
176  * XXX Error handling!  How to do multi-line transactions?  Too short arbitrary
177  * limit on the parameters?
178  */
179 static void
ui_config(char * cmd)180 ui_config(char *cmd)
181 {
182 	char	 subcmd[81], section[81], tag[81], value[81], tmp[81];
183 	char	*v, *nv;
184 	int	 trans = 0, items, nvlen;
185 	FILE	*fd;
186 
187 	if (sscanf(cmd, "C %80s", subcmd) != 1)
188 		goto fail;
189 
190 	if (strcasecmp(subcmd, "get") == 0) {
191 		if (sscanf(cmd, "C %*s [%80[^]]]:%80s", section, tag) != 2)
192 			goto fail;
193 		v = conf_get_str(section, tag);
194 		fd = ui_open_result();
195 		if (fd) {
196 			if (v)
197 				fprintf(fd, "%s\n", v);
198 			fclose(fd);
199 		}
200 		LOG_DBG((LOG_UI, 30, "ui_config: \"%s\"", cmd));
201 		return;
202 	}
203 
204 	trans = conf_begin();
205 	if (strcasecmp(subcmd, "set") == 0) {
206 		items = sscanf(cmd, "C %*s [%80[^]]]:%80[^=]=%80s %80s",
207 		    section, tag, value, tmp);
208 		if (!(items == 3 || items == 4))
209 			goto fail;
210 		conf_set(trans, section, tag, value, items == 4 ? 1 : 0, 0);
211 		if (strcasecmp(section, "Phase 2") == 0 &&
212 		    (strcasecmp(tag, "Connections") == 0 ||
213 			strcasecmp(tag, "Passive-connections") == 0))
214 			ui_conn_reinit();
215 	} else if (strcasecmp(subcmd, "add") == 0) {
216 		items = sscanf(cmd, "C %*s [%80[^]]]:%80[^=]=%80s %80s",
217 		    section, tag, value, tmp);
218 		if (!(items == 3 || items == 4))
219 			goto fail;
220 		v = conf_get_str(section, tag);
221 		if (!v)
222 			conf_set(trans, section, tag, value, 1, 0);
223 		else {
224 			/* Add the new value to the end of the 'v' list.  */
225 			nvlen = strlen(v) + strlen(value) + 2;
226 			nv = (char *)malloc(nvlen);
227 			if (!nv) {
228 				log_error("ui_config: malloc(%d) failed",
229 				    nvlen);
230 				if (trans)
231 					conf_end(trans, 0);
232 				return;
233 			}
234 			snprintf(nv, nvlen,
235 			    v[strlen(v) - 1] == ',' ? "%s%s" : "%s,%s", v,
236 			    value);
237 			conf_set(trans, section, tag, nv, 1, 0);
238 			free(nv);
239 		}
240 		if (strcasecmp(section, "Phase 2") == 0 &&
241 		    (strcasecmp(tag, "Connections") == 0 ||
242 			strcasecmp(tag, "Passive-connections") == 0))
243 			ui_conn_reinit();
244 	} else if (strcasecmp(subcmd, "rm") == 0) {
245 		if (sscanf(cmd, "C %*s [%80[^]]]:%80s", section, tag) != 2)
246 			goto fail;
247 		conf_remove(trans, section, tag);
248 	} else if (strcasecmp(subcmd, "rms") == 0) {
249 		if (sscanf(cmd, "C %*s [%80[^]]]", section) != 1)
250 			goto fail;
251 		conf_remove_section(trans, section);
252 	} else
253 		goto fail;
254 
255 	LOG_DBG((LOG_UI, 30, "ui_config: \"%s\"", cmd));
256 	conf_end(trans, 1);
257 	return;
258 
259 fail:
260 	if (trans)
261 		conf_end(trans, 0);
262 	log_print("ui_config: command \"%s\" malformed", cmd);
263 }
264 
265 static void
ui_delete(char * cmd)266 ui_delete(char *cmd)
267 {
268 	char            cookies_str[ISAKMP_HDR_COOKIES_LEN * 2 + 1];
269 	char            message_id_str[ISAKMP_HDR_MESSAGE_ID_LEN * 2 + 1];
270 	u_int8_t        cookies[ISAKMP_HDR_COOKIES_LEN];
271 	u_int8_t        message_id_buf[ISAKMP_HDR_MESSAGE_ID_LEN];
272 	u_int8_t       *message_id = message_id_buf;
273 	struct sa      *sa;
274 
275 	if (sscanf(cmd, "d %32s %8s", cookies_str, message_id_str) != 2) {
276 		log_print("ui_delete: command \"%s\" malformed", cmd);
277 		return;
278 	}
279 	if (strcmp(message_id_str, "-") == 0)
280 		message_id = 0;
281 
282 	if (hex2raw(cookies_str, cookies, ISAKMP_HDR_COOKIES_LEN) == -1 ||
283 	    (message_id && hex2raw(message_id_str, message_id_buf,
284 	    ISAKMP_HDR_MESSAGE_ID_LEN) == -1)) {
285 		log_print("ui_delete: command \"%s\" has bad arguments", cmd);
286 		return;
287 	}
288 	sa = sa_lookup(cookies, message_id);
289 	if (!sa) {
290 		log_print("ui_delete: command \"%s\" found no SA", cmd);
291 		return;
292 	}
293 	LOG_DBG((LOG_UI, 20,
294 	    "ui_delete: deleting SA for cookie \"%s\" msgid \"%s\"",
295 	    cookies_str, message_id_str));
296 	sa_delete(sa, 1);
297 }
298 
299 /* Parse the debug command found in CMD.  */
300 static void
ui_debug(char * cmd)301 ui_debug(char *cmd)
302 {
303 	int             cls, level;
304 	char            subcmd[3];
305 
306 	if (sscanf(cmd, "D %d %d", &cls, &level) == 2) {
307 		log_debug_cmd(cls, level);
308 		return;
309 	} else if (sscanf(cmd, "D %2s %d", subcmd, &level) == 2) {
310 		switch (subcmd[0]) {
311 		case 'A':
312 			for (cls = 0; cls < LOG_ENDCLASS; cls++)
313 				log_debug_cmd(cls, level);
314 			return;
315 		}
316 	} else if (sscanf(cmd, "D %2s", subcmd) == 1) {
317 		switch (subcmd[0]) {
318 		case 'T':
319 			log_debug_toggle();
320 			return;
321 		}
322 	}
323 	log_print("ui_debug: command \"%s\" malformed", cmd);
324 }
325 
326 static void
ui_packetlog(char * cmd)327 ui_packetlog(char *cmd)
328 {
329 	char            subcmd[81];
330 
331 	if (sscanf(cmd, "p %80s", subcmd) != 1)
332 		goto fail;
333 
334 	if (strncasecmp(subcmd, "on=", 3) == 0) {
335 		/* Start capture to a new file.  */
336 		if (subcmd[strlen(subcmd) - 1] == '\n')
337 			subcmd[strlen(subcmd) - 1] = 0;
338 		log_packet_restart(subcmd + 3);
339 	} else if (strcasecmp(subcmd, "on") == 0)
340 		log_packet_restart(NULL);
341 	else if (strcasecmp(subcmd, "off") == 0)
342 		log_packet_stop();
343 	return;
344 
345 fail:
346 	log_print("ui_packetlog: command \"%s\" malformed", cmd);
347 }
348 
349 static void
ui_shutdown_daemon(char * cmd)350 ui_shutdown_daemon(char *cmd)
351 {
352 	if (strlen(cmd) == 1) {
353 		log_print("ui_shutdown_daemon: received shutdown command");
354 		daemon_shutdown_now(0);
355 	} else
356 		log_print("ui_shutdown_daemon: command \"%s\" malformed", cmd);
357 }
358 
359 /* Report SAs and ongoing exchanges.  */
360 void
ui_report(char * cmd)361 ui_report(char *cmd)
362 {
363 	/* XXX Skip 'cmd' as arg? */
364 	sa_report();
365 	exchange_report();
366 	transport_report();
367 	connection_report();
368 	timer_report();
369 	conf_report();
370 }
371 
372 /* Report all SA configuration information.  */
373 void
ui_report_sa(char * cmd)374 ui_report_sa(char *cmd)
375 {
376 	/* Skip 'cmd' as arg? */
377 
378 	FILE *fd = ui_open_result();
379 	if (!fd)
380 		return;
381 
382 	sa_report_all(fd);
383 
384 	fclose(fd);
385 }
386 
387 /*
388  * Call the relevant command handler based on the first character of the
389  * line (the command).
390  */
391 static void
ui_handle_command(char * line)392 ui_handle_command(char *line)
393 {
394 	/* Find out what one-letter command was sent.  */
395 	switch (line[0]) {
396 	case 'c':
397 		ui_connect(line);
398 		break;
399 
400 	case 'C':
401 		ui_config(line);
402 		break;
403 
404 	case 'd':
405 		ui_delete(line);
406 		break;
407 
408 	case 'D':
409 		ui_debug(line);
410 		break;
411 
412 	case 'p':
413 		ui_packetlog(line);
414 		break;
415 
416 	case 'Q':
417 		ui_shutdown_daemon(line);
418 		break;
419 
420 	case 'R':
421 		reinit();
422 		break;
423 
424 	case 'S':
425 		ui_report_sa(line);
426 		break;
427 
428 	case 'r':
429 		ui_report(line);
430 		break;
431 
432 	case 't':
433 		ui_teardown(line);
434 		break;
435 
436 	case 'T':
437 		ui_teardown_all(line);
438 		break;
439 
440 	default:
441 		log_print("ui_handle_messages: unrecognized command: '%c'",
442 		    line[0]);
443 	}
444 }
445 
446 /*
447  * A half-complex implementation of reading from a file descriptor
448  * line by line without resorting to stdio which apparently have
449  * troubles with non-blocking fifos.
450  */
451 void
ui_handler(void)452 ui_handler(void)
453 {
454 	static char    *buf = 0;
455 	static char    *p;
456 	static size_t   sz;
457 	static size_t   resid;
458 	ssize_t         n;
459 	char           *new_buf;
460 
461 	/* If no buffer, set it up.  */
462 	if (!buf) {
463 		sz = BUF_SZ;
464 		buf = malloc(sz);
465 		if (!buf) {
466 			log_print("ui_handler: malloc (%lu) failed",
467 			    (unsigned long)sz);
468 			return;
469 		}
470 		p = buf;
471 		resid = sz;
472 	}
473 	/* If no place left in the buffer reallocate twice as large.  */
474 	if (!resid) {
475 		new_buf = realloc(buf, sz * 2);
476 		if (!new_buf) {
477 			log_print("ui_handler: realloc (%p, %lu) failed", buf,
478 			    (unsigned long)sz * 2);
479 			free(buf);
480 			buf = 0;
481 			return;
482 		}
483 		buf = new_buf;
484 		p = buf + sz;
485 		resid = sz;
486 		sz *= 2;
487 	}
488 	n = read(ui_socket, p, resid);
489 	if (n == -1) {
490 		log_error("ui_handler: read (%d, %p, %lu)", ui_socket, p,
491 		    (unsigned long)resid);
492 		return;
493 	}
494 	if (!n)
495 		return;
496 	resid -= n;
497 	while (n--) {
498 		/*
499 		 * When we find a newline, cut off the line and feed it to the
500 		 * command processor.  Then move the rest up-front.
501 		 */
502 		if (*p == '\n') {
503 			*p = '\0';
504 			ui_handle_command(buf);
505 			memcpy(buf, p + 1, n);
506 			p = buf;
507 			resid = sz - n;
508 			continue;
509 		}
510 		p++;
511 	}
512 }
513 
514 static FILE *
ui_open_result(void)515 ui_open_result(void)
516 {
517 	FILE *fd = monitor_fopen(RESULT_FILE, "w");
518 	if (!fd)
519 		log_error("ui_open_result: fopen() failed");
520 	return fd;
521 }
522