xref: /freebsd/sbin/dhclient/privsep.c (revision 5dae51da3da0cc94d17bd67b308fad304ebec7e0)
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 __FBSDID("$FreeBSD$");
21 
22 #include "dhcpd.h"
23 #include "privsep.h"
24 
25 struct buf *
26 buf_open(size_t len)
27 {
28 	struct buf	*buf;
29 
30 	if ((buf = calloc(1, sizeof(struct buf))) == NULL)
31 		return (NULL);
32 	if ((buf->buf = malloc(len)) == NULL) {
33 		free(buf);
34 		return (NULL);
35 	}
36 	buf->size = len;
37 
38 	return (buf);
39 }
40 
41 int
42 buf_add(struct buf *buf, void *data, size_t len)
43 {
44 	if (buf->wpos + len > buf->size)
45 		return (-1);
46 
47 	memcpy(buf->buf + buf->wpos, data, len);
48 	buf->wpos += len;
49 	return (0);
50 }
51 
52 int
53 buf_close(int sock, struct buf *buf)
54 {
55 	ssize_t	n;
56 
57 	do {
58 		n = write(sock, buf->buf + buf->rpos, buf->size - buf->rpos);
59 		if (n != -1)
60 			buf->rpos += n;
61 		if (n == 0) {			/* connection closed */
62 			errno = 0;
63 			return (-1);
64 		}
65 	} while (n == -1 && (errno == EAGAIN || errno == EINTR));
66 
67 	if (buf->rpos < buf->size)
68 		error("short write: wanted %lu got %ld bytes",
69 		    (unsigned long)buf->size, (long)buf->rpos);
70 
71 	free(buf->buf);
72 	free(buf);
73 	return (n);
74 }
75 
76 ssize_t
77 buf_read(int sock, void *buf, size_t nbytes)
78 {
79 	ssize_t	n, r = 0;
80 	char *p = buf;
81 
82 	do {
83 		n = read(sock, p, nbytes);
84 		if (n == 0)
85 			error("connection closed");
86 		if (n != -1) {
87 			r += n;
88 			p += n;
89 			nbytes -= n;
90 		}
91 	} while (n == -1 && (errno == EINTR || errno == EAGAIN));
92 
93 	if (n == -1)
94 		error("buf_read: %m");
95 
96 	if (r < nbytes)
97 		error("short read: wanted %lu got %ld bytes",
98 		    (unsigned long)nbytes, (long)r);
99 
100 	return (r);
101 }
102 
103 void
104 dispatch_imsg(struct interface_info *ifi, int fd)
105 {
106 	struct imsg_hdr		 hdr;
107 	char			*medium, *reason, *filename,
108 				*servername, *prefix;
109 	size_t			 medium_len, reason_len, filename_len,
110 				 servername_len, prefix_len, totlen;
111 	struct client_lease	 lease;
112 	int			 ret, i, optlen;
113 	struct buf		*buf;
114 	u_int16_t		mtu;
115 
116 	buf_read(fd, &hdr, sizeof(hdr));
117 
118 	switch (hdr.code) {
119 	case IMSG_SCRIPT_INIT:
120 		if (hdr.len < sizeof(hdr) + sizeof(size_t))
121 			error("corrupted message received");
122 		buf_read(fd, &medium_len, sizeof(medium_len));
123 		if (hdr.len < medium_len + sizeof(size_t) + sizeof(hdr)
124 		    + sizeof(size_t) || medium_len == SIZE_T_MAX)
125 			error("corrupted message received");
126 		if (medium_len > 0) {
127 			if ((medium = calloc(1, medium_len + 1)) == NULL)
128 				error("%m");
129 			buf_read(fd, medium, medium_len);
130 		} else
131 			medium = NULL;
132 
133 		buf_read(fd, &reason_len, sizeof(reason_len));
134 		if (hdr.len < medium_len + reason_len + sizeof(hdr) ||
135 		    reason_len == SIZE_T_MAX)
136 			error("corrupted message received");
137 		if (reason_len > 0) {
138 			if ((reason = calloc(1, reason_len + 1)) == NULL)
139 				error("%m");
140 			buf_read(fd, reason, reason_len);
141 		} else
142 			reason = NULL;
143 
144 		priv_script_init(reason, medium);
145 		free(reason);
146 		free(medium);
147 		break;
148 	case IMSG_SCRIPT_WRITE_PARAMS:
149 		bzero(&lease, sizeof lease);
150 		totlen = sizeof(hdr) + sizeof(lease) + sizeof(size_t);
151 		if (hdr.len < totlen)
152 			error("corrupted message received");
153 		buf_read(fd, &lease, sizeof(lease));
154 
155 		buf_read(fd, &filename_len, sizeof(filename_len));
156 		totlen += filename_len + sizeof(size_t);
157 		if (hdr.len < totlen || filename_len == SIZE_T_MAX)
158 			error("corrupted message received");
159 		if (filename_len > 0) {
160 			if ((filename = calloc(1, filename_len + 1)) == NULL)
161 				error("%m");
162 			buf_read(fd, filename, filename_len);
163 		} else
164 			filename = NULL;
165 
166 		buf_read(fd, &servername_len, sizeof(servername_len));
167 		totlen += servername_len + sizeof(size_t);
168 		if (hdr.len < totlen || servername_len == SIZE_T_MAX)
169 			error("corrupted message received");
170 		if (servername_len > 0) {
171 			if ((servername =
172 			    calloc(1, servername_len + 1)) == NULL)
173 				error("%m");
174 			buf_read(fd, servername, servername_len);
175 		} else
176 			servername = NULL;
177 
178 		buf_read(fd, &prefix_len, sizeof(prefix_len));
179 		totlen += prefix_len;
180 		if (hdr.len < totlen || prefix_len == SIZE_T_MAX)
181 			error("corrupted message received");
182 		if (prefix_len > 0) {
183 			if ((prefix = calloc(1, prefix_len + 1)) == NULL)
184 				error("%m");
185 			buf_read(fd, prefix, prefix_len);
186 		} else
187 			prefix = NULL;
188 
189 		for (i = 0; i < 256; i++) {
190 			totlen += sizeof(optlen);
191 			if (hdr.len < totlen)
192 				error("corrupted message received");
193 			buf_read(fd, &optlen, sizeof(optlen));
194 			lease.options[i].data = NULL;
195 			lease.options[i].len = optlen;
196 			if (optlen > 0) {
197 				totlen += optlen;
198 				if (hdr.len < totlen || optlen == SIZE_T_MAX)
199 					error("corrupted message received");
200 				lease.options[i].data =
201 				    calloc(1, optlen + 1);
202 				if (lease.options[i].data == NULL)
203 				    error("%m");
204 				buf_read(fd, lease.options[i].data, optlen);
205 			}
206 		}
207 		lease.server_name = servername;
208 		lease.filename = filename;
209 
210 		priv_script_write_params(prefix, &lease);
211 
212 		free(servername);
213 		free(filename);
214 		free(prefix);
215 		for (i = 0; i < 256; i++)
216 			if (lease.options[i].len > 0)
217 				free(lease.options[i].data);
218 		break;
219 	case IMSG_SCRIPT_GO:
220 		if (hdr.len != sizeof(hdr))
221 			error("corrupted message received");
222 
223 		ret = priv_script_go();
224 
225 		hdr.code = IMSG_SCRIPT_GO_RET;
226 		hdr.len = sizeof(struct imsg_hdr) + sizeof(int);
227 		if ((buf = buf_open(hdr.len)) == NULL)
228 			error("buf_open: %m");
229 		if (buf_add(buf, &hdr, sizeof(hdr)))
230 			error("buf_add: %m");
231 		if (buf_add(buf, &ret, sizeof(ret)))
232 			error("buf_add: %m");
233 		if (buf_close(fd, buf) == -1)
234 			error("buf_close: %m");
235 		break;
236 	case IMSG_SEND_PACKET:
237 		send_packet_priv(ifi, &hdr, fd);
238 		break;
239 	case IMSG_SET_INTERFACE_MTU:
240 		if (hdr.len < sizeof(hdr) + sizeof(u_int16_t))
241 			error("corrupted message received");
242 
243 		buf_read(fd, &mtu, sizeof(u_int16_t));
244 		interface_set_mtu_priv(ifi->name, mtu);
245 		break;
246 	default:
247 		error("received unknown message, code %d", hdr.code);
248 	}
249 }
250