hast_proto.c revision 212033
1204076Spjd/*-
2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation
3204076Spjd * All rights reserved.
4204076Spjd *
5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from
6204076Spjd * the FreeBSD Foundation.
7204076Spjd *
8204076Spjd * Redistribution and use in source and binary forms, with or without
9204076Spjd * modification, are permitted provided that the following conditions
10204076Spjd * are met:
11204076Spjd * 1. Redistributions of source code must retain the above copyright
12204076Spjd *    notice, this list of conditions and the following disclaimer.
13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
14204076Spjd *    notice, this list of conditions and the following disclaimer in the
15204076Spjd *    documentation and/or other materials provided with the distribution.
16204076Spjd *
17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27204076Spjd * SUCH DAMAGE.
28204076Spjd */
29204076Spjd
30204076Spjd#include <sys/cdefs.h>
31204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/hast_proto.c 212033 2010-08-30 22:26:42Z pjd $");
32204076Spjd
33204076Spjd#include <sys/endian.h>
34204076Spjd
35204076Spjd#include <assert.h>
36204076Spjd#include <errno.h>
37204076Spjd#include <string.h>
38204076Spjd#include <strings.h>
39204076Spjd
40207070Spjd#ifdef HAVE_CRYPTO
41204076Spjd#include <openssl/sha.h>
42207070Spjd#endif
43204076Spjd
44204076Spjd#include <hast.h>
45204076Spjd#include <ebuf.h>
46204076Spjd#include <nv.h>
47204076Spjd#include <pjdlog.h>
48204076Spjd#include <proto.h>
49204076Spjd
50204076Spjd#include "hast_proto.h"
51204076Spjd
52204076Spjdstruct hast_main_header {
53204076Spjd	/* Protocol version. */
54204076Spjd	uint8_t		version;
55204076Spjd	/* Size of nv headers. */
56204076Spjd	uint32_t	size;
57204076Spjd} __packed;
58204076Spjd
59212033Spjdtypedef int hps_send_t(const struct hast_resource *, struct nv *nv, void **,
60212033Spjd    size_t *, bool *);
61212033Spjdtypedef int hps_recv_t(const struct hast_resource *, struct nv *nv, void **,
62212033Spjd    size_t *, bool *);
63204076Spjd
64204076Spjdstruct hast_pipe_stage {
65204076Spjd	const char	*hps_name;
66204076Spjd	hps_send_t	*hps_send;
67204076Spjd	hps_recv_t	*hps_recv;
68204076Spjd};
69204076Spjd
70212033Spjdstatic int compression_send(const struct hast_resource *res, struct nv *nv,
71204076Spjd    void **datap, size_t *sizep, bool *freedatap);
72212033Spjdstatic int compression_recv(const struct hast_resource *res, struct nv *nv,
73204076Spjd    void **datap, size_t *sizep, bool *freedatap);
74207070Spjd#ifdef HAVE_CRYPTO
75212033Spjdstatic int checksum_send(const struct hast_resource *res, struct nv *nv,
76204076Spjd    void **datap, size_t *sizep, bool *freedatap);
77212033Spjdstatic int checksum_recv(const struct hast_resource *res, struct nv *nv,
78204076Spjd    void **datap, size_t *sizep, bool *freedatap);
79207070Spjd#endif
80204076Spjd
81204076Spjdstatic struct hast_pipe_stage pipeline[] = {
82204076Spjd	{ "compression", compression_send, compression_recv },
83207070Spjd#ifdef HAVE_CRYPTO
84204076Spjd	{ "checksum", checksum_send, checksum_recv }
85207070Spjd#endif
86204076Spjd};
87204076Spjd
88204076Spjdstatic int
89212033Spjdcompression_send(const struct hast_resource *res, struct nv *nv, void **datap,
90204076Spjd    size_t *sizep, bool *freedatap)
91204076Spjd{
92204076Spjd	unsigned char *newbuf;
93204076Spjd
94204076Spjd	res = res;	/* TODO */
95204076Spjd
96204076Spjd	/*
97204076Spjd	 * TODO: For now we emulate compression.
98204076Spjd	 * At 80% probability we succeed to compress data, which means we
99204076Spjd	 * allocate new buffer, copy the data over set *freedatap to true.
100204076Spjd	 */
101204076Spjd
102204076Spjd	if (arc4random_uniform(100) < 80) {
103204076Spjd		uint32_t *origsize;
104204076Spjd
105204076Spjd		/*
106204076Spjd		 * Compression succeeded (but we will grow by 4 bytes, not
107204076Spjd		 * shrink for now).
108204076Spjd		 */
109204076Spjd		newbuf = malloc(sizeof(uint32_t) + *sizep);
110204076Spjd		if (newbuf == NULL)
111204076Spjd			return (-1);
112204076Spjd		origsize = (void *)newbuf;
113204076Spjd		*origsize = htole32((uint32_t)*sizep);
114204076Spjd		nv_add_string(nv, "null", "compression");
115204076Spjd		if (nv_error(nv) != 0) {
116204076Spjd			free(newbuf);
117204076Spjd			errno = nv_error(nv);
118204076Spjd			return (-1);
119204076Spjd		}
120204076Spjd		bcopy(*datap, newbuf + sizeof(uint32_t), *sizep);
121204076Spjd		if (*freedatap)
122204076Spjd			free(*datap);
123204076Spjd		*freedatap = true;
124204076Spjd		*datap = newbuf;
125204076Spjd		*sizep = sizeof(uint32_t) + *sizep;
126204076Spjd	} else {
127204076Spjd		/*
128204076Spjd		 * Compression failed, so we leave everything as it was.
129204076Spjd		 * It is not critical for compression to succeed.
130204076Spjd		 */
131204076Spjd	}
132204076Spjd
133204076Spjd	return (0);
134204076Spjd}
135204076Spjd
136204076Spjdstatic int
137212033Spjdcompression_recv(const struct hast_resource *res, struct nv *nv, void **datap,
138204076Spjd    size_t *sizep, bool *freedatap)
139204076Spjd{
140204076Spjd	unsigned char *newbuf;
141204076Spjd	const char *algo;
142204076Spjd	size_t origsize;
143204076Spjd
144204076Spjd	res = res;	/* TODO */
145204076Spjd
146204076Spjd	/*
147204076Spjd	 * TODO: For now we emulate compression.
148204076Spjd	 */
149204076Spjd
150204076Spjd	algo = nv_get_string(nv, "compression");
151204076Spjd	if (algo == NULL)
152204076Spjd		return (0);	/* No compression. */
153204076Spjd	if (strcmp(algo, "null") != 0) {
154204076Spjd		pjdlog_error("Unknown compression algorithm '%s'.", algo);
155204076Spjd		return (-1);	/* Unknown compression algorithm. */
156204076Spjd	}
157204076Spjd
158204076Spjd	origsize = le32toh(*(uint32_t *)*datap);
159204076Spjd	newbuf = malloc(origsize);
160204076Spjd	if (newbuf == NULL)
161204076Spjd		return (-1);
162204076Spjd	bcopy((unsigned char *)*datap + sizeof(uint32_t), newbuf, origsize);
163204076Spjd	if (*freedatap)
164204076Spjd		free(*datap);
165204076Spjd	*freedatap = true;
166204076Spjd	*datap = newbuf;
167204076Spjd	*sizep = origsize;
168204076Spjd
169204076Spjd	return (0);
170204076Spjd}
171204076Spjd
172207070Spjd#ifdef HAVE_CRYPTO
173204076Spjdstatic int
174212033Spjdchecksum_send(const struct hast_resource *res, struct nv *nv, void **datap,
175204076Spjd    size_t *sizep, bool *freedatap __unused)
176204076Spjd{
177204076Spjd	unsigned char hash[SHA256_DIGEST_LENGTH];
178204076Spjd	SHA256_CTX ctx;
179204076Spjd
180204076Spjd	res = res;	/* TODO */
181204076Spjd
182204076Spjd	SHA256_Init(&ctx);
183204076Spjd	SHA256_Update(&ctx, *datap, *sizep);
184204076Spjd	SHA256_Final(hash, &ctx);
185204076Spjd
186204076Spjd	nv_add_string(nv, "sha256", "checksum");
187204076Spjd	nv_add_uint8_array(nv, hash, sizeof(hash), "hash");
188204076Spjd
189204076Spjd	return (0);
190204076Spjd}
191204076Spjd
192204076Spjdstatic int
193212033Spjdchecksum_recv(const struct hast_resource *res, struct nv *nv, void **datap,
194204076Spjd    size_t *sizep, bool *freedatap __unused)
195204076Spjd{
196204076Spjd	unsigned char chash[SHA256_DIGEST_LENGTH];
197204076Spjd	const unsigned char *rhash;
198204076Spjd	SHA256_CTX ctx;
199204076Spjd	const char *algo;
200204076Spjd	size_t size;
201204076Spjd
202204076Spjd	res = res;	/* TODO */
203204076Spjd
204204076Spjd	algo = nv_get_string(nv, "checksum");
205204076Spjd	if (algo == NULL)
206204076Spjd		return (0);	/* No checksum. */
207204076Spjd	if (strcmp(algo, "sha256") != 0) {
208204076Spjd		pjdlog_error("Unknown checksum algorithm '%s'.", algo);
209204076Spjd		return (-1);	/* Unknown checksum algorithm. */
210204076Spjd	}
211204076Spjd	rhash = nv_get_uint8_array(nv, &size, "hash");
212204076Spjd	if (rhash == NULL) {
213204076Spjd		pjdlog_error("Checksum algorithm is present, but hash is missing.");
214204076Spjd		return (-1);	/* Hash not found. */
215204076Spjd	}
216204076Spjd	if (size != sizeof(chash)) {
217204076Spjd		pjdlog_error("Invalid hash size (%zu) for %s, should be %zu.",
218204076Spjd		    size, algo, sizeof(chash));
219204076Spjd		return (-1);	/* Different hash size. */
220204076Spjd	}
221204076Spjd
222204076Spjd	SHA256_Init(&ctx);
223204076Spjd	SHA256_Update(&ctx, *datap, *sizep);
224204076Spjd	SHA256_Final(chash, &ctx);
225204076Spjd
226204076Spjd	if (bcmp(rhash, chash, sizeof(chash)) != 0) {
227204076Spjd		pjdlog_error("Hash mismatch.");
228204076Spjd		return (-1);	/* Hash mismatch. */
229204076Spjd	}
230204076Spjd
231204076Spjd	return (0);
232204076Spjd}
233207070Spjd#endif	/* HAVE_CRYPTO */
234204076Spjd
235204076Spjd/*
236204076Spjd * Send the given nv structure via conn.
237204076Spjd * We keep headers in nv structure and pass data in separate argument.
238204076Spjd * There can be no data at all (data is NULL then).
239204076Spjd */
240204076Spjdint
241212033Spjdhast_proto_send(const struct hast_resource *res, struct proto_conn *conn,
242204076Spjd    struct nv *nv, const void *data, size_t size)
243204076Spjd{
244204076Spjd	struct hast_main_header hdr;
245204076Spjd	struct ebuf *eb;
246204076Spjd	bool freedata;
247204076Spjd	void *dptr, *hptr;
248204076Spjd	size_t hsize;
249204076Spjd	int ret;
250204076Spjd
251204076Spjd	dptr = (void *)(uintptr_t)data;
252204076Spjd	freedata = false;
253204076Spjd	ret = -1;
254204076Spjd
255204076Spjd	if (data != NULL) {
256204076Spjdif (false) {
257204076Spjd		unsigned int ii;
258204076Spjd
259204076Spjd		for (ii = 0; ii < sizeof(pipeline) / sizeof(pipeline[0]);
260204076Spjd		    ii++) {
261204076Spjd			ret = pipeline[ii].hps_send(res, nv, &dptr, &size,
262204076Spjd			    &freedata);
263204076Spjd			if (ret == -1)
264204076Spjd				goto end;
265204076Spjd		}
266204076Spjd		ret = -1;
267204076Spjd}
268204076Spjd		nv_add_uint32(nv, size, "size");
269204076Spjd		if (nv_error(nv) != 0) {
270204076Spjd			errno = nv_error(nv);
271204076Spjd			goto end;
272204076Spjd		}
273204076Spjd	}
274204076Spjd
275204076Spjd	eb = nv_hton(nv);
276204076Spjd	if (eb == NULL)
277204076Spjd		goto end;
278204076Spjd
279204076Spjd	hdr.version = HAST_PROTO_VERSION;
280204076Spjd	hdr.size = htole32((uint32_t)ebuf_size(eb));
281204076Spjd	if (ebuf_add_head(eb, &hdr, sizeof(hdr)) < 0)
282204076Spjd		goto end;
283204076Spjd
284204076Spjd	hptr = ebuf_data(eb, &hsize);
285204076Spjd	if (proto_send(conn, hptr, hsize) < 0)
286204076Spjd		goto end;
287204076Spjd	if (data != NULL && proto_send(conn, dptr, size) < 0)
288204076Spjd		goto end;
289204076Spjd
290204076Spjd	ret = 0;
291204076Spjdend:
292204076Spjd	if (freedata)
293204076Spjd		free(dptr);
294204076Spjd	return (ret);
295204076Spjd}
296204076Spjd
297204076Spjdint
298212033Spjdhast_proto_recv_hdr(const struct proto_conn *conn, struct nv **nvp)
299204076Spjd{
300204076Spjd	struct hast_main_header hdr;
301204076Spjd	struct nv *nv;
302204076Spjd	struct ebuf *eb;
303204076Spjd	void *hptr;
304204076Spjd
305204076Spjd	eb = NULL;
306204076Spjd	nv = NULL;
307204076Spjd
308204076Spjd	if (proto_recv(conn, &hdr, sizeof(hdr)) < 0)
309204076Spjd		goto fail;
310204076Spjd
311204076Spjd	if (hdr.version != HAST_PROTO_VERSION) {
312204076Spjd		errno = ERPCMISMATCH;
313204076Spjd		goto fail;
314204076Spjd	}
315204076Spjd
316204076Spjd	hdr.size = le32toh(hdr.size);
317204076Spjd
318204076Spjd	eb = ebuf_alloc(hdr.size);
319204076Spjd	if (eb == NULL)
320204076Spjd		goto fail;
321204076Spjd	if (ebuf_add_tail(eb, NULL, hdr.size) < 0)
322204076Spjd		goto fail;
323204076Spjd	hptr = ebuf_data(eb, NULL);
324204076Spjd	assert(hptr != NULL);
325204076Spjd	if (proto_recv(conn, hptr, hdr.size) < 0)
326204076Spjd		goto fail;
327204076Spjd	nv = nv_ntoh(eb);
328204076Spjd	if (nv == NULL)
329204076Spjd		goto fail;
330204076Spjd
331204076Spjd	*nvp = nv;
332204076Spjd	return (0);
333204076Spjdfail:
334209175Spjd	if (eb != NULL)
335204076Spjd		ebuf_free(eb);
336204076Spjd	return (-1);
337204076Spjd}
338204076Spjd
339204076Spjdint
340212033Spjdhast_proto_recv_data(const struct hast_resource *res, struct proto_conn *conn,
341204076Spjd    struct nv *nv, void *data, size_t size)
342204076Spjd{
343204076Spjd	unsigned int ii;
344204076Spjd	bool freedata;
345204076Spjd	size_t dsize;
346204076Spjd	void *dptr;
347204076Spjd	int ret;
348204076Spjd
349204076Spjd	assert(data != NULL);
350204076Spjd	assert(size > 0);
351204076Spjd
352204076Spjd	ret = -1;
353204076Spjd	freedata = false;
354204076Spjd	dptr = data;
355204076Spjd
356204076Spjd	dsize = nv_get_uint32(nv, "size");
357204076Spjd	if (dsize == 0)
358204076Spjd		(void)nv_set_error(nv, 0);
359204076Spjd	else {
360204076Spjd		if (proto_recv(conn, data, dsize) < 0)
361204076Spjd			goto end;
362204076Spjdif (false) {
363204076Spjd		for (ii = sizeof(pipeline) / sizeof(pipeline[0]); ii > 0;
364204076Spjd		    ii--) {
365204076Spjd			assert(!"to be verified");
366204076Spjd			ret = pipeline[ii - 1].hps_recv(res, nv, &dptr,
367204076Spjd			    &dsize, &freedata);
368204076Spjd			if (ret == -1)
369204076Spjd				goto end;
370204076Spjd		}
371204076Spjd		ret = -1;
372204076Spjd		if (dsize < size)
373204076Spjd			goto end;
374204076Spjd		/* TODO: 'size' doesn't seem right here. It is maximum data size. */
375204076Spjd		if (dptr != data)
376204076Spjd			bcopy(dptr, data, dsize);
377204076Spjd}
378204076Spjd	}
379204076Spjd
380204076Spjd	ret = 0;
381204076Spjdend:
382204076Spjdif (ret < 0) printf("%s:%u %s\n", __func__, __LINE__, strerror(errno));
383204076Spjd	if (freedata)
384204076Spjd		free(dptr);
385204076Spjd	return (ret);
386204076Spjd}
387204076Spjd
388204076Spjdint
389212033Spjdhast_proto_recv(const struct hast_resource *res, struct proto_conn *conn,
390204076Spjd    struct nv **nvp, void *data, size_t size)
391204076Spjd{
392204076Spjd	struct nv *nv;
393204076Spjd	size_t dsize;
394204076Spjd	int ret;
395204076Spjd
396204076Spjd	ret = hast_proto_recv_hdr(conn, &nv);
397204076Spjd	if (ret < 0)
398204076Spjd		return (ret);
399204076Spjd	dsize = nv_get_uint32(nv, "size");
400204076Spjd	if (dsize == 0)
401204076Spjd		(void)nv_set_error(nv, 0);
402204076Spjd	else
403204076Spjd		ret = hast_proto_recv_data(res, conn, nv, data, size);
404204076Spjd	if (ret < 0)
405204076Spjd		nv_free(nv);
406204076Spjd	else
407204076Spjd		*nvp = nv;
408204076Spjd	return (ret);
409204076Spjd}
410