xref: /openbsd/sbin/isakmpd/ui.c (revision db3296cf)
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