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(const struct hast_resource *, struct nv *nv, void **, 60 size_t *, bool *); 61 typedef int hps_recv_t(const struct hast_resource *, struct nv *nv, void **, 62 size_t *, bool *); 63 64 struct hast_pipe_stage { 65 const char *hps_name; 66 hps_send_t *hps_send; 67 hps_recv_t *hps_recv; 68 }; 69 70 static int compression_send(const struct hast_resource *res, struct nv *nv, 71 void **datap, size_t *sizep, bool *freedatap); 72 static int compression_recv(const struct hast_resource *res, struct nv *nv, 73 void **datap, size_t *sizep, bool *freedatap); 74 #ifdef HAVE_CRYPTO 75 static int checksum_send(const struct hast_resource *res, struct nv *nv, 76 void **datap, size_t *sizep, bool *freedatap); 77 static int checksum_recv(const struct hast_resource *res, struct nv *nv, 78 void **datap, size_t *sizep, bool *freedatap); 79 #endif 80 81 static struct hast_pipe_stage pipeline[] = { 82 { "compression", compression_send, compression_recv }, 83 #ifdef HAVE_CRYPTO 84 { "checksum", checksum_send, checksum_recv } 85 #endif 86 }; 87 88 static int 89 compression_send(const struct hast_resource *res, struct nv *nv, void **datap, 90 size_t *sizep, bool *freedatap) 91 { 92 unsigned char *newbuf; 93 94 res = res; /* TODO */ 95 96 /* 97 * TODO: For now we emulate compression. 98 * At 80% probability we succeed to compress data, which means we 99 * allocate new buffer, copy the data over set *freedatap to true. 100 */ 101 102 if (arc4random_uniform(100) < 80) { 103 uint32_t *origsize; 104 105 /* 106 * Compression succeeded (but we will grow by 4 bytes, not 107 * shrink for now). 108 */ 109 newbuf = malloc(sizeof(uint32_t) + *sizep); 110 if (newbuf == NULL) 111 return (-1); 112 origsize = (void *)newbuf; 113 *origsize = htole32((uint32_t)*sizep); 114 nv_add_string(nv, "null", "compression"); 115 if (nv_error(nv) != 0) { 116 free(newbuf); 117 errno = nv_error(nv); 118 return (-1); 119 } 120 bcopy(*datap, newbuf + sizeof(uint32_t), *sizep); 121 if (*freedatap) 122 free(*datap); 123 *freedatap = true; 124 *datap = newbuf; 125 *sizep = sizeof(uint32_t) + *sizep; 126 } else { 127 /* 128 * Compression failed, so we leave everything as it was. 129 * It is not critical for compression to succeed. 130 */ 131 } 132 133 return (0); 134 } 135 136 static int 137 compression_recv(const struct hast_resource *res, struct nv *nv, void **datap, 138 size_t *sizep, bool *freedatap) 139 { 140 unsigned char *newbuf; 141 const char *algo; 142 size_t origsize; 143 144 res = res; /* TODO */ 145 146 /* 147 * TODO: For now we emulate compression. 148 */ 149 150 algo = nv_get_string(nv, "compression"); 151 if (algo == NULL) 152 return (0); /* No compression. */ 153 if (strcmp(algo, "null") != 0) { 154 pjdlog_error("Unknown compression algorithm '%s'.", algo); 155 return (-1); /* Unknown compression algorithm. */ 156 } 157 158 origsize = le32toh(*(uint32_t *)*datap); 159 newbuf = malloc(origsize); 160 if (newbuf == NULL) 161 return (-1); 162 bcopy((unsigned char *)*datap + sizeof(uint32_t), newbuf, origsize); 163 if (*freedatap) 164 free(*datap); 165 *freedatap = true; 166 *datap = newbuf; 167 *sizep = origsize; 168 169 return (0); 170 } 171 172 #ifdef HAVE_CRYPTO 173 static int 174 checksum_send(const struct hast_resource *res, struct nv *nv, void **datap, 175 size_t *sizep, bool *freedatap __unused) 176 { 177 unsigned char hash[SHA256_DIGEST_LENGTH]; 178 SHA256_CTX ctx; 179 180 res = res; /* TODO */ 181 182 SHA256_Init(&ctx); 183 SHA256_Update(&ctx, *datap, *sizep); 184 SHA256_Final(hash, &ctx); 185 186 nv_add_string(nv, "sha256", "checksum"); 187 nv_add_uint8_array(nv, hash, sizeof(hash), "hash"); 188 189 return (0); 190 } 191 192 static int 193 checksum_recv(const struct hast_resource *res, struct nv *nv, void **datap, 194 size_t *sizep, bool *freedatap __unused) 195 { 196 unsigned char chash[SHA256_DIGEST_LENGTH]; 197 const unsigned char *rhash; 198 SHA256_CTX ctx; 199 const char *algo; 200 size_t size; 201 202 res = res; /* TODO */ 203 204 algo = nv_get_string(nv, "checksum"); 205 if (algo == NULL) 206 return (0); /* No checksum. */ 207 if (strcmp(algo, "sha256") != 0) { 208 pjdlog_error("Unknown checksum algorithm '%s'.", algo); 209 return (-1); /* Unknown checksum algorithm. */ 210 } 211 rhash = nv_get_uint8_array(nv, &size, "hash"); 212 if (rhash == NULL) { 213 pjdlog_error("Checksum algorithm is present, but hash is missing."); 214 return (-1); /* Hash not found. */ 215 } 216 if (size != sizeof(chash)) { 217 pjdlog_error("Invalid hash size (%zu) for %s, should be %zu.", 218 size, algo, sizeof(chash)); 219 return (-1); /* Different hash size. */ 220 } 221 222 SHA256_Init(&ctx); 223 SHA256_Update(&ctx, *datap, *sizep); 224 SHA256_Final(chash, &ctx); 225 226 if (bcmp(rhash, chash, sizeof(chash)) != 0) { 227 pjdlog_error("Hash mismatch."); 228 return (-1); /* Hash mismatch. */ 229 } 230 231 return (0); 232 } 233 #endif /* HAVE_CRYPTO */ 234 235 /* 236 * Send the given nv structure via conn. 237 * We keep headers in nv structure and pass data in separate argument. 238 * There can be no data at all (data is NULL then). 239 */ 240 int 241 hast_proto_send(const struct hast_resource *res, struct proto_conn *conn, 242 struct nv *nv, const void *data, size_t size) 243 { 244 struct hast_main_header hdr; 245 struct ebuf *eb; 246 bool freedata; 247 void *dptr, *hptr; 248 size_t hsize; 249 int ret; 250 251 dptr = (void *)(uintptr_t)data; 252 freedata = false; 253 ret = -1; 254 255 if (data != NULL) { 256 if (false) { 257 unsigned int ii; 258 259 for (ii = 0; ii < sizeof(pipeline) / sizeof(pipeline[0]); 260 ii++) { 261 ret = pipeline[ii].hps_send(res, nv, &dptr, &size, 262 &freedata); 263 if (ret == -1) 264 goto end; 265 } 266 ret = -1; 267 } 268 nv_add_uint32(nv, size, "size"); 269 if (nv_error(nv) != 0) { 270 errno = nv_error(nv); 271 goto end; 272 } 273 } 274 275 eb = nv_hton(nv); 276 if (eb == NULL) 277 goto end; 278 279 hdr.version = HAST_PROTO_VERSION; 280 hdr.size = htole32((uint32_t)ebuf_size(eb)); 281 if (ebuf_add_head(eb, &hdr, sizeof(hdr)) < 0) 282 goto end; 283 284 hptr = ebuf_data(eb, &hsize); 285 if (proto_send(conn, hptr, hsize) < 0) 286 goto end; 287 if (data != NULL && proto_send(conn, dptr, size) < 0) 288 goto end; 289 290 ret = 0; 291 end: 292 if (freedata) 293 free(dptr); 294 return (ret); 295 } 296 297 int 298 hast_proto_recv_hdr(const struct proto_conn *conn, struct nv **nvp) 299 { 300 struct hast_main_header hdr; 301 struct nv *nv; 302 struct ebuf *eb; 303 void *hptr; 304 305 eb = NULL; 306 nv = NULL; 307 308 if (proto_recv(conn, &hdr, sizeof(hdr)) < 0) 309 goto fail; 310 311 if (hdr.version != HAST_PROTO_VERSION) { 312 errno = ERPCMISMATCH; 313 goto fail; 314 } 315 316 hdr.size = le32toh(hdr.size); 317 318 eb = ebuf_alloc(hdr.size); 319 if (eb == NULL) 320 goto fail; 321 if (ebuf_add_tail(eb, NULL, hdr.size) < 0) 322 goto fail; 323 hptr = ebuf_data(eb, NULL); 324 assert(hptr != NULL); 325 if (proto_recv(conn, hptr, hdr.size) < 0) 326 goto fail; 327 nv = nv_ntoh(eb); 328 if (nv == NULL) 329 goto fail; 330 331 *nvp = nv; 332 return (0); 333 fail: 334 if (eb != NULL) 335 ebuf_free(eb); 336 return (-1); 337 } 338 339 int 340 hast_proto_recv_data(const struct hast_resource *res, struct proto_conn *conn, 341 struct nv *nv, void *data, size_t size) 342 { 343 unsigned int ii; 344 bool freedata; 345 size_t dsize; 346 void *dptr; 347 int ret; 348 349 assert(data != NULL); 350 assert(size > 0); 351 352 ret = -1; 353 freedata = false; 354 dptr = data; 355 356 dsize = nv_get_uint32(nv, "size"); 357 if (dsize == 0) 358 (void)nv_set_error(nv, 0); 359 else { 360 if (proto_recv(conn, data, dsize) < 0) 361 goto end; 362 if (false) { 363 for (ii = sizeof(pipeline) / sizeof(pipeline[0]); ii > 0; 364 ii--) { 365 assert(!"to be verified"); 366 ret = pipeline[ii - 1].hps_recv(res, nv, &dptr, 367 &dsize, &freedata); 368 if (ret == -1) 369 goto end; 370 } 371 ret = -1; 372 if (dsize < size) 373 goto end; 374 /* TODO: 'size' doesn't seem right here. It is maximum data size. */ 375 if (dptr != data) 376 bcopy(dptr, data, dsize); 377 } 378 } 379 380 ret = 0; 381 end: 382 if (ret < 0) printf("%s:%u %s\n", __func__, __LINE__, strerror(errno)); 383 if (freedata) 384 free(dptr); 385 return (ret); 386 } 387 388 int 389 hast_proto_recv(const struct hast_resource *res, struct proto_conn *conn, 390 struct nv **nvp, void *data, size_t size) 391 { 392 struct nv *nv; 393 size_t dsize; 394 int ret; 395 396 ret = hast_proto_recv_hdr(conn, &nv); 397 if (ret < 0) 398 return (ret); 399 dsize = nv_get_uint32(nv, "size"); 400 if (dsize == 0) 401 (void)nv_set_error(nv, 0); 402 else 403 ret = hast_proto_recv_data(res, conn, nv, data, size); 404 if (ret < 0) 405 nv_free(nv); 406 else 407 *nvp = nv; 408 return (ret); 409 } 410