1/*	$OpenBSD: privsep.c,v 1.7 2004/05/10 18:34:42 deraadt Exp $ */
2
3/*
4 * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
15 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16 * OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/cdefs.h>
20#include "dhcpd.h"
21#include "privsep.h"
22
23struct buf *
24buf_open(size_t len)
25{
26	struct buf	*buf;
27
28	if ((buf = calloc(1, sizeof(struct buf))) == NULL)
29		return (NULL);
30	if ((buf->buf = malloc(len)) == NULL) {
31		free(buf);
32		return (NULL);
33	}
34	buf->size = len;
35
36	return (buf);
37}
38
39int
40buf_add(struct buf *buf, const void *data, size_t len)
41{
42	if (buf->wpos + len > buf->size)
43		return (-1);
44
45	memcpy(buf->buf + buf->wpos, data, len);
46	buf->wpos += len;
47	return (0);
48}
49
50int
51buf_close(int sock, struct buf *buf)
52{
53	ssize_t	n;
54
55	do {
56		n = write(sock, buf->buf + buf->rpos, buf->size - buf->rpos);
57		if (n != -1)
58			buf->rpos += n;
59		if (n == 0) {			/* connection closed */
60			errno = 0;
61			return (-1);
62		}
63	} while (n == -1 && (errno == EAGAIN || errno == EINTR));
64
65	if (buf->rpos < buf->size)
66		error("short write: wanted %lu got %ld bytes",
67		    (unsigned long)buf->size, (long)buf->rpos);
68
69	free(buf->buf);
70	free(buf);
71	return (n);
72}
73
74ssize_t
75buf_read(int sock, void *buf, size_t nbytes)
76{
77	ssize_t	n;
78	size_t r = 0;
79	char *p = buf;
80
81	do {
82		n = read(sock, p, nbytes);
83		if (n == 0)
84			error("connection closed");
85		if (n != -1) {
86			r += (size_t)n;
87			p += n;
88			nbytes -= n;
89		}
90	} while (n == -1 && (errno == EINTR || errno == EAGAIN));
91
92	if (n == -1)
93		error("buf_read: %m");
94
95	if (r < nbytes)
96		error("short read: wanted %lu got %ld bytes",
97		    (unsigned long)nbytes, (long)r);
98
99	return (r);
100}
101
102void
103dispatch_imsg(struct interface_info *ifix, int fd)
104{
105	struct imsg_hdr		 hdr;
106	char			*medium, *reason, *filename,
107				*servername, *prefix;
108	size_t			 medium_len, reason_len, filename_len,
109				 servername_len, optlen, prefix_len, totlen;
110	struct client_lease	 lease;
111	int			 ret, i;
112	struct buf		*buf;
113	u_int16_t		mtu;
114
115	buf_read(fd, &hdr, sizeof(hdr));
116
117	switch (hdr.code) {
118	case IMSG_SCRIPT_INIT:
119		if (hdr.len < sizeof(hdr) + sizeof(size_t))
120			error("corrupted message received");
121		buf_read(fd, &medium_len, sizeof(medium_len));
122		if (hdr.len < medium_len + sizeof(size_t) + sizeof(hdr)
123		    + sizeof(size_t) || medium_len == SIZE_T_MAX)
124			error("corrupted message received");
125		if (medium_len > 0) {
126			if ((medium = calloc(1, medium_len + 1)) == NULL)
127				error("%m");
128			buf_read(fd, medium, medium_len);
129		} else
130			medium = NULL;
131
132		buf_read(fd, &reason_len, sizeof(reason_len));
133		if (hdr.len < medium_len + reason_len + sizeof(hdr) ||
134		    reason_len == SIZE_T_MAX)
135			error("corrupted message received");
136		if (reason_len > 0) {
137			if ((reason = calloc(1, reason_len + 1)) == NULL)
138				error("%m");
139			buf_read(fd, reason, reason_len);
140		} else
141			reason = NULL;
142
143		priv_script_init(reason, medium);
144		free(reason);
145		free(medium);
146		break;
147	case IMSG_SCRIPT_WRITE_PARAMS:
148		bzero(&lease, sizeof lease);
149		totlen = sizeof(hdr) + sizeof(lease) + sizeof(size_t);
150		if (hdr.len < totlen)
151			error("corrupted message received");
152		buf_read(fd, &lease, sizeof(lease));
153
154		buf_read(fd, &filename_len, sizeof(filename_len));
155		totlen += filename_len + sizeof(size_t);
156		if (hdr.len < totlen || filename_len == SIZE_T_MAX)
157			error("corrupted message received");
158		if (filename_len > 0) {
159			if ((filename = calloc(1, filename_len + 1)) == NULL)
160				error("%m");
161			buf_read(fd, filename, filename_len);
162		} else
163			filename = NULL;
164
165		buf_read(fd, &servername_len, sizeof(servername_len));
166		totlen += servername_len + sizeof(size_t);
167		if (hdr.len < totlen || servername_len == SIZE_T_MAX)
168			error("corrupted message received");
169		if (servername_len > 0) {
170			if ((servername =
171			    calloc(1, servername_len + 1)) == NULL)
172				error("%m");
173			buf_read(fd, servername, servername_len);
174		} else
175			servername = NULL;
176
177		buf_read(fd, &prefix_len, sizeof(prefix_len));
178		totlen += prefix_len;
179		if (hdr.len < totlen || prefix_len == SIZE_T_MAX)
180			error("corrupted message received");
181		if (prefix_len > 0) {
182			if ((prefix = calloc(1, prefix_len + 1)) == NULL)
183				error("%m");
184			buf_read(fd, prefix, prefix_len);
185		} else
186			prefix = NULL;
187
188		for (i = 0; i < 256; i++) {
189			totlen += sizeof(optlen);
190			if (hdr.len < totlen)
191				error("corrupted message received");
192			buf_read(fd, &optlen, sizeof(optlen));
193			lease.options[i].data = NULL;
194			lease.options[i].len = optlen;
195			if (optlen > 0) {
196				totlen += optlen;
197				if (hdr.len < totlen || optlen == SIZE_T_MAX)
198					error("corrupted message received");
199				lease.options[i].data =
200				    calloc(1, optlen + 1);
201				if (lease.options[i].data == NULL)
202				    error("%m");
203				buf_read(fd, lease.options[i].data, optlen);
204			}
205		}
206		lease.server_name = servername;
207		lease.filename = filename;
208
209		priv_script_write_params(prefix, &lease);
210
211		free(servername);
212		free(filename);
213		free(prefix);
214		for (i = 0; i < 256; i++)
215			if (lease.options[i].len > 0)
216				free(lease.options[i].data);
217		break;
218	case IMSG_SCRIPT_GO:
219		if (hdr.len != sizeof(hdr))
220			error("corrupted message received");
221
222		ret = priv_script_go();
223
224		hdr.code = IMSG_SCRIPT_GO_RET;
225		hdr.len = sizeof(struct imsg_hdr) + sizeof(int);
226		if ((buf = buf_open(hdr.len)) == NULL)
227			error("buf_open: %m");
228		if (buf_add(buf, &hdr, sizeof(hdr)))
229			error("buf_add: %m");
230		if (buf_add(buf, &ret, sizeof(ret)))
231			error("buf_add: %m");
232		if (buf_close(fd, buf) == -1)
233			error("buf_close: %m");
234		break;
235	case IMSG_SEND_PACKET:
236		send_packet_priv(ifix, &hdr, fd);
237		break;
238	case IMSG_SET_INTERFACE_MTU:
239		if (hdr.len < sizeof(hdr) + sizeof(u_int16_t))
240			error("corrupted message received");
241
242		buf_read(fd, &mtu, sizeof(u_int16_t));
243		interface_set_mtu_priv(ifix->name, mtu);
244		break;
245	default:
246		error("received unknown message, code %d", hdr.code);
247	}
248}
249