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