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