1 /* $OpenBSD: ui.c,v 1.58 2021/10/24 21:24:21 deraadt 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); 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 timespec ts; 201 202 if (ui_cr_event) 203 timer_remove_event(ui_cr_event); 204 205 clock_gettime(CLOCK_MONOTONIC, &ts); 206 ts.tv_sec += 5; 207 208 ui_cr_event = timer_add_event("ui_conn_reinit", ui_conn_reinit_event, 209 0, &ts); 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 } 642