1 /* $OpenBSD: ui.c,v 1.26 2001/12/11 01:43:54 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 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 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by Ericsson Radio Systems. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * This code was written under funding by Ericsson Radio Systems. 36 */ 37 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <fcntl.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include <errno.h> 45 46 #include "sysdep.h" 47 48 #include "conf.h" 49 #include "connection.h" 50 #include "doi.h" 51 #include "exchange.h" 52 #include "init.h" 53 #include "isakmp.h" 54 #include "log.h" 55 #include "sa.h" 56 #include "timer.h" 57 #include "transport.h" 58 #include "ui.h" 59 #include "util.h" 60 61 #define BUF_SZ 256 62 63 /* from isakmpd.c */ 64 void daemon_shutdown_now (int); 65 66 char *ui_fifo = FIFO; 67 int ui_socket; 68 69 /* Create and open the FIFO used for user control. */ 70 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 else 79 { 80 /* Don't overwrite a file, i.e '-f /etc/isakmpd/isakmpd.conf'. */ 81 if (lstat (ui_fifo, &st) == 0) 82 if ((st.st_mode & S_IFMT) == S_IFREG) 83 { 84 errno = EEXIST; 85 log_fatal ("ui_init: could not create FIFO \"%s\"", ui_fifo); 86 } 87 88 /* No need to know about errors. */ 89 unlink (ui_fifo); 90 if (mkfifo (ui_fifo, 0600) == -1) 91 log_fatal ("ui_init: mkfifo (\"%s\", 0600) failed", ui_fifo); 92 93 ui_socket = open (ui_fifo, O_RDWR | O_NONBLOCK, 0); 94 if (ui_socket == -1) 95 log_fatal ("ui_init: open (\"%s\", O_RDWR | O_NONBLOCK, 0) failed", 96 ui_fifo); 97 } 98 } 99 100 /* 101 * Setup a phase 2 connection. 102 * XXX Maybe phase 1 works too, but teardown won't work then, fix? 103 */ 104 static void 105 ui_connect (char *cmd) 106 { 107 char name[81]; 108 109 if (sscanf (cmd, "c %80s", name) != 1) 110 { 111 log_print ("ui_connect: command \"%s\" malformed", cmd); 112 return; 113 } 114 log_print ("ui_connect: setup connection \"%s\"", name); 115 connection_setup (name); 116 } 117 118 /* Tear down a phase 2 connection. */ 119 static void 120 ui_teardown (char *cmd) 121 { 122 char name[81]; 123 struct sa *sa; 124 125 if (sscanf (cmd, "t %80s", name) != 1) 126 { 127 log_print ("ui_teardown: command \"%s\" malformed", cmd); 128 return; 129 } 130 log_print ("ui_teardown: teardown connection \"%s\"", name); 131 connection_teardown (name); 132 while ((sa = sa_lookup_by_name (name, 2)) != 0) 133 sa_delete (sa, 1); 134 } 135 136 /* 137 * Call the configuration API. 138 * XXX Error handling! How to do multi-line transactions? Too short arbitrary 139 * limit on the parameters? 140 */ 141 static void 142 ui_config (char *cmd) 143 { 144 char subcmd[81], section[81], tag[81], value[81]; 145 int override, trans = 0; 146 147 if (sscanf (cmd, "C %80s", subcmd) != 1) 148 goto fail; 149 150 trans = conf_begin (); 151 if (strcasecmp (subcmd, "set") == 0) 152 { 153 if (sscanf (cmd, "C %*s [%80[^]]]:%80[^=]=%80s %d", section, tag, value, 154 &override) != 4) 155 goto fail; 156 conf_set (trans, section, tag, value, override, 0); 157 } 158 else if (strcasecmp (subcmd, "rm") == 0) 159 { 160 if (sscanf (cmd, "C %*s [%80[^]]]:%80s", section, tag) != 2) 161 goto fail; 162 conf_remove (trans, section, tag); 163 } 164 else if (strcasecmp (subcmd, "rms") == 0) 165 { 166 if (sscanf (cmd, "C %*s [%80[^]]]", section) != 1) 167 goto fail; 168 conf_remove_section (trans, section); 169 } 170 else 171 goto fail; 172 173 log_print ("ui_config: \"%s\"", cmd); 174 conf_end (trans, 1); 175 return; 176 177 fail: 178 if (trans) 179 conf_end (trans, 0); 180 log_print ("ui_config: command \"%s\" malformed", cmd); 181 } 182 183 static void 184 ui_delete (char *cmd) 185 { 186 char cookies_str[ISAKMP_HDR_COOKIES_LEN * 2 + 1]; 187 char message_id_str[ISAKMP_HDR_MESSAGE_ID_LEN * 2 + 1]; 188 u_int8_t cookies[ISAKMP_HDR_COOKIES_LEN]; 189 u_int8_t message_id_buf[ISAKMP_HDR_MESSAGE_ID_LEN]; 190 u_int8_t *message_id = message_id_buf; 191 struct sa *sa; 192 193 if (sscanf (cmd, "d %32s %8s", cookies_str, message_id_str) != 2) 194 { 195 log_print ("ui_delete: command \"%s\" malformed", cmd); 196 return; 197 } 198 199 if (strcmp (message_id_str, "-") == 0) 200 message_id = 0; 201 202 if (hex2raw (cookies_str, cookies, ISAKMP_HDR_COOKIES_LEN) == -1 203 || (message_id && hex2raw (message_id_str, message_id_buf, 204 ISAKMP_HDR_MESSAGE_ID_LEN) == -1)) 205 { 206 log_print ("ui_delete: command \"%s\" has bad arguments", cmd); 207 return; 208 } 209 210 sa = sa_lookup (cookies, message_id); 211 if (!sa) 212 { 213 log_print ("ui_delete: command \"%s\" found no SA", cmd); 214 return; 215 } 216 log_print ("ui_delete: deleting SA for cookie \"%s\" msgid \"%s\"", 217 cookies_str, message_id_str); 218 sa_delete (sa, 1); 219 } 220 221 #ifdef USE_DEBUG 222 /* Parse the debug command found in CMD. */ 223 static void 224 ui_debug (char *cmd) 225 { 226 int cls, level; 227 char subcmd[3]; 228 229 if (sscanf (cmd, "D %d %d", &cls, &level) == 2) 230 { 231 log_debug_cmd (cls, level); 232 return; 233 } 234 else if (sscanf (cmd, "D %2s %d", subcmd, &level) == 2) 235 { 236 switch (subcmd[0]) 237 { 238 case 'A': 239 for (cls = 0; cls < LOG_ENDCLASS; cls++) 240 log_debug_cmd (cls, level); 241 return; 242 } 243 } 244 else if (sscanf (cmd, "D %2s", subcmd) == 1) 245 { 246 switch (subcmd[0]) 247 { 248 case 'T': 249 log_debug_toggle (); 250 return; 251 } 252 } 253 254 log_print ("ui_debug: command \"%s\" malformed", cmd); 255 return; 256 } 257 258 static void 259 ui_packetlog (char *cmd) 260 { 261 char subcmd[81]; 262 263 if (sscanf (cmd, "p %80s", subcmd) != 1) 264 goto fail; 265 266 if (strncasecmp (subcmd, "on=", 3) == 0) 267 { 268 /* Start capture to a new file. */ 269 if (subcmd[strlen (subcmd) - 1] == '\n') 270 subcmd[strlen (subcmd) - 1] = 0; 271 log_packet_restart (subcmd + 3); 272 } 273 else if (strcasecmp (subcmd, "on") == 0) 274 log_packet_restart (NULL); 275 else if (strcasecmp (subcmd, "off") == 0) 276 log_packet_stop (); 277 278 return; 279 280 fail: 281 log_print ("ui_packetlog: command \"%s\" malformed", cmd); 282 } 283 #endif /* USE_DEBUG */ 284 285 static void 286 ui_shutdown_daemon (char *cmd) 287 { 288 if (strlen (cmd) == 1) 289 { 290 log_print ("ui_shutdown_daemon: received shutdown command"); 291 daemon_shutdown_now (0); 292 } 293 else 294 log_print ("ui_shutdown_daemon: command \"%s\" malformed", cmd); 295 } 296 297 /* Report SAs and ongoing exchanges. */ 298 void 299 ui_report (char *cmd) 300 { 301 /* XXX Skip 'cmd' as arg? */ 302 sa_report (); 303 exchange_report (); 304 transport_report (); 305 connection_report (); 306 timer_report (); 307 conf_report (); 308 } 309 310 /* 311 * Call the relevant command handler based on the first character of the 312 * line (the command). 313 */ 314 static void 315 ui_handle_command (char *line) 316 { 317 /* Find out what one-letter command was sent. */ 318 switch (line[0]) 319 { 320 case 'c': 321 ui_connect (line); 322 break; 323 324 case 'C': 325 ui_config (line); 326 break; 327 328 case 'd': 329 ui_delete (line); 330 break; 331 332 #ifdef USE_DEBUG 333 case 'D': 334 ui_debug (line); 335 break; 336 337 case 'p': 338 ui_packetlog (line); 339 break; 340 #endif 341 342 case 'Q': 343 ui_shutdown_daemon (line); 344 break; 345 346 case 'R': 347 reinit (); 348 break; 349 350 case 'r': 351 ui_report (line); 352 break; 353 354 case 't': 355 ui_teardown (line); 356 break; 357 358 default: 359 log_print ("ui_handle_messages: unrecognized command: '%c'", line[0]); 360 } 361 } 362 363 /* 364 * A half-complex implementation of reading from a file descriptor 365 * line by line without resorting to stdio which apparently have 366 * troubles with non-blocking fifos. 367 */ 368 void 369 ui_handler (void) 370 { 371 static char *buf = 0; 372 static char *p; 373 static size_t sz; 374 static size_t resid; 375 size_t n; 376 char *new_buf; 377 378 /* If no buffer, set it up. */ 379 if (!buf) 380 { 381 sz = BUF_SZ; 382 buf = malloc (sz); 383 if (!buf) 384 { 385 log_print ("ui_handler: malloc (%d) failed", sz); 386 return; 387 } 388 p = buf; 389 resid = sz; 390 } 391 392 /* If no place left in the buffer reallocate twice as large. */ 393 if (!resid) 394 { 395 new_buf = realloc (buf, sz * 2); 396 if (!new_buf) 397 { 398 log_print ("ui_handler: realloc (%p, %d) failed", buf, sz * 2); 399 free (buf); 400 buf = 0; 401 return; 402 } 403 buf = new_buf; 404 p = buf + sz; 405 resid = sz; 406 sz *= 2; 407 } 408 409 n = read (ui_socket, p, resid); 410 if (n == -1) 411 { 412 log_error ("ui_handler: read (%d, %p, %d)", ui_socket, p, resid); 413 return; 414 } 415 416 if (!n) 417 return; 418 resid -= n; 419 while (n--) 420 { 421 /* 422 * When we find a newline, cut off the line and feed it to the 423 * command processor. Then move the rest up-front. 424 */ 425 if (*p == '\n') 426 { 427 *p = '\0'; 428 ui_handle_command (buf); 429 memcpy (buf, p + 1, n); 430 p = buf; 431 resid = sz - n; 432 continue; 433 } 434 p++; 435 } 436 } 437