]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sbin/hastd/hast_proto.c
MFC r213265:
[FreeBSD/releng/8.2.git] / sbin / hastd / hast_proto.c
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 }