1 /* $OpenBSD: ui.c,v 1.35 2003/11/06 16:12:08 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_DBG ((LOG_UI, 10, "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_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 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_DBG ((LOG_UI, 30, "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_DBG ((LOG_UI, 20, 225 "ui_delete: deleting SA for cookie \"%s\" msgid \"%s\"", 226 cookies_str, message_id_str)); 227 sa_delete (sa, 1); 228 } 229 230 #ifdef USE_DEBUG 231 /* Parse the debug command found in CMD. */ 232 static void 233 ui_debug (char *cmd) 234 { 235 int cls, level; 236 char subcmd[3]; 237 238 if (sscanf (cmd, "D %d %d", &cls, &level) == 2) 239 { 240 log_debug_cmd (cls, level); 241 return; 242 } 243 else if (sscanf (cmd, "D %2s %d", subcmd, &level) == 2) 244 { 245 switch (subcmd[0]) 246 { 247 case 'A': 248 for (cls = 0; cls < LOG_ENDCLASS; cls++) 249 log_debug_cmd (cls, level); 250 return; 251 } 252 } 253 else if (sscanf (cmd, "D %2s", subcmd) == 1) 254 { 255 switch (subcmd[0]) 256 { 257 case 'T': 258 log_debug_toggle (); 259 return; 260 } 261 } 262 263 log_print ("ui_debug: command \"%s\" malformed", cmd); 264 return; 265 } 266 267 static void 268 ui_packetlog (char *cmd) 269 { 270 char subcmd[81]; 271 272 if (sscanf (cmd, "p %80s", subcmd) != 1) 273 goto fail; 274 275 if (strncasecmp (subcmd, "on=", 3) == 0) 276 { 277 /* Start capture to a new file. */ 278 if (subcmd[strlen (subcmd) - 1] == '\n') 279 subcmd[strlen (subcmd) - 1] = 0; 280 log_packet_restart (subcmd + 3); 281 } 282 else if (strcasecmp (subcmd, "on") == 0) 283 log_packet_restart (NULL); 284 else if (strcasecmp (subcmd, "off") == 0) 285 log_packet_stop (); 286 287 return; 288 289 fail: 290 log_print ("ui_packetlog: command \"%s\" malformed", cmd); 291 } 292 #endif /* USE_DEBUG */ 293 294 static void 295 ui_shutdown_daemon (char *cmd) 296 { 297 if (strlen (cmd) == 1) 298 { 299 log_print ("ui_shutdown_daemon: received shutdown command"); 300 daemon_shutdown_now (0); 301 } 302 else 303 log_print ("ui_shutdown_daemon: command \"%s\" malformed", cmd); 304 } 305 306 /* Report SAs and ongoing exchanges. */ 307 void 308 ui_report (char *cmd) 309 { 310 /* XXX Skip 'cmd' as arg? */ 311 sa_report (); 312 exchange_report (); 313 transport_report (); 314 connection_report (); 315 timer_report (); 316 conf_report (); 317 } 318 319 /* Report all SA configuration information. */ 320 void 321 ui_report_sa (char *cmd) 322 { 323 /* Skip 'cmd' as arg? */ 324 sa_report_all (); 325 } 326 327 /* 328 * Call the relevant command handler based on the first character of the 329 * line (the command). 330 */ 331 static void 332 ui_handle_command (char *line) 333 { 334 /* Find out what one-letter command was sent. */ 335 switch (line[0]) 336 { 337 case 'c': 338 ui_connect (line); 339 break; 340 341 case 'C': 342 ui_config (line); 343 break; 344 345 case 'd': 346 ui_delete (line); 347 break; 348 349 #ifdef USE_DEBUG 350 case 'D': 351 ui_debug (line); 352 break; 353 354 case 'p': 355 ui_packetlog (line); 356 break; 357 #endif 358 359 case 'Q': 360 ui_shutdown_daemon (line); 361 break; 362 363 case 'R': 364 reinit (); 365 break; 366 367 case 'S': 368 ui_report_sa (line); 369 break; 370 371 case 'r': 372 ui_report (line); 373 break; 374 375 case 't': 376 ui_teardown (line); 377 break; 378 379 case 'T': 380 ui_teardown_all (line); 381 break; 382 383 default: 384 log_print ("ui_handle_messages: unrecognized command: '%c'", line[0]); 385 } 386 } 387 388 /* 389 * A half-complex implementation of reading from a file descriptor 390 * line by line without resorting to stdio which apparently have 391 * troubles with non-blocking fifos. 392 */ 393 void 394 ui_handler (void) 395 { 396 static char *buf = 0; 397 static char *p; 398 static size_t sz; 399 static size_t resid; 400 size_t n; 401 char *new_buf; 402 403 /* If no buffer, set it up. */ 404 if (!buf) 405 { 406 sz = BUF_SZ; 407 buf = malloc (sz); 408 if (!buf) 409 { 410 log_print ("ui_handler: malloc (%lu) failed", (unsigned long)sz); 411 return; 412 } 413 p = buf; 414 resid = sz; 415 } 416 417 /* If no place left in the buffer reallocate twice as large. */ 418 if (!resid) 419 { 420 new_buf = realloc (buf, sz * 2); 421 if (!new_buf) 422 { 423 log_print ("ui_handler: realloc (%p, %lu) failed", buf, 424 (unsigned long)sz * 2); 425 free (buf); 426 buf = 0; 427 return; 428 } 429 buf = new_buf; 430 p = buf + sz; 431 resid = sz; 432 sz *= 2; 433 } 434 435 n = read (ui_socket, p, resid); 436 if (n == -1) 437 { 438 log_error ("ui_handler: read (%d, %p, %lu)", ui_socket, p, 439 (unsigned long)resid); 440 return; 441 } 442 443 if (!n) 444 return; 445 resid -= n; 446 while (n--) 447 { 448 /* 449 * When we find a newline, cut off the line and feed it to the 450 * command processor. Then move the rest up-front. 451 */ 452 if (*p == '\n') 453 { 454 *p = '\0'; 455 ui_handle_command (buf); 456 memcpy (buf, p + 1, n); 457 p = buf; 458 resid = sz - n; 459 continue; 460 } 461 p++; 462 } 463 } 464