1 /*
2  * Copyright 2018 Jonathan Dieter <jdieter@gmail.com>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *  1. Redistributions of source code must retain the above copyright notice,
8  *     this list of conditions and the following disclaimer.
9  *
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <assert.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <stdint.h>
31 #include <stdbool.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <zck.h>
38 
39 #include "zck_private.h"
40 
41 
42 
43 /* If lead format changes, this needs to be changed */
zck_get_min_download_size()44 int PUBLIC zck_get_min_download_size() {
45     /* Magic + hash type + hash digest + header size */
46     return 5 + MAX_COMP_SIZE*2 + get_max_hash_size();
47 }
48 
49 
50 
zck_clear(zckCtx * zck)51 static void zck_clear(zckCtx *zck) {
52     if(zck == NULL)
53         return;
54     index_free(zck);
55     if(zck->header)
56         free(zck->header);
57     zck->header = NULL;
58     zck->header_size = 0;
59     if(!comp_close(zck))
60         zck_log(ZCK_LOG_WARNING, "Unable to close compression");
61     hash_close(&(zck->full_hash));
62     hash_close(&(zck->check_full_hash));
63     hash_close(&(zck->check_chunk_hash));
64     clear_work_index(zck);
65     if(zck->full_hash_digest) {
66         free(zck->full_hash_digest);
67         zck->full_hash_digest = NULL;
68     }
69     if(zck->header_digest) {
70         free(zck->header_digest);
71         zck->header_digest = NULL;
72     }
73     if(zck->prep_digest) {
74         free(zck->prep_digest);
75         zck->prep_digest = NULL;
76     }
77     if(zck->temp_fd) {
78         close(zck->temp_fd);
79         zck->temp_fd = 0;
80     }
81     if(zck->msg) {
82         free(zck->msg);
83         zck->msg = NULL;
84     }
85     zck->error_state = 0;
86     zck->fd = -1;
87 }
88 
hex_to_int(char c)89 static int hex_to_int (char c) {
90     if (c >= 97)
91         c = c - 32;
92     int result = (c / 16 - 3) * 10 + (c % 16);
93     if (result > 9)
94         result--;
95     if (result < 0 || result > 15)
96         return -1;
97     return result;
98 }
99 
ascii_checksum_to_bin(zckCtx * zck,char * checksum,int checksum_length)100 static char *ascii_checksum_to_bin (zckCtx *zck, char *checksum,
101                                     int checksum_length) {
102     char *raw_checksum = zmalloc(checksum_length/2);
103     char *rp = raw_checksum;
104     int buf = 0;
105     for (int i=0; i<checksum_length; i++) {
106         // Get integer value of hex checksum character.  If -1 is returned, then
107         // the character wasn't actually hex, so return NULL
108         int cksum = hex_to_int(checksum[i]);
109         if (cksum < 0) {
110             free(raw_checksum);
111             return NULL;
112         }
113         if (i % 2 == 0)
114             buf = cksum;
115         else {
116             rp[0] = buf*16 + cksum;
117             rp++;
118         }
119     }
120     return raw_checksum;
121 }
122 
zmalloc(size_t size)123 void *zmalloc(size_t size) {
124     void *ret = calloc(1, size);
125     assert(ret);
126     return ret;
127 }
128 
zrealloc(void * ptr,size_t size)129 void *zrealloc(void *ptr, size_t size) {
130     void *ret = realloc(ptr, size);
131     assert(ret);
132     return ret;
133 }
134 
get_tmp_fd(zckCtx * zck)135 int get_tmp_fd(zckCtx *zck) {
136     VALIDATE_BOOL(zck);
137 
138     int temp_fd;
139     char *fname = NULL;
140     char template[] = "zcktempXXXXXX";
141     char *tmpdir = getenv("TMPDIR");
142 
143     if(tmpdir == NULL) {
144         tmpdir = "/tmp/";
145     } else if(strlen(tmpdir) > 1024) {
146         set_error(zck, "TMPDIR environmental variable is > 1024 bytes");
147         return -1;
148     }
149 
150     fname = zmalloc(strlen(template) + strlen(tmpdir) + 2);
151     int i=0;
152     for(i=0; i<strlen(tmpdir); i++)
153         fname[i] = tmpdir[i];
154     int offset = i;
155     fname[offset] = '/';
156     offset++;
157     for(i=0; i<strlen(template); i++)
158         fname[offset + i] = template[i];
159     offset += i;
160     fname[offset] = '\0';
161 
162     mode_t old_mode_mask;
163     old_mode_mask = umask (S_IXUSR | S_IRWXG | S_IRWXO);
164     temp_fd = mkstemp(fname);
165     umask(old_mode_mask);
166     if(temp_fd < 0) {
167         free(fname);
168         set_error(zck, "Unable to create temporary file");
169         return -1;
170     }
171     if(unlink(fname) < 0) {
172         free(fname);
173         set_error(zck, "Unable to delete temporary file");
174         return -1;
175     }
176     free(fname);
177     return temp_fd;
178 }
179 
import_dict(zckCtx * zck)180 bool import_dict(zckCtx *zck) {
181     VALIDATE_BOOL(zck);
182 
183     size_t size = zck->index.first->length;
184 
185     /* No dict */
186     if(size == 0)
187         return true;
188 
189     zck_log(ZCK_LOG_DEBUG, "Reading compression dict");
190     char *data = zmalloc(size);
191     if(comp_read(zck, data, size, 0) != size) {
192         set_error(zck, "Error reading compressed dict");
193         return false;
194     }
195     zck_log(ZCK_LOG_DEBUG, "Resetting compression");
196     if(!comp_reset(zck))
197         return false;
198     zck_log(ZCK_LOG_DEBUG, "Setting dict");
199     if(!comp_soption(zck, ZCK_COMP_DICT, data, size))
200         return false;
201     if(!comp_init(zck))
202         return false;
203 
204     return true;
205 }
206 
zck_set_soption(zckCtx * zck,zck_soption option,const char * value,size_t length)207 bool PUBLIC zck_set_soption(zckCtx *zck, zck_soption option, const char *value,
208                             size_t length) {
209     VALIDATE_BOOL(zck);
210     char *data = zmalloc(length);
211     memcpy(data, value, length);
212 
213     /* Validation options */
214     if(option == ZCK_VAL_HEADER_DIGEST) {
215         VALIDATE_READ_BOOL(zck);
216         zckHashType chk_type = {0};
217         if(zck->prep_hash_type < 0) {
218             free(data);
219             set_error(zck, "For validation, you must set the header hash type "
220                            "*before* the header digest itself");
221             return false;
222         }
223         if(!hash_setup(zck, &chk_type, zck->prep_hash_type)) {
224             free(data);
225             return false;
226         }
227         if(chk_type.digest_size*2 != length) {
228             free(data);
229             set_fatal_error(zck, "Hash digest size mismatch for header "
230                                  "validation\n"
231                                  "Expected: %i\nProvided: %lu",
232                                  chk_type.digest_size*2, length);
233             return false;
234         }
235         zck_log(ZCK_LOG_DEBUG, "Setting expected hash to (%s)%s",
236                 zck_hash_name_from_type(zck->prep_hash_type), data);
237         zck->prep_digest = ascii_checksum_to_bin(zck, data, length);
238         free(data);
239         if(zck->prep_digest == NULL) {
240             set_fatal_error(zck, "Non-hex character found in supplied digest");
241             return false;
242         }
243 
244     /* Compression options */
245     } else if(option < 2000) {
246         VALIDATE_WRITE_BOOL(zck);
247         return comp_soption(zck, option, data, length);
248 
249     /* Unknown options */
250     } else {
251         free(data);
252         set_error(zck, "Unknown string option %i", option);
253         return false;
254     }
255     return true;
256 }
257 
zck_set_ioption(zckCtx * zck,zck_ioption option,ssize_t value)258 bool PUBLIC zck_set_ioption(zckCtx *zck, zck_ioption option, ssize_t value) {
259     VALIDATE_BOOL(zck);
260 
261     /* Set hash type */
262     if(option == ZCK_HASH_FULL_TYPE) {
263         VALIDATE_WRITE_BOOL(zck);
264         return set_full_hash_type(zck, value);
265     } else if(option == ZCK_HASH_CHUNK_TYPE) {
266         VALIDATE_WRITE_BOOL(zck);
267         return set_chunk_hash_type(zck, value);
268 
269     /* Validation options */
270     } else if(option == ZCK_VAL_HEADER_HASH_TYPE) {
271         VALIDATE_READ_BOOL(zck);
272         if(value < 0) {
273             set_error(zck, "Header hash type can't be less than zero: %li",
274                       value);
275             return false;
276         }
277         /* Make sure that header hash type is set before the header digest,
278          * otherwise we run the risk of a buffer overflow */
279         if(zck->prep_digest != NULL) {
280             set_error(zck, "For validation, you must set the header hash type "
281                            "*before* the header digest itself");
282             return false;
283         }
284         zck->prep_hash_type = value;
285     } else if(option == ZCK_VAL_HEADER_LENGTH) {
286         VALIDATE_READ_BOOL(zck);
287         if(value < 0) {
288             set_error(zck,
289                       "Header size validation can't be less than zero: %li",
290                       value);
291             return false;
292         }
293         zck->prep_hdr_size = value;
294 
295     /* Hash options */
296     } else if(option < 100) {
297         /* Currently no hash options other than setting hash type, so bail */
298         set_error(zck, "Unknown option %lu", value);
299         return false;
300 
301     /* Compression options */
302     } else if(option < 2000) {
303         VALIDATE_WRITE_BOOL(zck);
304         return comp_ioption(zck, option, value);
305 
306     /* Unknown options */
307     } else {
308         set_error(zck, "Unknown integer option %i", option);
309         return false;
310     }
311     return true;
312 }
313 
zck_close(zckCtx * zck)314 bool PUBLIC zck_close(zckCtx *zck) {
315     VALIDATE_BOOL(zck);
316 
317     if(zck->mode == ZCK_MODE_WRITE) {
318         if(zck_end_chunk(zck) < 0)
319             return false;
320         if(!header_create(zck))
321             return false;
322         if(!write_header(zck))
323             return false;
324         zck_log(ZCK_LOG_DEBUG, "Writing chunks");
325         if(!chunks_from_temp(zck))
326             return false;
327         zck_log(ZCK_LOG_DEBUG, "Finished writing file, cleaning up");
328         if(!comp_close(zck))
329             return false;
330         if(zck->temp_fd) {
331             close(zck->temp_fd);
332             zck->temp_fd = 0;
333         }
334     } else {
335         if(validate_file(zck, ZCK_LOG_WARNING) < 1)
336             return false;
337     }
338 
339     return true;
340 }
341 
zck_free(zckCtx ** zck)342 void PUBLIC zck_free(zckCtx **zck) {
343     if(zck == NULL || *zck == NULL)
344         return;
345     zck_clear(*zck);
346     free(*zck);
347     *zck = NULL;
348 }
349 
zck_create()350 zckCtx PUBLIC *zck_create() {
351     zckCtx *zck = zmalloc(sizeof(zckCtx));
352     zck_clear_error(NULL);
353     zck->prep_hash_type = -1;
354     zck->prep_hdr_size = -1;
355     return zck;
356 }
357 
zck_init_adv_read(zckCtx * zck,int src_fd)358 bool PUBLIC zck_init_adv_read (zckCtx *zck, int src_fd) {
359     VALIDATE_BOOL(zck);
360 
361     zck->mode = ZCK_MODE_READ;
362     zck->fd = src_fd;
363     return true;
364 }
365 
zck_init_read(zckCtx * zck,int src_fd)366 bool PUBLIC zck_init_read (zckCtx *zck, int src_fd) {
367     VALIDATE_BOOL(zck);
368 
369     if(!zck_init_adv_read(zck, src_fd)) {
370         set_fatal_error(zck, "Unable to read file");
371         return false;
372     }
373 
374     if(!zck_read_lead(zck)) {
375         set_fatal_error(zck, "Unable to read lead");
376         return false;
377     }
378 
379     if(!zck_read_header(zck)) {
380         set_fatal_error(zck, "Unable to read header");
381         return false;
382     }
383 
384     return true;
385 }
386 
zck_init_write(zckCtx * zck,int dst_fd)387 bool PUBLIC zck_init_write (zckCtx *zck, int dst_fd) {
388     VALIDATE_BOOL(zck);
389 
390     zck->mode = ZCK_MODE_WRITE;
391     zck->temp_fd = get_tmp_fd(zck);
392     if(zck->temp_fd < 0)
393         return false;
394 
395     /* Set defaults */
396 #ifdef ZCHUNK_ZSTD
397     if(!zck_set_ioption(zck, ZCK_COMP_TYPE, ZCK_COMP_ZSTD))
398         return false;
399 #else
400     if(!zck_set_ioption(zck, ZCK_COMP_TYPE, ZCK_COMP_NONE))
401         return false;
402 #endif
403     if(!zck_set_ioption(zck, ZCK_HASH_FULL_TYPE, ZCK_HASH_SHA256))
404         return false;
405     if(!zck_set_ioption(zck, ZCK_HASH_CHUNK_TYPE, ZCK_HASH_SHA512_128))
406         return false;
407     zck->fd = dst_fd;
408 
409     return true;
410 }
411 
zck_get_fd(zckCtx * zck)412 int PUBLIC zck_get_fd(zckCtx *zck) {
413     VALIDATE_BOOL(zck);
414     return zck->fd;
415 }
416 
zck_set_fd(zckCtx * zck,int fd)417 bool PUBLIC zck_set_fd(zckCtx *zck, int fd) {
418     VALIDATE_BOOL(zck);
419     zck->fd = fd;
420     return true;
421 }
422