xref: /openbsd-src/sbin/iked/control.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: control.c,v 1.8 2012/04/05 17:31:36 deraadt Exp $	*/
2 /*	$vantronix: control.c,v 1.4 2010/05/14 07:35:52 reyk Exp $	*/
3 
4 /*
5  * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
6  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/queue.h>
22 #include <sys/param.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/socket.h>
26 #include <sys/un.h>
27 #include <sys/tree.h>
28 
29 #include <net/if.h>
30 
31 #include <errno.h>
32 #include <event.h>
33 #include <fcntl.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <signal.h>
38 
39 #include "iked.h"
40 
41 #define	CONTROL_BACKLOG	5
42 
43 struct ctl_connlist ctl_conns;
44 
45 void
46 	 control_accept(int, short, void *);
47 struct ctl_conn
48 	*control_connbyfd(int);
49 void	 control_close(int, struct control_sock *);
50 void	 control_dispatch_imsg(int, short, void *);
51 void	 control_imsg_forward(struct imsg *);
52 
53 int
54 control_init(struct privsep *ps, struct control_sock *cs)
55 {
56 	struct iked		*env = ps->ps_env;
57 	struct sockaddr_un	 sun;
58 	int			 fd;
59 	mode_t			 old_umask, mode;
60 
61 	if (cs->cs_name == NULL)
62 		return (0);
63 
64 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
65 		log_warn("%s: socket", __func__);
66 		return (-1);
67 	}
68 
69 	sun.sun_family = AF_UNIX;
70 	if (strlcpy(sun.sun_path, cs->cs_name,
71 	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
72 		log_warn("%s: %s name too long", __func__, cs->cs_name);
73 		close(fd);
74 		return (-1);
75 	}
76 
77 	if (unlink(cs->cs_name) == -1)
78 		if (errno != ENOENT) {
79 			log_warn("%s: unlink %s", __func__, cs->cs_name);
80 			close(fd);
81 			return (-1);
82 		}
83 
84 	if (cs->cs_restricted) {
85 		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
86 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
87 	} else {
88 		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
89 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
90 	}
91 
92 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
93 		log_warn("%s: bind: %s", __func__, cs->cs_name);
94 		close(fd);
95 		(void)umask(old_umask);
96 		return (-1);
97 	}
98 	(void)umask(old_umask);
99 
100 	if (chmod(cs->cs_name, mode) == -1) {
101 		log_warn("%s: chmod", __func__);
102 		close(fd);
103 		(void)unlink(cs->cs_name);
104 		return (-1);
105 	}
106 
107 	socket_set_blockmode(fd, BM_NONBLOCK);
108 	cs->cs_fd = fd;
109 	cs->cs_env = env;
110 
111 	return (0);
112 }
113 
114 int
115 control_listen(struct control_sock *cs)
116 {
117 	if (cs->cs_name == NULL)
118 		return (0);
119 
120 	if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) {
121 		log_warn("%s: listen", __func__);
122 		return (-1);
123 	}
124 
125 	event_set(&cs->cs_ev, cs->cs_fd, EV_READ,
126 	    control_accept, cs);
127 	event_add(&cs->cs_ev, NULL);
128 	evtimer_set(&cs->cs_evt, control_accept, cs);
129 
130 	return (0);
131 }
132 
133 void
134 control_cleanup(struct control_sock *cs)
135 {
136 	if (cs->cs_name == NULL)
137 		return;
138 	event_del(&cs->cs_ev);
139 	event_del(&cs->cs_evt);
140 	(void)unlink(cs->cs_name);
141 }
142 
143 /* ARGSUSED */
144 void
145 control_accept(int listenfd, short event, void *arg)
146 {
147 	struct control_sock	*cs = arg;
148 	int			 connfd;
149 	socklen_t		 len;
150 	struct sockaddr_un	 sun;
151 	struct ctl_conn		*c;
152 
153 	event_add(&cs->cs_ev, NULL);
154 	if ((event & EV_TIMEOUT))
155 		return;
156 
157 	len = sizeof(sun);
158 	if ((connfd = accept(listenfd,
159 	    (struct sockaddr *)&sun, &len)) == -1) {
160 		/*
161 		 * Pause accept if we are out of file descriptors, or
162 		 * libevent will haunt us here too.
163 		 */
164 		if (errno == ENFILE || errno == EMFILE) {
165 			struct timeval evtpause = { 1, 0 };
166 
167 			event_del(&cs->cs_ev);
168 			evtimer_add(&cs->cs_evt, &evtpause);
169 		} else if (errno != EWOULDBLOCK && errno != EINTR)
170 			log_warn("%s: accept", __func__);
171 		return;
172 	}
173 
174 	socket_set_blockmode(connfd, BM_NONBLOCK);
175 
176 	if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
177 		log_warn("%s", __func__);
178 		close(connfd);
179 		return;
180 	}
181 
182 	imsg_init(&c->iev.ibuf, connfd);
183 	c->iev.handler = control_dispatch_imsg;
184 	c->iev.events = EV_READ;
185 	c->iev.data = cs;
186 	event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
187 	    c->iev.handler, c->iev.data);
188 	event_add(&c->iev.ev, NULL);
189 
190 	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
191 }
192 
193 struct ctl_conn *
194 control_connbyfd(int fd)
195 {
196 	struct ctl_conn	*c;
197 
198 	for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.fd != fd;
199 	    c = TAILQ_NEXT(c, entry))
200 		;	/* nothing */
201 
202 	return (c);
203 }
204 
205 void
206 control_close(int fd, struct control_sock *cs)
207 {
208 	struct ctl_conn	*c;
209 
210 	if ((c = control_connbyfd(fd)) == NULL) {
211 		log_warn("%s: fd %d: not found", __func__, fd);
212 		return;
213 	}
214 
215 	msgbuf_clear(&c->iev.ibuf.w);
216 	TAILQ_REMOVE(&ctl_conns, c, entry);
217 
218 	event_del(&c->iev.ev);
219 	close(c->iev.ibuf.fd);
220 
221 	/* Some file descriptors are available again. */
222 	if (evtimer_pending(&cs->cs_evt, NULL)) {
223 		evtimer_del(&cs->cs_evt);
224 		event_add(&cs->cs_ev, NULL);
225 	}
226 
227 	free(c);
228 }
229 
230 /* ARGSUSED */
231 void
232 control_dispatch_imsg(int fd, short event, void *arg)
233 {
234 	struct control_sock	*cs = arg;
235 	struct iked		*env = cs->cs_env;
236 	struct ctl_conn		*c;
237 	struct imsg		 imsg;
238 	int			 n, v;
239 
240 	if ((c = control_connbyfd(fd)) == NULL) {
241 		log_warn("%s: fd %d: not found", __func__, fd);
242 		return;
243 	}
244 
245 	switch (event) {
246 	case EV_READ:
247 		if ((n = imsg_read(&c->iev.ibuf)) == -1 || n == 0) {
248 			control_close(fd, cs);
249 			return;
250 		}
251 		break;
252 	case EV_WRITE:
253 		if (msgbuf_write(&c->iev.ibuf.w) < 0) {
254 			control_close(fd, cs);
255 			return;
256 		}
257 		imsg_event_add(&c->iev);
258 		return;
259 	default:
260 		fatalx("unknown event");
261 	}
262 
263 	for (;;) {
264 		if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
265 			control_close(fd, cs);
266 			return;
267 		}
268 
269 		if (n == 0)
270 			break;
271 
272 		control_imsg_forward(&imsg);
273 
274 		switch (imsg.hdr.type) {
275 		case IMSG_CTL_NOTIFY:
276 			if (c->flags & CTL_CONN_NOTIFY) {
277 				log_debug("%s: "
278 				    "client requested notify more than once",
279 				    __func__);
280 				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
281 				    0, 0, -1, NULL, 0);
282 				break;
283 			}
284 			c->flags |= CTL_CONN_NOTIFY;
285 			break;
286 		case IMSG_CTL_VERBOSE:
287 			IMSG_SIZE_CHECK(&imsg, &v);
288 
289 			memcpy(&v, imsg.data, sizeof(v));
290 			log_verbose(v);
291 
292 			proc_forward_imsg(env, &imsg, PROC_PARENT);
293 			proc_forward_imsg(env, &imsg, PROC_IKEV2);
294 			proc_forward_imsg(env, &imsg, PROC_IKEV1);
295 			break;
296 		case IMSG_CTL_RELOAD:
297 		case IMSG_CTL_RESET:
298 		case IMSG_CTL_COUPLE:
299 		case IMSG_CTL_DECOUPLE:
300 		case IMSG_CTL_ACTIVE:
301 		case IMSG_CTL_PASSIVE:
302 			proc_forward_imsg(env, &imsg, PROC_PARENT);
303 			break;
304 		default:
305 			log_debug("%s: error handling imsg %d",
306 			    __func__, imsg.hdr.type);
307 			break;
308 		}
309 		imsg_free(&imsg);
310 	}
311 
312 	imsg_event_add(&c->iev);
313 }
314 
315 void
316 control_imsg_forward(struct imsg *imsg)
317 {
318 	struct ctl_conn *c;
319 
320 	TAILQ_FOREACH(c, &ctl_conns, entry)
321 		if (c->flags & CTL_CONN_NOTIFY)
322 			imsg_compose(&c->iev.ibuf, imsg->hdr.type,
323 			    0, imsg->hdr.pid, -1, imsg->data,
324 			    imsg->hdr.len - IMSG_HEADER_SIZE);
325 }
326