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