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