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