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