1 /* $OpenBSD: ui.c,v 1.21 2001/08/24 13:53:02 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 "isakmp.h" 53 #include "log.h" 54 #include "sa.h" 55 #include "timer.h" 56 #include "transport.h" 57 #include "ui.h" 58 #include "util.h" 59 60 #define BUF_SZ 256 61 62 /* from isakmpd.c */ 63 void daemon_shutdown_now (int); 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 (mkfifo (ui_fifo, 0600) == -1) 90 log_fatal ("ui_init: mkfifo (\"%s\", 0600) failed", ui_fifo); 91 92 /* XXX Is O_RDWR needed on some OSes? Photurisd seems to imply that. */ 93 ui_socket = open (ui_fifo, O_RDONLY | O_NONBLOCK, 0); 94 if (ui_socket == -1) 95 log_fatal ("ui_init: open (\"%s\", O_RDONLY | 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 228 if (sscanf (cmd, "D %d %d", &cls, &level) != 2) 229 { 230 log_print ("ui_debug: command \"%s\" malformed", cmd); 231 return; 232 } 233 log_debug_cmd (cls, level); 234 } 235 236 static void 237 ui_packetlog (char *cmd) 238 { 239 char subcmd[81]; 240 241 if (sscanf (cmd, "p %80s", subcmd) != 1) 242 goto fail; 243 244 if (strncasecmp (subcmd, "on=", 3) == 0) 245 { 246 /* Start capture to a new file. */ 247 if (subcmd[strlen (subcmd) - 1] == '\n') 248 subcmd[strlen (subcmd) - 1] = 0; 249 log_packet_restart (subcmd + 3); 250 } 251 else if (strcasecmp (subcmd, "on") == 0) 252 log_packet_restart (NULL); 253 else if (strcasecmp (subcmd, "off") == 0) 254 log_packet_stop (); 255 256 return; 257 258 fail: 259 log_print ("ui_packetlog: command \"%s\" malformed", cmd); 260 } 261 #endif /* USE_DEBUG */ 262 263 static void 264 ui_shutdown_daemon (char *cmd) 265 { 266 if (strlen (cmd) == 1) 267 { 268 log_print ("ui_shutdown_daemon: received shutdown command"); 269 daemon_shutdown_now (0); 270 } 271 else 272 log_print ("ui_shutdown_daemon: command \"%s\" malformed", cmd); 273 } 274 275 /* Report SAs and ongoing exchanges. */ 276 void 277 ui_report (char *cmd) 278 { 279 /* XXX Skip 'cmd' as arg? */ 280 sa_report (); 281 exchange_report (); 282 transport_report (); 283 connection_report (); 284 timer_report (); 285 conf_report (); 286 } 287 288 /* 289 * Call the relevant command handler based on the first character of the 290 * line (the command). 291 */ 292 static void 293 ui_handle_command (char *line) 294 { 295 /* Find out what one-letter command was sent. */ 296 switch (line[0]) 297 { 298 case 'c': 299 ui_connect (line); 300 break; 301 302 case 'C': 303 ui_config (line); 304 break; 305 306 case 'd': 307 ui_delete (line); 308 break; 309 310 #ifdef USE_DEBUG 311 case 'D': 312 ui_debug (line); 313 break; 314 #endif 315 316 #ifdef USE_DEBUG 317 case 'p': 318 ui_packetlog (line); 319 break; 320 #endif 321 322 case 'Q': 323 ui_shutdown_daemon (line); 324 break; 325 326 case 'r': 327 ui_report (line); 328 break; 329 330 case 't': 331 ui_teardown (line); 332 break; 333 334 default: 335 log_print ("ui_handle_messages: unrecognized command: '%c'", line[0]); 336 } 337 } 338 339 /* 340 * A half-complex implementation of reading from a file descriptor 341 * line by line without resorting to stdio which apparently have 342 * troubles with non-blocking fifos. 343 */ 344 void 345 ui_handler (void) 346 { 347 static char *buf = 0; 348 static char *p; 349 static size_t sz; 350 static size_t resid; 351 size_t n; 352 char *new_buf; 353 354 /* If no buffer, set it up. */ 355 if (!buf) 356 { 357 sz = BUF_SZ; 358 buf = malloc (sz); 359 if (!buf) 360 { 361 log_print ("ui_handler: malloc (%d) failed", sz); 362 return; 363 } 364 p = buf; 365 resid = sz; 366 } 367 368 /* If no place left in the buffer reallocate twice as large. */ 369 if (!resid) 370 { 371 new_buf = realloc (buf, sz * 2); 372 if (!new_buf) 373 { 374 log_print ("ui_handler: realloc (%p, %d) failed", buf, sz * 2); 375 free (buf); 376 buf = 0; 377 return; 378 } 379 buf = new_buf; 380 p = buf + sz; 381 resid = sz; 382 sz *= 2; 383 } 384 385 n = read (ui_socket, p, resid); 386 if (n == -1) 387 { 388 log_error ("ui_handler: read (%d, %p, %d)", ui_socket, p, resid); 389 return; 390 } 391 392 if (!n) 393 return; 394 resid -= n; 395 while (n--) 396 { 397 /* 398 * When we find a newline, cut off the line and feed it to the 399 * command processor. Then move the rest up-front. 400 */ 401 if (*p == '\n') 402 { 403 *p = '\0'; 404 ui_handle_command (buf); 405 memcpy (buf, p + 1, n); 406 p = buf; 407 resid = sz - n; 408 continue; 409 } 410 p++; 411 } 412 } 413