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