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