1 /* $OpenBSD: ui.c,v 1.20 2001/07/06 14:37:12 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 char *ui_fifo = FIFO; 63 int ui_socket; 64 65 /* Create and open the FIFO used for user control. */ 66 void 67 ui_init (void) 68 { 69 struct stat st; 70 71 /* -f- means control messages comes in via stdin. */ 72 if (strcmp (ui_fifo, "-") == 0) 73 ui_socket = 0; 74 else 75 { 76 /* Don't overwrite a file, i.e '-f /etc/isakmpd/isakmpd.conf'. */ 77 if (lstat (ui_fifo, &st) == 0) 78 if ((st.st_mode & S_IFMT) == S_IFREG) 79 { 80 errno = EEXIST; 81 log_fatal ("ui_init: could not create FIFO \"%s\"", ui_fifo); 82 } 83 84 /* No need to know about errors. */ 85 unlink (ui_fifo); 86 if (mkfifo (ui_fifo, 0600) == -1) 87 log_fatal ("ui_init: mkfifo (\"%s\", 0600) failed", ui_fifo); 88 89 /* XXX Is O_RDWR needed on some OSes? Photurisd seems to imply that. */ 90 ui_socket = open (ui_fifo, O_RDONLY | O_NONBLOCK, 0); 91 if (ui_socket == -1) 92 log_fatal ("ui_init: open (\"%s\", O_RDONLY | O_NONBLOCK, 0) failed", 93 ui_fifo); 94 } 95 } 96 97 /* 98 * Setup a phase 2 connection. 99 * XXX Maybe phase 1 works too, but teardown won't work then, fix? 100 */ 101 static void 102 ui_connect (char *cmd) 103 { 104 char name[81]; 105 106 if (sscanf (cmd, "c %80s", name) != 1) 107 { 108 log_print ("ui_connect: command \"%s\" malformed", cmd); 109 return; 110 } 111 log_print ("ui_connect: setup connection \"%s\"", name); 112 connection_setup (name); 113 } 114 115 /* Tear down a phase 2 connection. */ 116 static void 117 ui_teardown (char *cmd) 118 { 119 char name[81]; 120 struct sa *sa; 121 122 if (sscanf (cmd, "t %80s", name) != 1) 123 { 124 log_print ("ui_teardown: command \"%s\" malformed", cmd); 125 return; 126 } 127 log_print ("ui_teardown: teardown connection \"%s\"", name); 128 connection_teardown (name); 129 while ((sa = sa_lookup_by_name (name, 2)) != 0) 130 sa_delete (sa, 1); 131 } 132 133 /* 134 * Call the configuration API. 135 * XXX Error handling! How to do multi-line transactions? Too short arbitrary 136 * limit on the parameters? 137 */ 138 static void 139 ui_config (char *cmd) 140 { 141 char subcmd[81], section[81], tag[81], value[81]; 142 int override, trans = 0; 143 144 if (sscanf (cmd, "C %80s", subcmd) != 1) 145 goto fail; 146 147 trans = conf_begin (); 148 if (strcasecmp (subcmd, "set") == 0) 149 { 150 if (sscanf (cmd, "C %*s [%80[^]]]:%80[^=]=%80s %d", section, tag, value, 151 &override) != 4) 152 goto fail; 153 conf_set (trans, section, tag, value, override, 0); 154 } 155 else if (strcasecmp (subcmd, "rm") == 0) 156 { 157 if (sscanf (cmd, "C %*s [%80[^]]]:%80s", section, tag) != 2) 158 goto fail; 159 conf_remove (trans, section, tag); 160 } 161 else if (strcasecmp (subcmd, "rms") == 0) 162 { 163 if (sscanf (cmd, "C %*s [%80[^]]]", section) != 1) 164 goto fail; 165 conf_remove_section (trans, section); 166 } 167 else 168 goto fail; 169 170 log_print ("ui_config: \"%s\"", cmd); 171 conf_end (trans, 1); 172 return; 173 174 fail: 175 if (trans) 176 conf_end (trans, 0); 177 log_print ("ui_config: command \"%s\" malformed", cmd); 178 } 179 180 static void 181 ui_delete (char *cmd) 182 { 183 char cookies_str[ISAKMP_HDR_COOKIES_LEN * 2 + 1]; 184 char message_id_str[ISAKMP_HDR_MESSAGE_ID_LEN * 2 + 1]; 185 u_int8_t cookies[ISAKMP_HDR_COOKIES_LEN]; 186 u_int8_t message_id_buf[ISAKMP_HDR_MESSAGE_ID_LEN]; 187 u_int8_t *message_id = message_id_buf; 188 struct sa *sa; 189 190 if (sscanf (cmd, "d %32s %8s", cookies_str, message_id_str) != 2) 191 { 192 log_print ("ui_delete: command \"%s\" malformed", cmd); 193 return; 194 } 195 196 if (strcmp (message_id_str, "-") == 0) 197 message_id = 0; 198 199 if (hex2raw (cookies_str, cookies, ISAKMP_HDR_COOKIES_LEN) == -1 200 || (message_id && hex2raw (message_id_str, message_id_buf, 201 ISAKMP_HDR_MESSAGE_ID_LEN) == -1)) 202 { 203 log_print ("ui_delete: command \"%s\" has bad arguments", cmd); 204 return; 205 } 206 207 sa = sa_lookup (cookies, message_id); 208 if (!sa) 209 { 210 log_print ("ui_delete: command \"%s\" found no SA", cmd); 211 return; 212 } 213 log_print ("ui_delete: deleting SA for cookie \"%s\" msgid \"%s\"", 214 cookies_str, message_id_str); 215 sa_delete (sa, 1); 216 } 217 218 #ifdef USE_DEBUG 219 /* Parse the debug command found in CMD. */ 220 static void 221 ui_debug (char *cmd) 222 { 223 int cls, level; 224 225 if (sscanf (cmd, "D %d %d", &cls, &level) != 2) 226 { 227 log_print ("ui_debug: command \"%s\" malformed", cmd); 228 return; 229 } 230 log_debug_cmd (cls, level); 231 } 232 233 static void 234 ui_packetlog (char *cmd) 235 { 236 char subcmd[81]; 237 238 if (sscanf (cmd, "p %80s", subcmd) != 1) 239 goto fail; 240 241 if (strncasecmp (subcmd, "on=", 3) == 0) 242 { 243 /* Start capture to a new file. */ 244 if (subcmd[strlen (subcmd) - 1] == '\n') 245 subcmd[strlen (subcmd) - 1] = 0; 246 log_packet_restart (subcmd + 3); 247 } 248 else if (strcasecmp (subcmd, "on") == 0) 249 log_packet_restart (NULL); 250 else if (strcasecmp (subcmd, "off") == 0) 251 log_packet_stop (); 252 253 return; 254 255 fail: 256 log_print ("ui_packetlog: command \"%s\" malformed", cmd); 257 } 258 #endif /* USE_DEBUG */ 259 260 /* Report SAs and ongoing exchanges. */ 261 void 262 ui_report (char *cmd) 263 { 264 /* XXX Skip 'cmd' as arg? */ 265 sa_report (); 266 exchange_report (); 267 transport_report (); 268 connection_report (); 269 timer_report (); 270 conf_report (); 271 } 272 273 /* 274 * Call the relevant command handler based on the first character of the 275 * line (the command). 276 */ 277 static void 278 ui_handle_command (char *line) 279 { 280 /* Find out what one-letter command was sent. */ 281 switch (line[0]) 282 { 283 case 'c': 284 ui_connect (line); 285 break; 286 287 case 'C': 288 ui_config (line); 289 break; 290 291 case 'd': 292 ui_delete (line); 293 break; 294 295 #ifdef USE_DEBUG 296 case 'D': 297 ui_debug (line); 298 break; 299 #endif 300 301 case 'r': 302 ui_report (line); 303 break; 304 305 case 't': 306 ui_teardown (line); 307 break; 308 309 #ifdef USE_DEBUG 310 case 'p': 311 ui_packetlog (line); 312 break; 313 #endif 314 315 default: 316 log_print ("ui_handle_messages: unrecognized command: '%c'", line[0]); 317 } 318 } 319 320 /* 321 * A half-complex implementation of reading from a file descriptor 322 * line by line without resorting to stdio which apparently have 323 * troubles with non-blocking fifos. 324 */ 325 void 326 ui_handler (void) 327 { 328 static char *buf = 0; 329 static char *p; 330 static size_t sz; 331 static size_t resid; 332 size_t n; 333 char *new_buf; 334 335 /* If no buffer, set it up. */ 336 if (!buf) 337 { 338 sz = BUF_SZ; 339 buf = malloc (sz); 340 if (!buf) 341 { 342 log_print ("ui_handler: malloc (%d) failed", sz); 343 return; 344 } 345 p = buf; 346 resid = sz; 347 } 348 349 /* If no place left in the buffer reallocate twice as large. */ 350 if (!resid) 351 { 352 new_buf = realloc (buf, sz * 2); 353 if (!new_buf) 354 { 355 log_print ("ui_handler: realloc (%p, %d) failed", buf, sz * 2); 356 free (buf); 357 buf = 0; 358 return; 359 } 360 buf = new_buf; 361 p = buf + sz; 362 resid = sz; 363 sz *= 2; 364 } 365 366 n = read (ui_socket, p, resid); 367 if (n == -1) 368 { 369 log_error ("ui_handler: read (%d, %p, %d)", ui_socket, p, resid); 370 return; 371 } 372 373 if (!n) 374 return; 375 resid -= n; 376 while (n--) 377 { 378 /* 379 * When we find a newline, cut off the line and feed it to the 380 * command processor. Then move the rest up-front. 381 */ 382 if (*p == '\n') 383 { 384 *p = '\0'; 385 ui_handle_command (buf); 386 memcpy (buf, p + 1, n); 387 p = buf; 388 resid = sz - n; 389 continue; 390 } 391 p++; 392 } 393 } 394