1147072Sbrooks/*	$OpenBSD: privsep.c,v 1.7 2004/05/10 18:34:42 deraadt Exp $ */
2147072Sbrooks
3147072Sbrooks/*
4147072Sbrooks * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
5147072Sbrooks *
6147072Sbrooks * Permission to use, copy, modify, and distribute this software for any
7147072Sbrooks * purpose with or without fee is hereby granted, provided that the above
8147072Sbrooks * copyright notice and this permission notice appear in all copies.
9147072Sbrooks *
10147072Sbrooks * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11147072Sbrooks * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12147072Sbrooks * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13147072Sbrooks * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14147072Sbrooks * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
15147072Sbrooks * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16147072Sbrooks * OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE.
17147072Sbrooks */
18147072Sbrooks
19149399Sbrooks#include <sys/cdefs.h>
20149399Sbrooks__FBSDID("$FreeBSD$");
21149399Sbrooks
22147072Sbrooks#include "dhcpd.h"
23147072Sbrooks#include "privsep.h"
24147072Sbrooks
25147072Sbrooksstruct buf *
26147072Sbrooksbuf_open(size_t len)
27147072Sbrooks{
28147072Sbrooks	struct buf	*buf;
29147072Sbrooks
30147072Sbrooks	if ((buf = calloc(1, sizeof(struct buf))) == NULL)
31147072Sbrooks		return (NULL);
32147072Sbrooks	if ((buf->buf = malloc(len)) == NULL) {
33147072Sbrooks		free(buf);
34147072Sbrooks		return (NULL);
35147072Sbrooks	}
36147072Sbrooks	buf->size = len;
37147072Sbrooks
38147072Sbrooks	return (buf);
39147072Sbrooks}
40147072Sbrooks
41147072Sbrooksint
42147072Sbrooksbuf_add(struct buf *buf, void *data, size_t len)
43147072Sbrooks{
44147072Sbrooks	if (buf->wpos + len > buf->size)
45147072Sbrooks		return (-1);
46147072Sbrooks
47147072Sbrooks	memcpy(buf->buf + buf->wpos, data, len);
48147072Sbrooks	buf->wpos += len;
49147072Sbrooks	return (0);
50147072Sbrooks}
51147072Sbrooks
52147072Sbrooksint
53147072Sbrooksbuf_close(int sock, struct buf *buf)
54147072Sbrooks{
55147072Sbrooks	ssize_t	n;
56147072Sbrooks
57147072Sbrooks	do {
58147072Sbrooks		n = write(sock, buf->buf + buf->rpos, buf->size - buf->rpos);
59147072Sbrooks		if (n != -1)
60147072Sbrooks			buf->rpos += n;
61147072Sbrooks		if (n == 0) {			/* connection closed */
62147072Sbrooks			errno = 0;
63147072Sbrooks			return (-1);
64147072Sbrooks		}
65147072Sbrooks	} while (n == -1 && (errno == EAGAIN || errno == EINTR));
66147072Sbrooks
67147072Sbrooks	if (buf->rpos < buf->size)
68147072Sbrooks		error("short write: wanted %lu got %ld bytes",
69147072Sbrooks		    (unsigned long)buf->size, (long)buf->rpos);
70147072Sbrooks
71147072Sbrooks	free(buf->buf);
72147072Sbrooks	free(buf);
73147072Sbrooks	return (n);
74147072Sbrooks}
75147072Sbrooks
76147072Sbrooksssize_t
77147072Sbrooksbuf_read(int sock, void *buf, size_t nbytes)
78147072Sbrooks{
79147072Sbrooks	ssize_t	n, r = 0;
80147072Sbrooks	char *p = buf;
81147072Sbrooks
82147072Sbrooks	do {
83147072Sbrooks		n = read(sock, p, nbytes);
84147072Sbrooks		if (n == 0)
85147072Sbrooks			error("connection closed");
86147072Sbrooks		if (n != -1) {
87147072Sbrooks			r += n;
88147072Sbrooks			p += n;
89147072Sbrooks			nbytes -= n;
90147072Sbrooks		}
91147072Sbrooks	} while (n == -1 && (errno == EINTR || errno == EAGAIN));
92147072Sbrooks
93147072Sbrooks	if (n == -1)
94147072Sbrooks		error("buf_read: %m");
95147072Sbrooks
96147072Sbrooks	if (r < nbytes)
97147072Sbrooks		error("short read: wanted %lu got %ld bytes",
98147072Sbrooks		    (unsigned long)nbytes, (long)r);
99147072Sbrooks
100147072Sbrooks	return (r);
101147072Sbrooks}
102147072Sbrooks
103147072Sbrooksvoid
104147072Sbrooksdispatch_imsg(int fd)
105147072Sbrooks{
106147072Sbrooks	struct imsg_hdr		 hdr;
107147072Sbrooks	char			*medium, *reason, *filename,
108147072Sbrooks				*servername, *prefix;
109147072Sbrooks	size_t			 medium_len, reason_len, filename_len,
110147072Sbrooks				 servername_len, prefix_len, totlen;
111147072Sbrooks	struct client_lease	 lease;
112147072Sbrooks	int			 ret, i, optlen;
113147072Sbrooks	struct buf		*buf;
114147072Sbrooks
115147072Sbrooks	buf_read(fd, &hdr, sizeof(hdr));
116147072Sbrooks
117147072Sbrooks	switch (hdr.code) {
118147072Sbrooks	case IMSG_SCRIPT_INIT:
119147072Sbrooks		if (hdr.len < sizeof(hdr) + sizeof(size_t))
120147072Sbrooks			error("corrupted message received");
121147072Sbrooks		buf_read(fd, &medium_len, sizeof(medium_len));
122147072Sbrooks		if (hdr.len < medium_len + sizeof(size_t) + sizeof(hdr)
123147072Sbrooks		    + sizeof(size_t) || medium_len == SIZE_T_MAX)
124147072Sbrooks			error("corrupted message received");
125147072Sbrooks		if (medium_len > 0) {
126147072Sbrooks			if ((medium = calloc(1, medium_len + 1)) == NULL)
127147072Sbrooks				error("%m");
128147072Sbrooks			buf_read(fd, medium, medium_len);
129147072Sbrooks		} else
130147072Sbrooks			medium = NULL;
131147072Sbrooks
132147072Sbrooks		buf_read(fd, &reason_len, sizeof(reason_len));
133147072Sbrooks		if (hdr.len < medium_len + reason_len + sizeof(hdr) ||
134147072Sbrooks		    reason_len == SIZE_T_MAX)
135147072Sbrooks			error("corrupted message received");
136147072Sbrooks		if (reason_len > 0) {
137147072Sbrooks			if ((reason = calloc(1, reason_len + 1)) == NULL)
138147072Sbrooks				error("%m");
139147072Sbrooks			buf_read(fd, reason, reason_len);
140147072Sbrooks		} else
141147072Sbrooks			reason = NULL;
142147072Sbrooks
143147072Sbrooks		priv_script_init(reason, medium);
144147072Sbrooks		free(reason);
145147072Sbrooks		free(medium);
146147072Sbrooks		break;
147147072Sbrooks	case IMSG_SCRIPT_WRITE_PARAMS:
148147072Sbrooks		bzero(&lease, sizeof lease);
149147072Sbrooks		totlen = sizeof(hdr) + sizeof(lease) + sizeof(size_t);
150147072Sbrooks		if (hdr.len < totlen)
151147072Sbrooks			error("corrupted message received");
152147072Sbrooks		buf_read(fd, &lease, sizeof(lease));
153147072Sbrooks
154147072Sbrooks		buf_read(fd, &filename_len, sizeof(filename_len));
155147072Sbrooks		totlen += filename_len + sizeof(size_t);
156147072Sbrooks		if (hdr.len < totlen || filename_len == SIZE_T_MAX)
157147072Sbrooks			error("corrupted message received");
158147072Sbrooks		if (filename_len > 0) {
159147072Sbrooks			if ((filename = calloc(1, filename_len + 1)) == NULL)
160147072Sbrooks				error("%m");
161147072Sbrooks			buf_read(fd, filename, filename_len);
162147072Sbrooks		} else
163147072Sbrooks			filename = NULL;
164147072Sbrooks
165147072Sbrooks		buf_read(fd, &servername_len, sizeof(servername_len));
166147072Sbrooks		totlen += servername_len + sizeof(size_t);
167147072Sbrooks		if (hdr.len < totlen || servername_len == SIZE_T_MAX)
168147072Sbrooks			error("corrupted message received");
169147072Sbrooks		if (servername_len > 0) {
170147072Sbrooks			if ((servername =
171147072Sbrooks			    calloc(1, servername_len + 1)) == NULL)
172147072Sbrooks				error("%m");
173147072Sbrooks			buf_read(fd, servername, servername_len);
174147072Sbrooks		} else
175147072Sbrooks			servername = NULL;
176147072Sbrooks
177147072Sbrooks		buf_read(fd, &prefix_len, sizeof(prefix_len));
178147072Sbrooks		totlen += prefix_len;
179147072Sbrooks		if (hdr.len < totlen || prefix_len == SIZE_T_MAX)
180147072Sbrooks			error("corrupted message received");
181147072Sbrooks		if (prefix_len > 0) {
182147072Sbrooks			if ((prefix = calloc(1, prefix_len + 1)) == NULL)
183147072Sbrooks				error("%m");
184147072Sbrooks			buf_read(fd, prefix, prefix_len);
185147072Sbrooks		} else
186147072Sbrooks			prefix = NULL;
187147072Sbrooks
188147072Sbrooks		for (i = 0; i < 256; i++) {
189147072Sbrooks			totlen += sizeof(optlen);
190147072Sbrooks			if (hdr.len < totlen)
191147072Sbrooks				error("corrupted message received");
192147072Sbrooks			buf_read(fd, &optlen, sizeof(optlen));
193147072Sbrooks			lease.options[i].data = NULL;
194147072Sbrooks			lease.options[i].len = optlen;
195147072Sbrooks			if (optlen > 0) {
196147072Sbrooks				totlen += optlen;
197147072Sbrooks				if (hdr.len < totlen || optlen == SIZE_T_MAX)
198147072Sbrooks					error("corrupted message received");
199147072Sbrooks				lease.options[i].data =
200147072Sbrooks				    calloc(1, optlen + 1);
201147072Sbrooks				if (lease.options[i].data == NULL)
202147072Sbrooks				    error("%m");
203147072Sbrooks				buf_read(fd, lease.options[i].data, optlen);
204147072Sbrooks			}
205147072Sbrooks		}
206147072Sbrooks		lease.server_name = servername;
207147072Sbrooks		lease.filename = filename;
208147072Sbrooks
209147072Sbrooks		priv_script_write_params(prefix, &lease);
210147072Sbrooks
211147072Sbrooks		free(servername);
212147072Sbrooks		free(filename);
213147072Sbrooks		free(prefix);
214147072Sbrooks		for (i = 0; i < 256; i++)
215147072Sbrooks			if (lease.options[i].len > 0)
216147072Sbrooks				free(lease.options[i].data);
217147072Sbrooks		break;
218147072Sbrooks	case IMSG_SCRIPT_GO:
219147072Sbrooks		if (hdr.len != sizeof(hdr))
220147072Sbrooks			error("corrupted message received");
221147072Sbrooks
222147072Sbrooks		ret = priv_script_go();
223147072Sbrooks
224147072Sbrooks		hdr.code = IMSG_SCRIPT_GO_RET;
225147072Sbrooks		hdr.len = sizeof(struct imsg_hdr) + sizeof(int);
226147072Sbrooks		if ((buf = buf_open(hdr.len)) == NULL)
227147072Sbrooks			error("buf_open: %m");
228147072Sbrooks		if (buf_add(buf, &hdr, sizeof(hdr)))
229147072Sbrooks			error("buf_add: %m");
230147072Sbrooks		if (buf_add(buf, &ret, sizeof(ret)))
231147072Sbrooks			error("buf_add: %m");
232147072Sbrooks		if (buf_close(fd, buf) == -1)
233147072Sbrooks			error("buf_close: %m");
234147072Sbrooks		break;
235147072Sbrooks	default:
236147072Sbrooks		error("received unknown message, code %d", hdr.code);
237147072Sbrooks	}
238147072Sbrooks}
239