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