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