xref: /freebsd/sbin/hastd/hast_proto.c (revision 1670a1c2a47d10ecccd001970b859caf93cd3b6e)
1 /*-
2  * Copyright (c) 2009-2010 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/endian.h>
34 
35 #include <assert.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <strings.h>
39 
40 #ifdef HAVE_CRYPTO
41 #include <openssl/sha.h>
42 #endif
43 
44 #include <hast.h>
45 #include <ebuf.h>
46 #include <nv.h>
47 #include <pjdlog.h>
48 #include <proto.h>
49 
50 #include "hast_proto.h"
51 
52 struct hast_main_header {
53 	/* Protocol version. */
54 	uint8_t		version;
55 	/* Size of nv headers. */
56 	uint32_t	size;
57 } __packed;
58 
59 typedef int hps_send_t(struct hast_resource *, struct nv *nv, void **, size_t *, bool *);
60 typedef int hps_recv_t(struct hast_resource *, struct nv *nv, void **, size_t *, bool *);
61 
62 struct hast_pipe_stage {
63 	const char	*hps_name;
64 	hps_send_t	*hps_send;
65 	hps_recv_t	*hps_recv;
66 };
67 
68 static int compression_send(struct hast_resource *res, struct nv *nv,
69     void **datap, size_t *sizep, bool *freedatap);
70 static int compression_recv(struct hast_resource *res, struct nv *nv,
71     void **datap, size_t *sizep, bool *freedatap);
72 #ifdef HAVE_CRYPTO
73 static int checksum_send(struct hast_resource *res, struct nv *nv,
74     void **datap, size_t *sizep, bool *freedatap);
75 static int checksum_recv(struct hast_resource *res, struct nv *nv,
76     void **datap, size_t *sizep, bool *freedatap);
77 #endif
78 
79 static struct hast_pipe_stage pipeline[] = {
80 	{ "compression", compression_send, compression_recv },
81 #ifdef HAVE_CRYPTO
82 	{ "checksum", checksum_send, checksum_recv }
83 #endif
84 };
85 
86 static int
87 compression_send(struct hast_resource *res, struct nv *nv, void **datap,
88     size_t *sizep, bool *freedatap)
89 {
90 	unsigned char *newbuf;
91 
92 	res = res;	/* TODO */
93 
94 	/*
95 	 * TODO: For now we emulate compression.
96 	 * At 80% probability we succeed to compress data, which means we
97 	 * allocate new buffer, copy the data over set *freedatap to true.
98 	 */
99 
100 	if (arc4random_uniform(100) < 80) {
101 		uint32_t *origsize;
102 
103 		/*
104 		 * Compression succeeded (but we will grow by 4 bytes, not
105 		 * shrink for now).
106 		 */
107 		newbuf = malloc(sizeof(uint32_t) + *sizep);
108 		if (newbuf == NULL)
109 			return (-1);
110 		origsize = (void *)newbuf;
111 		*origsize = htole32((uint32_t)*sizep);
112 		nv_add_string(nv, "null", "compression");
113 		if (nv_error(nv) != 0) {
114 			free(newbuf);
115 			errno = nv_error(nv);
116 			return (-1);
117 		}
118 		bcopy(*datap, newbuf + sizeof(uint32_t), *sizep);
119 		if (*freedatap)
120 			free(*datap);
121 		*freedatap = true;
122 		*datap = newbuf;
123 		*sizep = sizeof(uint32_t) + *sizep;
124 	} else {
125 		/*
126 		 * Compression failed, so we leave everything as it was.
127 		 * It is not critical for compression to succeed.
128 		 */
129 	}
130 
131 	return (0);
132 }
133 
134 static int
135 compression_recv(struct hast_resource *res, struct nv *nv, void **datap,
136     size_t *sizep, bool *freedatap)
137 {
138 	unsigned char *newbuf;
139 	const char *algo;
140 	size_t origsize;
141 
142 	res = res;	/* TODO */
143 
144 	/*
145 	 * TODO: For now we emulate compression.
146 	 */
147 
148 	algo = nv_get_string(nv, "compression");
149 	if (algo == NULL)
150 		return (0);	/* No compression. */
151 	if (strcmp(algo, "null") != 0) {
152 		pjdlog_error("Unknown compression algorithm '%s'.", algo);
153 		return (-1);	/* Unknown compression algorithm. */
154 	}
155 
156 	origsize = le32toh(*(uint32_t *)*datap);
157 	newbuf = malloc(origsize);
158 	if (newbuf == NULL)
159 		return (-1);
160 	bcopy((unsigned char *)*datap + sizeof(uint32_t), newbuf, origsize);
161 	if (*freedatap)
162 		free(*datap);
163 	*freedatap = true;
164 	*datap = newbuf;
165 	*sizep = origsize;
166 
167 	return (0);
168 }
169 
170 #ifdef HAVE_CRYPTO
171 static int
172 checksum_send(struct hast_resource *res, struct nv *nv, void **datap,
173     size_t *sizep, bool *freedatap __unused)
174 {
175 	unsigned char hash[SHA256_DIGEST_LENGTH];
176 	SHA256_CTX ctx;
177 
178 	res = res;	/* TODO */
179 
180 	SHA256_Init(&ctx);
181 	SHA256_Update(&ctx, *datap, *sizep);
182 	SHA256_Final(hash, &ctx);
183 
184 	nv_add_string(nv, "sha256", "checksum");
185 	nv_add_uint8_array(nv, hash, sizeof(hash), "hash");
186 
187 	return (0);
188 }
189 
190 static int
191 checksum_recv(struct hast_resource *res, struct nv *nv, void **datap,
192     size_t *sizep, bool *freedatap __unused)
193 {
194 	unsigned char chash[SHA256_DIGEST_LENGTH];
195 	const unsigned char *rhash;
196 	SHA256_CTX ctx;
197 	const char *algo;
198 	size_t size;
199 
200 	res = res;	/* TODO */
201 
202 	algo = nv_get_string(nv, "checksum");
203 	if (algo == NULL)
204 		return (0);	/* No checksum. */
205 	if (strcmp(algo, "sha256") != 0) {
206 		pjdlog_error("Unknown checksum algorithm '%s'.", algo);
207 		return (-1);	/* Unknown checksum algorithm. */
208 	}
209 	rhash = nv_get_uint8_array(nv, &size, "hash");
210 	if (rhash == NULL) {
211 		pjdlog_error("Checksum algorithm is present, but hash is missing.");
212 		return (-1);	/* Hash not found. */
213 	}
214 	if (size != sizeof(chash)) {
215 		pjdlog_error("Invalid hash size (%zu) for %s, should be %zu.",
216 		    size, algo, sizeof(chash));
217 		return (-1);	/* Different hash size. */
218 	}
219 
220 	SHA256_Init(&ctx);
221 	SHA256_Update(&ctx, *datap, *sizep);
222 	SHA256_Final(chash, &ctx);
223 
224 	if (bcmp(rhash, chash, sizeof(chash)) != 0) {
225 		pjdlog_error("Hash mismatch.");
226 		return (-1);	/* Hash mismatch. */
227 	}
228 
229 	return (0);
230 }
231 #endif	/* HAVE_CRYPTO */
232 
233 /*
234  * Send the given nv structure via conn.
235  * We keep headers in nv structure and pass data in separate argument.
236  * There can be no data at all (data is NULL then).
237  */
238 int
239 hast_proto_send(struct hast_resource *res, struct proto_conn *conn,
240     struct nv *nv, const void *data, size_t size)
241 {
242 	struct hast_main_header hdr;
243 	struct ebuf *eb;
244 	bool freedata;
245 	void *dptr, *hptr;
246 	size_t hsize;
247 	int ret;
248 
249 	dptr = (void *)(uintptr_t)data;
250 	freedata = false;
251 	ret = -1;
252 
253 	if (data != NULL) {
254 if (false) {
255 		unsigned int ii;
256 
257 		for (ii = 0; ii < sizeof(pipeline) / sizeof(pipeline[0]);
258 		    ii++) {
259 			ret = pipeline[ii].hps_send(res, nv, &dptr, &size,
260 			    &freedata);
261 			if (ret == -1)
262 				goto end;
263 		}
264 		ret = -1;
265 }
266 		nv_add_uint32(nv, size, "size");
267 		if (nv_error(nv) != 0) {
268 			errno = nv_error(nv);
269 			goto end;
270 		}
271 	}
272 
273 	eb = nv_hton(nv);
274 	if (eb == NULL)
275 		goto end;
276 
277 	hdr.version = HAST_PROTO_VERSION;
278 	hdr.size = htole32((uint32_t)ebuf_size(eb));
279 	if (ebuf_add_head(eb, &hdr, sizeof(hdr)) < 0)
280 		goto end;
281 
282 	hptr = ebuf_data(eb, &hsize);
283 	if (proto_send(conn, hptr, hsize) < 0)
284 		goto end;
285 	if (data != NULL && proto_send(conn, dptr, size) < 0)
286 		goto end;
287 
288 	ret = 0;
289 end:
290 	if (freedata)
291 		free(dptr);
292 	return (ret);
293 }
294 
295 int
296 hast_proto_recv_hdr(struct proto_conn *conn, struct nv **nvp)
297 {
298 	struct hast_main_header hdr;
299 	struct nv *nv;
300 	struct ebuf *eb;
301 	void *hptr;
302 
303 	eb = NULL;
304 	nv = NULL;
305 
306 	if (proto_recv(conn, &hdr, sizeof(hdr)) < 0)
307 		goto fail;
308 
309 	if (hdr.version != HAST_PROTO_VERSION) {
310 		errno = ERPCMISMATCH;
311 		goto fail;
312 	}
313 
314 	hdr.size = le32toh(hdr.size);
315 
316 	eb = ebuf_alloc(hdr.size);
317 	if (eb == NULL)
318 		goto fail;
319 	if (ebuf_add_tail(eb, NULL, hdr.size) < 0)
320 		goto fail;
321 	hptr = ebuf_data(eb, NULL);
322 	assert(hptr != NULL);
323 	if (proto_recv(conn, hptr, hdr.size) < 0)
324 		goto fail;
325 	nv = nv_ntoh(eb);
326 	if (nv == NULL)
327 		goto fail;
328 
329 	*nvp = nv;
330 	return (0);
331 fail:
332 	if (eb != NULL)
333 		ebuf_free(eb);
334 	return (-1);
335 }
336 
337 int
338 hast_proto_recv_data(struct hast_resource *res, struct proto_conn *conn,
339     struct nv *nv, void *data, size_t size)
340 {
341 	unsigned int ii;
342 	bool freedata;
343 	size_t dsize;
344 	void *dptr;
345 	int ret;
346 
347 	assert(data != NULL);
348 	assert(size > 0);
349 
350 	ret = -1;
351 	freedata = false;
352 	dptr = data;
353 
354 	dsize = nv_get_uint32(nv, "size");
355 	if (dsize == 0)
356 		(void)nv_set_error(nv, 0);
357 	else {
358 		if (proto_recv(conn, data, dsize) < 0)
359 			goto end;
360 if (false) {
361 		for (ii = sizeof(pipeline) / sizeof(pipeline[0]); ii > 0;
362 		    ii--) {
363 			assert(!"to be verified");
364 			ret = pipeline[ii - 1].hps_recv(res, nv, &dptr,
365 			    &dsize, &freedata);
366 			if (ret == -1)
367 				goto end;
368 		}
369 		ret = -1;
370 		if (dsize < size)
371 			goto end;
372 		/* TODO: 'size' doesn't seem right here. It is maximum data size. */
373 		if (dptr != data)
374 			bcopy(dptr, data, dsize);
375 }
376 	}
377 
378 	ret = 0;
379 end:
380 if (ret < 0) printf("%s:%u %s\n", __func__, __LINE__, strerror(errno));
381 	if (freedata)
382 		free(dptr);
383 	return (ret);
384 }
385 
386 int
387 hast_proto_recv(struct hast_resource *res, struct proto_conn *conn,
388     struct nv **nvp, void *data, size_t size)
389 {
390 	struct nv *nv;
391 	size_t dsize;
392 	int ret;
393 
394 	ret = hast_proto_recv_hdr(conn, &nv);
395 	if (ret < 0)
396 		return (ret);
397 	dsize = nv_get_uint32(nv, "size");
398 	if (dsize == 0)
399 		(void)nv_set_error(nv, 0);
400 	else
401 		ret = hast_proto_recv_data(res, conn, nv, data, size);
402 	if (ret < 0)
403 		nv_free(nv);
404 	else
405 		*nvp = nv;
406 	return (ret);
407 }
408