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