hast_proto.c revision 209175
159243Sobrien/*-
259243Sobrien * Copyright (c) 2009-2010 The FreeBSD Foundation
359243Sobrien * All rights reserved.
459243Sobrien *
559243Sobrien * This software was developed by Pawel Jakub Dawidek under sponsorship from
659243Sobrien * the FreeBSD Foundation.
759243Sobrien *
859243Sobrien * Redistribution and use in source and binary forms, with or without
959243Sobrien * modification, are permitted provided that the following conditions
1059243Sobrien * are met:
1159243Sobrien * 1. Redistributions of source code must retain the above copyright
1259243Sobrien *    notice, this list of conditions and the following disclaimer.
1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1459243Sobrien *    notice, this list of conditions and the following disclaimer in the
1559243Sobrien *    documentation and/or other materials provided with the distribution.
1659243Sobrien *
1759243Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
1859243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1959243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2059243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2159243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2259243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2359243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2459243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2559243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2659243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2759243Sobrien * SUCH DAMAGE.
2859243Sobrien */
2959243Sobrien
3059243Sobrien#include <sys/cdefs.h>
3159243Sobrien__FBSDID("$FreeBSD: head/sbin/hastd/hast_proto.c 209175 2010-06-14 21:01:13Z pjd $");
3259243Sobrien
3359243Sobrien#include <sys/endian.h>
3459243Sobrien
3559243Sobrien#include <assert.h>
3659243Sobrien#include <errno.h>
3759243Sobrien#include <string.h>
3859243Sobrien#include <strings.h>
3959243Sobrien
4059243Sobrien#ifdef HAVE_CRYPTO
4159243Sobrien#include <openssl/sha.h>
4259243Sobrien#endif
4359243Sobrien
4459243Sobrien#include <hast.h>
4559243Sobrien#include <ebuf.h>
4659243Sobrien#include <nv.h>
4759243Sobrien#include <pjdlog.h>
4859243Sobrien#include <proto.h>
4959243Sobrien
5059243Sobrien#include "hast_proto.h"
5159243Sobrien
5259243Sobrienstruct hast_main_header {
5359243Sobrien	/* Protocol version. */
5459243Sobrien	uint8_t		version;
5559243Sobrien	/* Size of nv headers. */
5659243Sobrien	uint32_t	size;
5759243Sobrien} __packed;
5859243Sobrien
5959243Sobrientypedef int hps_send_t(struct hast_resource *, struct nv *nv, void **, size_t *, bool *);
6059243Sobrientypedef int hps_recv_t(struct hast_resource *, struct nv *nv, void **, size_t *, bool *);
6159243Sobrien
6259243Sobrienstruct hast_pipe_stage {
6359243Sobrien	const char	*hps_name;
6459243Sobrien	hps_send_t	*hps_send;
6559243Sobrien	hps_recv_t	*hps_recv;
6659243Sobrien};
6759243Sobrien
6859243Sobrienstatic int compression_send(struct hast_resource *res, struct nv *nv,
6959243Sobrien    void **datap, size_t *sizep, bool *freedatap);
7059243Sobrienstatic int compression_recv(struct hast_resource *res, struct nv *nv,
7159243Sobrien    void **datap, size_t *sizep, bool *freedatap);
7259243Sobrien#ifdef HAVE_CRYPTO
7359243Sobrienstatic int checksum_send(struct hast_resource *res, struct nv *nv,
7459243Sobrien    void **datap, size_t *sizep, bool *freedatap);
7559243Sobrienstatic int checksum_recv(struct hast_resource *res, struct nv *nv,
7659243Sobrien    void **datap, size_t *sizep, bool *freedatap);
7759243Sobrien#endif
7859243Sobrien
7959243Sobrienstatic struct hast_pipe_stage pipeline[] = {
8059243Sobrien	{ "compression", compression_send, compression_recv },
8159243Sobrien#ifdef HAVE_CRYPTO
8259243Sobrien	{ "checksum", checksum_send, checksum_recv }
8359243Sobrien#endif
8459243Sobrien};
85131962Smp
8659243Sobrienstatic int
8759243Sobriencompression_send(struct hast_resource *res, struct nv *nv, void **datap,
8859243Sobrien    size_t *sizep, bool *freedatap)
8959243Sobrien{
9059243Sobrien	unsigned char *newbuf;
9159243Sobrien
9259243Sobrien	res = res;	/* TODO */
9359243Sobrien
9459243Sobrien	/*
9559243Sobrien	 * TODO: For now we emulate compression.
9659243Sobrien	 * At 80% probability we succeed to compress data, which means we
9759243Sobrien	 * allocate new buffer, copy the data over set *freedatap to true.
9859243Sobrien	 */
9959243Sobrien
10059243Sobrien	if (arc4random_uniform(100) < 80) {
10159243Sobrien		uint32_t *origsize;
10259243Sobrien
10359243Sobrien		/*
10459243Sobrien		 * Compression succeeded (but we will grow by 4 bytes, not
10559243Sobrien		 * shrink for now).
10659243Sobrien		 */
10759243Sobrien		newbuf = malloc(sizeof(uint32_t) + *sizep);
10859243Sobrien		if (newbuf == NULL)
10959243Sobrien			return (-1);
11059243Sobrien		origsize = (void *)newbuf;
11159243Sobrien		*origsize = htole32((uint32_t)*sizep);
11259243Sobrien		nv_add_string(nv, "null", "compression");
11359243Sobrien		if (nv_error(nv) != 0) {
11459243Sobrien			free(newbuf);
11559243Sobrien			errno = nv_error(nv);
11659243Sobrien			return (-1);
11759243Sobrien		}
11859243Sobrien		bcopy(*datap, newbuf + sizeof(uint32_t), *sizep);
11959243Sobrien		if (*freedatap)
12059243Sobrien			free(*datap);
12159243Sobrien		*freedatap = true;
12259243Sobrien		*datap = newbuf;
12359243Sobrien		*sizep = sizeof(uint32_t) + *sizep;
12459243Sobrien	} else {
12559243Sobrien		/*
12659243Sobrien		 * Compression failed, so we leave everything as it was.
12759243Sobrien		 * It is not critical for compression to succeed.
12859243Sobrien		 */
12959243Sobrien	}
13059243Sobrien
13159243Sobrien	return (0);
13259243Sobrien}
13359243Sobrien
13459243Sobrienstatic int
13559243Sobriencompression_recv(struct hast_resource *res, struct nv *nv, void **datap,
13659243Sobrien    size_t *sizep, bool *freedatap)
13759243Sobrien{
13859243Sobrien	unsigned char *newbuf;
13959243Sobrien	const char *algo;
14059243Sobrien	size_t origsize;
14159243Sobrien
14259243Sobrien	res = res;	/* TODO */
14359243Sobrien
14459243Sobrien	/*
14559243Sobrien	 * TODO: For now we emulate compression.
14659243Sobrien	 */
14759243Sobrien
14859243Sobrien	algo = nv_get_string(nv, "compression");
14959243Sobrien	if (algo == NULL)
15059243Sobrien		return (0);	/* No compression. */
15159243Sobrien	if (strcmp(algo, "null") != 0) {
15259243Sobrien		pjdlog_error("Unknown compression algorithm '%s'.", algo);
15359243Sobrien		return (-1);	/* Unknown compression algorithm. */
15459243Sobrien	}
15559243Sobrien
15659243Sobrien	origsize = le32toh(*(uint32_t *)*datap);
15759243Sobrien	newbuf = malloc(origsize);
15859243Sobrien	if (newbuf == NULL)
15959243Sobrien		return (-1);
16059243Sobrien	bcopy((unsigned char *)*datap + sizeof(uint32_t), newbuf, origsize);
16159243Sobrien	if (*freedatap)
16259243Sobrien		free(*datap);
16359243Sobrien	*freedatap = true;
16459243Sobrien	*datap = newbuf;
16559243Sobrien	*sizep = origsize;
16659243Sobrien
16759243Sobrien	return (0);
16859243Sobrien}
16959243Sobrien
17059243Sobrien#ifdef HAVE_CRYPTO
17159243Sobrienstatic int
17259243Sobrienchecksum_send(struct hast_resource *res, struct nv *nv, void **datap,
17359243Sobrien    size_t *sizep, bool *freedatap __unused)
17459243Sobrien{
17559243Sobrien	unsigned char hash[SHA256_DIGEST_LENGTH];
17659243Sobrien	SHA256_CTX ctx;
17759243Sobrien
17859243Sobrien	res = res;	/* TODO */
17959243Sobrien
18059243Sobrien	SHA256_Init(&ctx);
18159243Sobrien	SHA256_Update(&ctx, *datap, *sizep);
18259243Sobrien	SHA256_Final(hash, &ctx);
18359243Sobrien
18459243Sobrien	nv_add_string(nv, "sha256", "checksum");
18559243Sobrien	nv_add_uint8_array(nv, hash, sizeof(hash), "hash");
18659243Sobrien
18759243Sobrien	return (0);
18859243Sobrien}
18959243Sobrien
19059243Sobrienstatic int
19159243Sobrienchecksum_recv(struct hast_resource *res, struct nv *nv, void **datap,
19259243Sobrien    size_t *sizep, bool *freedatap __unused)
19359243Sobrien{
19459243Sobrien	unsigned char chash[SHA256_DIGEST_LENGTH];
19559243Sobrien	const unsigned char *rhash;
19659243Sobrien	SHA256_CTX ctx;
19759243Sobrien	const char *algo;
19859243Sobrien	size_t size;
19959243Sobrien
20059243Sobrien	res = res;	/* TODO */
20159243Sobrien
20259243Sobrien	algo = nv_get_string(nv, "checksum");
20359243Sobrien	if (algo == NULL)
20459243Sobrien		return (0);	/* No checksum. */
20559243Sobrien	if (strcmp(algo, "sha256") != 0) {
20659243Sobrien		pjdlog_error("Unknown checksum algorithm '%s'.", algo);
20759243Sobrien		return (-1);	/* Unknown checksum algorithm. */
20859243Sobrien	}
20959243Sobrien	rhash = nv_get_uint8_array(nv, &size, "hash");
21059243Sobrien	if (rhash == NULL) {
21159243Sobrien		pjdlog_error("Checksum algorithm is present, but hash is missing.");
21259243Sobrien		return (-1);	/* Hash not found. */
21359243Sobrien	}
21459243Sobrien	if (size != sizeof(chash)) {
21559243Sobrien		pjdlog_error("Invalid hash size (%zu) for %s, should be %zu.",
21659243Sobrien		    size, algo, sizeof(chash));
21759243Sobrien		return (-1);	/* Different hash size. */
21859243Sobrien	}
21959243Sobrien
22059243Sobrien	SHA256_Init(&ctx);
22159243Sobrien	SHA256_Update(&ctx, *datap, *sizep);
22259243Sobrien	SHA256_Final(chash, &ctx);
22359243Sobrien
22459243Sobrien	if (bcmp(rhash, chash, sizeof(chash)) != 0) {
22559243Sobrien		pjdlog_error("Hash mismatch.");
22659243Sobrien		return (-1);	/* Hash mismatch. */
22759243Sobrien	}
22859243Sobrien
22959243Sobrien	return (0);
23059243Sobrien}
23159243Sobrien#endif	/* HAVE_CRYPTO */
23259243Sobrien
23359243Sobrien/*
23459243Sobrien * Send the given nv structure via conn.
23559243Sobrien * We keep headers in nv structure and pass data in separate argument.
23659243Sobrien * There can be no data at all (data is NULL then).
23759243Sobrien */
23859243Sobrienint
23959243Sobrienhast_proto_send(struct hast_resource *res, struct proto_conn *conn,
24059243Sobrien    struct nv *nv, const void *data, size_t size)
24159243Sobrien{
24259243Sobrien	struct hast_main_header hdr;
24359243Sobrien	struct ebuf *eb;
24459243Sobrien	bool freedata;
24559243Sobrien	void *dptr, *hptr;
24659243Sobrien	size_t hsize;
24759243Sobrien	int ret;
24859243Sobrien
24959243Sobrien	dptr = (void *)(uintptr_t)data;
25059243Sobrien	freedata = false;
25159243Sobrien	ret = -1;
25259243Sobrien
25359243Sobrien	if (data != NULL) {
25459243Sobrienif (false) {
25559243Sobrien		unsigned int ii;
25659243Sobrien
25759243Sobrien		for (ii = 0; ii < sizeof(pipeline) / sizeof(pipeline[0]);
25859243Sobrien		    ii++) {
25959243Sobrien			ret = pipeline[ii].hps_send(res, nv, &dptr, &size,
26059243Sobrien			    &freedata);
26159243Sobrien			if (ret == -1)
26259243Sobrien				goto end;
26359243Sobrien		}
26459243Sobrien		ret = -1;
26559243Sobrien}
26659243Sobrien		nv_add_uint32(nv, size, "size");
26759243Sobrien		if (nv_error(nv) != 0) {
26859243Sobrien			errno = nv_error(nv);
26959243Sobrien			goto end;
27059243Sobrien		}
27159243Sobrien	}
27259243Sobrien
27359243Sobrien	eb = nv_hton(nv);
27459243Sobrien	if (eb == NULL)
27559243Sobrien		goto end;
27659243Sobrien
27759243Sobrien	hdr.version = HAST_PROTO_VERSION;
27859243Sobrien	hdr.size = htole32((uint32_t)ebuf_size(eb));
27959243Sobrien	if (ebuf_add_head(eb, &hdr, sizeof(hdr)) < 0)
28059243Sobrien		goto end;
28159243Sobrien
28259243Sobrien	hptr = ebuf_data(eb, &hsize);
28359243Sobrien	if (proto_send(conn, hptr, hsize) < 0)
28459243Sobrien		goto end;
28559243Sobrien	if (data != NULL && proto_send(conn, dptr, size) < 0)
28659243Sobrien		goto end;
28759243Sobrien
28859243Sobrien	ret = 0;
28959243Sobrienend:
29059243Sobrien	if (freedata)
29159243Sobrien		free(dptr);
29259243Sobrien	return (ret);
29359243Sobrien}
29459243Sobrien
29559243Sobrienint
29659243Sobrienhast_proto_recv_hdr(struct proto_conn *conn, struct nv **nvp)
29759243Sobrien{
29859243Sobrien	struct hast_main_header hdr;
29959243Sobrien	struct nv *nv;
30059243Sobrien	struct ebuf *eb;
30159243Sobrien	void *hptr;
30259243Sobrien
30359243Sobrien	eb = NULL;
30459243Sobrien	nv = NULL;
30559243Sobrien
30659243Sobrien	if (proto_recv(conn, &hdr, sizeof(hdr)) < 0)
30759243Sobrien		goto fail;
30859243Sobrien
30959243Sobrien	if (hdr.version != HAST_PROTO_VERSION) {
31059243Sobrien		errno = ERPCMISMATCH;
31159243Sobrien		goto fail;
31259243Sobrien	}
31359243Sobrien
31459243Sobrien	hdr.size = le32toh(hdr.size);
31559243Sobrien
31659243Sobrien	eb = ebuf_alloc(hdr.size);
31759243Sobrien	if (eb == NULL)
31859243Sobrien		goto fail;
31959243Sobrien	if (ebuf_add_tail(eb, NULL, hdr.size) < 0)
32059243Sobrien		goto fail;
32159243Sobrien	hptr = ebuf_data(eb, NULL);
32259243Sobrien	assert(hptr != NULL);
32359243Sobrien	if (proto_recv(conn, hptr, hdr.size) < 0)
32459243Sobrien		goto fail;
32559243Sobrien	nv = nv_ntoh(eb);
32659243Sobrien	if (nv == NULL)
32759243Sobrien		goto fail;
32859243Sobrien
32959243Sobrien	*nvp = nv;
33059243Sobrien	return (0);
33159243Sobrienfail:
33259243Sobrien	if (eb != NULL)
33359243Sobrien		ebuf_free(eb);
33459243Sobrien	return (-1);
33559243Sobrien}
33659243Sobrien
33759243Sobrienint
33859243Sobrienhast_proto_recv_data(struct hast_resource *res, struct proto_conn *conn,
33959243Sobrien    struct nv *nv, void *data, size_t size)
34059243Sobrien{
34159243Sobrien	unsigned int ii;
34259243Sobrien	bool freedata;
34359243Sobrien	size_t dsize;
34459243Sobrien	void *dptr;
34559243Sobrien	int ret;
34659243Sobrien
34759243Sobrien	assert(data != NULL);
34859243Sobrien	assert(size > 0);
34959243Sobrien
35059243Sobrien	ret = -1;
35159243Sobrien	freedata = false;
35259243Sobrien	dptr = data;
35359243Sobrien
35459243Sobrien	dsize = nv_get_uint32(nv, "size");
35559243Sobrien	if (dsize == 0)
35659243Sobrien		(void)nv_set_error(nv, 0);
35759243Sobrien	else {
35859243Sobrien		if (proto_recv(conn, data, dsize) < 0)
35959243Sobrien			goto end;
36059243Sobrienif (false) {
36159243Sobrien		for (ii = sizeof(pipeline) / sizeof(pipeline[0]); ii > 0;
36259243Sobrien		    ii--) {
36359243Sobrien			assert(!"to be verified");
36459243Sobrien			ret = pipeline[ii - 1].hps_recv(res, nv, &dptr,
36559243Sobrien			    &dsize, &freedata);
36659243Sobrien			if (ret == -1)
36759243Sobrien				goto end;
36859243Sobrien		}
36959243Sobrien		ret = -1;
37059243Sobrien		if (dsize < size)
37159243Sobrien			goto end;
37259243Sobrien		/* TODO: 'size' doesn't seem right here. It is maximum data size. */
37359243Sobrien		if (dptr != data)
37459243Sobrien			bcopy(dptr, data, dsize);
37559243Sobrien}
37659243Sobrien	}
37759243Sobrien
37859243Sobrien	ret = 0;
37959243Sobrienend:
38059243Sobrienif (ret < 0) printf("%s:%u %s\n", __func__, __LINE__, strerror(errno));
38159243Sobrien	if (freedata)
38259243Sobrien		free(dptr);
38359243Sobrien	return (ret);
38459243Sobrien}
38559243Sobrien
38659243Sobrienint
38759243Sobrienhast_proto_recv(struct hast_resource *res, struct proto_conn *conn,
38859243Sobrien    struct nv **nvp, void *data, size_t size)
38959243Sobrien{
39059243Sobrien	struct nv *nv;
39159243Sobrien	size_t dsize;
39259243Sobrien	int ret;
39359243Sobrien
39459243Sobrien	ret = hast_proto_recv_hdr(conn, &nv);
39559243Sobrien	if (ret < 0)
39659243Sobrien		return (ret);
39759243Sobrien	dsize = nv_get_uint32(nv, "size");
39859243Sobrien	if (dsize == 0)
39959243Sobrien		(void)nv_set_error(nv, 0);
40059243Sobrien	else
40159243Sobrien		ret = hast_proto_recv_data(res, conn, nv, data, size);
40259243Sobrien	if (ret < 0)
40359243Sobrien		nv_free(nv);
40459243Sobrien	else
40559243Sobrien		*nvp = nv;
40659243Sobrien	return (ret);
40759243Sobrien}
40859243Sobrien