control.c revision 1.7
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
43struct ctl_connlist ctl_conns;
44
45void
46	 control_accept(int, short, void *);
47struct ctl_conn
48	*control_connbyfd(int);
49void	 control_close(int);
50void	 control_dispatch_imsg(int, short, void *);
51void	 control_imsg_forward(struct imsg *);
52
53int
54control_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
114int
115control_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
132void
133control_cleanup(struct control_sock *cs)
134{
135	if (cs->cs_name == NULL)
136		return;
137	(void)unlink(cs->cs_name);
138}
139
140/* ARGSUSED */
141void
142control_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
177struct ctl_conn *
178control_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
189void
190control_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 */
208void
209control_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
291void
292control_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