xref: /freebsd/sbin/dhclient/privsep.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
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 
23 struct buf *
24 buf_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 
39 int
40 buf_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 
50 int
51 buf_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 
74 ssize_t
75 buf_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 
102 void
103 dispatch_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