xref: /openbsd/sbin/iked/control.c (revision 891d7ab6)
1 /*	$OpenBSD: control.c,v 1.7 2011/05/09 11:15:18 reyk 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);
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 | EV_PERSIST,
126 	    control_accept, cs->cs_env);
127 	event_add(&cs->cs_ev, NULL);
128 
129 	return (0);
130 }
131 
132 void
133 control_cleanup(struct control_sock *cs)
134 {
135 	if (cs->cs_name == NULL)
136 		return;
137 	(void)unlink(cs->cs_name);
138 }
139 
140 /* ARGSUSED */
141 void
142 control_accept(int listenfd, short event, void *arg)
143 {
144 	struct iked		*env = arg;
145 	int			 connfd;
146 	socklen_t		 len;
147 	struct sockaddr_un	 sun;
148 	struct ctl_conn		*c;
149 
150 	len = sizeof(sun);
151 	if ((connfd = accept(listenfd,
152 	    (struct sockaddr *)&sun, &len)) == -1) {
153 		if (errno != EWOULDBLOCK && errno != EINTR)
154 			log_warn("%s: accept", __func__);
155 		return;
156 	}
157 
158 	socket_set_blockmode(connfd, BM_NONBLOCK);
159 
160 	if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
161 		log_warn("%s", __func__);
162 		close(connfd);
163 		return;
164 	}
165 
166 	imsg_init(&c->iev.ibuf, connfd);
167 	c->iev.handler = control_dispatch_imsg;
168 	c->iev.events = EV_READ;
169 	c->iev.data = env;
170 	event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
171 	    c->iev.handler, c->iev.data);
172 	event_add(&c->iev.ev, NULL);
173 
174 	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
175 }
176 
177 struct ctl_conn *
178 control_connbyfd(int fd)
179 {
180 	struct ctl_conn	*c;
181 
182 	for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.fd != fd;
183 	    c = TAILQ_NEXT(c, entry))
184 		;	/* nothing */
185 
186 	return (c);
187 }
188 
189 void
190 control_close(int fd)
191 {
192 	struct ctl_conn	*c;
193 
194 	if ((c = control_connbyfd(fd)) == NULL) {
195 		log_warn("%s: fd %d: not found", __func__, fd);
196 		return;
197 	}
198 
199 	msgbuf_clear(&c->iev.ibuf.w);
200 	TAILQ_REMOVE(&ctl_conns, c, entry);
201 
202 	event_del(&c->iev.ev);
203 	close(c->iev.ibuf.fd);
204 	free(c);
205 }
206 
207 /* ARGSUSED */
208 void
209 control_dispatch_imsg(int fd, short event, void *arg)
210 {
211 	struct iked		*env = arg;
212 	struct ctl_conn		*c;
213 	struct imsg		 imsg;
214 	int			 n, v;
215 
216 	if ((c = control_connbyfd(fd)) == NULL) {
217 		log_warn("%s: fd %d: not found", __func__, fd);
218 		return;
219 	}
220 
221 	switch (event) {
222 	case EV_READ:
223 		if ((n = imsg_read(&c->iev.ibuf)) == -1 || n == 0) {
224 			control_close(fd);
225 			return;
226 		}
227 		break;
228 	case EV_WRITE:
229 		if (msgbuf_write(&c->iev.ibuf.w) < 0) {
230 			control_close(fd);
231 			return;
232 		}
233 		imsg_event_add(&c->iev);
234 		return;
235 	default:
236 		fatalx("unknown event");
237 	}
238 
239 	for (;;) {
240 		if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
241 			control_close(fd);
242 			return;
243 		}
244 
245 		if (n == 0)
246 			break;
247 
248 		control_imsg_forward(&imsg);
249 
250 		switch (imsg.hdr.type) {
251 		case IMSG_CTL_NOTIFY:
252 			if (c->flags & CTL_CONN_NOTIFY) {
253 				log_debug("%s: "
254 				    "client requested notify more than once",
255 				    __func__);
256 				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
257 				    0, 0, -1, NULL, 0);
258 				break;
259 			}
260 			c->flags |= CTL_CONN_NOTIFY;
261 			break;
262 		case IMSG_CTL_VERBOSE:
263 			IMSG_SIZE_CHECK(&imsg, &v);
264 
265 			memcpy(&v, imsg.data, sizeof(v));
266 			log_verbose(v);
267 
268 			proc_forward_imsg(env, &imsg, PROC_PARENT);
269 			proc_forward_imsg(env, &imsg, PROC_IKEV2);
270 			proc_forward_imsg(env, &imsg, PROC_IKEV1);
271 			break;
272 		case IMSG_CTL_RELOAD:
273 		case IMSG_CTL_RESET:
274 		case IMSG_CTL_COUPLE:
275 		case IMSG_CTL_DECOUPLE:
276 		case IMSG_CTL_ACTIVE:
277 		case IMSG_CTL_PASSIVE:
278 			proc_forward_imsg(env, &imsg, PROC_PARENT);
279 			break;
280 		default:
281 			log_debug("%s: error handling imsg %d",
282 			    __func__, imsg.hdr.type);
283 			break;
284 		}
285 		imsg_free(&imsg);
286 	}
287 
288 	imsg_event_add(&c->iev);
289 }
290 
291 void
292 control_imsg_forward(struct imsg *imsg)
293 {
294 	struct ctl_conn *c;
295 
296 	TAILQ_FOREACH(c, &ctl_conns, entry)
297 		if (c->flags & CTL_CONN_NOTIFY)
298 			imsg_compose(&c->iev.ibuf, imsg->hdr.type,
299 			    0, imsg->hdr.pid, -1, imsg->data,
300 			    imsg->hdr.len - IMSG_HEADER_SIZE);
301 }
302