1 /*
2 * checksum.c
3 *
4 * Copyright (c) 2008- Steve McIntyre <steve@einval.com>
5 *
6 * Implementation of a generic checksum interface, used in JTE.
7 *
8 * GNU GPL v2
9 */
10
11 #include <mconfig.h>
12 #include "genisoimage.h"
13 #include <timedefs.h>
14 #include <fctldefs.h>
15 #include <regex.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include "md5.h"
20 #include "sha1.h"
21 #include "sha256.h"
22 #include "sha512.h"
23 #include "checksum.h"
24
25 #ifdef THREADED_CHECKSUMS
26 # include <pthread.h>
27 #endif
28
md5_init(void * context)29 static void md5_init(void *context)
30 {
31 mk_MD5Init(context);
32 }
md5_update(void * context,unsigned char const * buf,unsigned int len)33 static void md5_update(void *context, unsigned char const *buf, unsigned int len)
34 {
35 mk_MD5Update(context, buf, len);
36 }
md5_final(unsigned char * digest,void * context)37 static void md5_final(unsigned char *digest, void *context)
38 {
39 mk_MD5Final(digest, context);
40 }
41
sha1_init(void * context)42 static void sha1_init(void *context)
43 {
44 sha1_init_ctx(context);
45 }
sha1_update(void * context,unsigned char const * buf,unsigned int len)46 static void sha1_update(void *context, unsigned char const *buf, unsigned int len)
47 {
48 sha1_process_bytes(buf, len, context);
49 }
sha1_final(unsigned char * digest,void * context)50 static void sha1_final(unsigned char *digest, void *context)
51 {
52 sha1_finish_ctx(context, digest);
53 }
54
sha256_init(void * context)55 static void sha256_init(void *context)
56 {
57 sha256_init_ctx(context);
58 }
sha256_update(void * context,unsigned char const * buf,unsigned int len)59 static void sha256_update(void *context, unsigned char const *buf, unsigned int len)
60 {
61 sha256_process_bytes(buf, len, context);
62 }
sha256_final(unsigned char * digest,void * context)63 static void sha256_final(unsigned char *digest, void *context)
64 {
65 sha256_finish_ctx(context, digest);
66 }
67
sha512_init(void * context)68 static void sha512_init(void *context)
69 {
70 sha512_init_ctx(context);
71 }
sha512_update(void * context,unsigned char const * buf,unsigned int len)72 static void sha512_update(void *context, unsigned char const *buf, unsigned int len)
73 {
74 sha512_process_bytes(buf, len, context);
75 }
sha512_final(unsigned char * digest,void * context)76 static void sha512_final(unsigned char *digest, void *context)
77 {
78 sha512_finish_ctx(context, digest);
79 }
80
81 struct checksum_details
82 {
83 char *name;
84 char *prog;
85 int digest_size;
86 int context_size;
87 void (*init)(void *context);
88 void (*update)(void *context, unsigned char const *buf, unsigned int len);
89 void (*final)(unsigned char *digest, void *context);
90 };
91
92 static const struct checksum_details algorithms[] =
93 {
94 {
95 "MD5",
96 "md5sum",
97 16,
98 sizeof(struct mk_MD5Context),
99 md5_init,
100 md5_update,
101 md5_final
102 },
103 {
104 "SHA1",
105 "sha1sum",
106 20,
107 sizeof(struct sha1_ctx),
108 sha1_init,
109 sha1_update,
110 sha1_final
111 },
112 {
113 "SHA256",
114 "sha256sum",
115 32,
116 sizeof(struct sha256_ctx),
117 sha256_init,
118 sha256_update,
119 sha256_final
120 },
121 {
122 "SHA512",
123 "sha512sum",
124 64,
125 sizeof(struct sha512_ctx),
126 sha512_init,
127 sha512_update,
128 sha512_final
129 }
130 };
131
132 struct algo_context
133 {
134 void *context;
135 unsigned char *digest;
136 int enabled;
137 int finalised;
138 char *hexdump;
139 #ifdef THREADED_CHECKSUMS
140 unsigned char const *buf;
141 unsigned int len;
142 int which;
143 pthread_t thread;
144 struct _checksum_context *parent;
145 pthread_mutex_t start_mutex;
146 pthread_cond_t start_cv;
147 #endif
148 };
149
150 struct _checksum_context
151 {
152 #ifdef THREADED_CHECKSUMS
153 unsigned int index;
154 unsigned int threads_running;
155 unsigned int threads_desired;
156 pthread_mutex_t done_mutex;
157 pthread_cond_t done_cv;
158 #endif
159 char *owner;
160 struct algo_context algo[NUM_CHECKSUMS];
161 };
162
checksum_information(enum checksum_types which)163 struct checksum_info *checksum_information(enum checksum_types which)
164 {
165 return (struct checksum_info *)&algorithms[which];
166 }
167
168 /* Dump a buffer in hex */
hex_dump_to_buffer(char * output_buffer,unsigned char * buf,size_t buf_size)169 static void hex_dump_to_buffer(char *output_buffer, unsigned char *buf, size_t buf_size)
170 {
171 unsigned int i;
172 char *p = output_buffer;
173
174 memset(output_buffer, 0, 1 + (2*buf_size));
175 for (i = 0; i < buf_size ; i++)
176 p += sprintf(p, "%2.2x", buf[i]);
177 }
178
179 #ifdef THREADED_CHECKSUMS
checksum_thread(void * arg)180 static void *checksum_thread(void *arg)
181 {
182 struct algo_context *a = arg;
183 struct _checksum_context *c = a->parent;
184 int num_blocks_summed = 0;
185
186 while (1)
187 {
188 /* wait to be given some work to do */
189 pthread_mutex_lock(&a->start_mutex);
190 while (a->buf == NULL)
191 {
192 pthread_cond_wait(&a->start_cv, &a->start_mutex);
193 }
194 pthread_mutex_unlock(&a->start_mutex);
195
196 /* if we're given a zero-length buffer, then that means we're
197 * done */
198 if (a->len == 0)
199 break;
200
201 /* actually do the checksum on the supplied buffer */
202 algorithms[a->which].update(a->context, a->buf, a->len);
203 num_blocks_summed++;
204 a->buf = NULL;
205
206 /* and tell the main thread that we're done with that
207 * buffer */
208 pthread_mutex_lock(&c->done_mutex);
209 c->threads_running--;
210 if (c->threads_running == 0)
211 pthread_cond_signal(&c->done_cv);
212 pthread_mutex_unlock(&c->done_mutex);
213 }
214
215 pthread_exit(0);
216 }
217 #endif
218
checksum_init_context(int checksums,const char * owner)219 checksum_context_t *checksum_init_context(int checksums, const char *owner)
220 {
221 int i = 0;
222 int ret = 0;
223 struct _checksum_context *context = calloc(1, sizeof(struct _checksum_context));
224
225 if (!context)
226 return NULL;
227
228 context->owner = strdup(owner);
229 if (!context->owner)
230 {
231 free(context);
232 return NULL;
233 }
234
235 #ifdef THREADED_CHECKSUMS
236 pthread_mutex_init(&context->done_mutex, NULL);
237 pthread_cond_init(&context->done_cv, NULL);
238 context->index = 0;
239 context->threads_running = 0;
240 context->threads_desired = 0;
241
242 for (i = 0; i < NUM_CHECKSUMS; i++)
243 if ( (1 << i) & checksums)
244 context->threads_desired++;
245 #endif
246
247 for (i = 0; i < NUM_CHECKSUMS; i++)
248 {
249 struct algo_context *a = &context->algo[i];
250 if ( (1 << i) & checksums)
251 {
252 a->context = malloc(algorithms[i].context_size);
253 if (!a->context)
254 {
255 checksum_free_context(context);
256 return NULL;
257 }
258 a->digest = malloc(algorithms[i].digest_size);
259 if (!a->digest)
260 {
261 checksum_free_context(context);
262 return NULL;
263 }
264 a->hexdump = malloc(1 + (2*algorithms[i].digest_size));
265 if (!a->hexdump)
266 {
267 checksum_free_context(context);
268 return NULL;
269 }
270 algorithms[i].init(a->context);
271 a->enabled = 1;
272 a->finalised = 0;
273 #ifdef THREADED_CHECKSUMS
274 a->which = i;
275 a->parent = context;
276 a->buf = NULL;
277 a->len = 0;
278 pthread_mutex_init(&a->start_mutex, NULL);
279 pthread_cond_init(&a->start_cv, NULL);
280 ret = pthread_create(&a->thread, NULL, checksum_thread, a);
281 if (ret != 0)
282 {
283 fprintf(stderr, "failed to create new thread: %d\n", ret);
284 checksum_free_context(context);
285 return NULL;
286 }
287 #endif
288 }
289 else
290 a->enabled = 0;
291 }
292
293 return context;
294 }
295
checksum_free_context(checksum_context_t * context)296 void checksum_free_context(checksum_context_t *context)
297 {
298 int i = 0;
299 struct _checksum_context *c = context;
300
301 for (i = 0; i < NUM_CHECKSUMS; i++)
302 {
303 struct algo_context *a = &c->algo[i];
304
305 #ifdef THREADED_CHECKSUMS
306 if (a->thread)
307 {
308 void *ret;
309 pthread_cancel(a->thread);
310 pthread_join(a->thread, &ret);
311 a->thread = 0;
312 }
313 #endif
314 free(a->context);
315 free(a->digest);
316 free(a->hexdump);
317 }
318 free(c->owner);
319 free(c);
320 }
321
322 #ifdef THREADED_CHECKSUMS
checksum_update(checksum_context_t * context,unsigned char const * buf,unsigned int len)323 void checksum_update(checksum_context_t *context,
324 unsigned char const *buf, unsigned int len)
325 {
326 int i = 0;
327 struct _checksum_context *c = context;
328 static int index = 0;
329
330 index++;
331
332 c->threads_running = c->threads_desired;
333 for (i = 0; i < NUM_CHECKSUMS; i++)
334 {
335 if (c->algo[i].enabled)
336 {
337 struct algo_context *a = &c->algo[i];
338 pthread_mutex_lock(&a->start_mutex);
339 a->len = len;
340 a->buf = buf;
341 pthread_cond_signal(&a->start_cv);
342 pthread_mutex_unlock(&a->start_mutex);
343 }
344 }
345
346 /* Should now all be running, wait on them all to return */
347 pthread_mutex_lock(&c->done_mutex);
348 while (c->threads_running > 0)
349 {
350 pthread_cond_wait(&c->done_cv, &c->done_mutex);
351 }
352 pthread_mutex_unlock(&c->done_mutex);
353 }
354
355 #else // THREADED_CHECKSUMS
356
checksum_update(checksum_context_t * context,unsigned char const * buf,unsigned int len)357 void checksum_update(checksum_context_t *context,
358 unsigned char const *buf, unsigned int len)
359 {
360 int i = 0;
361 struct _checksum_context *c = context;
362
363 for (i = 0; i < NUM_CHECKSUMS; i++)
364 {
365 if (c->algo[i].enabled)
366 {
367 struct algo_context *a = &c->algo[i];
368 algorithms[i].update(a->context, buf, len);
369 }
370 }
371 }
372
373 #endif // THREADED_CHECKSUMS
374
checksum_final(checksum_context_t * context)375 void checksum_final(checksum_context_t *context)
376 {
377 int i = 0;
378 struct _checksum_context *c = context;
379
380 #ifdef THREADED_CHECKSUMS
381 void *thread_ret;
382 /* Clean up the threads */
383 c->threads_running = c->threads_desired;
384
385 for (i = 0; i < NUM_CHECKSUMS; i++)
386 {
387 if (c->algo[i].enabled)
388 {
389 void *ret = 0;
390 struct algo_context *a = &c->algo[i];
391
392 pthread_mutex_lock(&a->start_mutex);
393 a->len = 0;
394 a->buf = (unsigned char *)-1;
395 pthread_cond_signal(&a->start_cv);
396 pthread_mutex_unlock(&a->start_mutex);
397 pthread_join(a->thread, &ret);
398 a->thread = 0;
399 }
400 }
401 #endif
402
403 for (i = 0; i < NUM_CHECKSUMS; i++)
404 {
405 struct algo_context *a = &c->algo[i];
406 if (a->enabled)
407 {
408 algorithms[i].final(a->digest, a->context);
409 hex_dump_to_buffer(a->hexdump, a->digest, algorithms[i].digest_size);
410 a->finalised = 1;
411 }
412 }
413 }
414
checksum_copy(checksum_context_t * context,enum checksum_types which,unsigned char * digest)415 void checksum_copy(checksum_context_t *context,
416 enum checksum_types which,
417 unsigned char *digest)
418 {
419 struct _checksum_context *c = context;
420
421 if (c->algo[which].enabled)
422 {
423 if (c->algo[which].finalised)
424 memcpy(digest, c->algo[which].digest, algorithms[which].digest_size);
425 else
426 memset(digest, 0, algorithms[which].digest_size);
427 }
428 else
429 fprintf(stderr, "Asked for %s checksum, not enabled!\n",
430 algorithms[which].name);
431 }
432
checksum_hex(checksum_context_t * context,enum checksum_types which)433 const char *checksum_hex(checksum_context_t *context,
434 enum checksum_types which)
435 {
436 struct _checksum_context *c = context;
437
438 if (c->algo[which].enabled && c->algo[which].finalised)
439 return c->algo[which].hexdump;
440
441 /* else */
442 return NULL;
443 }
444
445
446 /* Parse the command line options for which checksums to use */
parse_checksum_algo(char * arg,int * algo)447 int parse_checksum_algo(char *arg, int *algo)
448 {
449 int error = 0;
450 int i = 0;
451 char *start_ptr = arg;
452 int len = 0;
453
454 *algo = 0;
455
456 if (!strcasecmp(arg, "all"))
457 {
458 *algo = 0xFF;
459 return 0;
460 }
461
462 while (*start_ptr != 0)
463 {
464 int match = 0;
465 len = 0;
466
467 while (start_ptr[len] != ',' && start_ptr[len] != 0)
468 len++;
469
470 if (len)
471 {
472 for (i = 0; i < NUM_CHECKSUMS; i++)
473 {
474 if (len == strlen(algorithms[i].name) &&
475 !strncasecmp(start_ptr, algorithms[i].name, len))
476 {
477 match = 1;
478 *algo |= (1 << i);
479 }
480 }
481
482 if (!match)
483 {
484 fprintf(stderr, "invalid algorithm name found in %s\n", arg);
485 return EINVAL;
486 }
487 }
488
489 if (start_ptr[len] == 0)
490 break;
491
492 start_ptr += len + 1;
493 }
494
495 if (! (*algo & CHECK_MD5_USED))
496 {
497 fprintf(stderr, "invalid choices: algorithms *must* include MD5\n");
498 return EINVAL;
499 }
500
501 return 0;
502 }
503
504 #ifdef CHECKSUM_SELF_TEST
505 #include <sys/types.h>
506 #include <sys/stat.h>
507 #include <fcntl.h>
508 #include <unistd.h>
509 #include <errno.h>
510 #include <stdlib.h>
511
main(int argc,char ** argv)512 int main(int argc, char **argv)
513 {
514 char buf[1024];
515 int fd = -1;
516 char *filename;
517 int err = 0;
518 static checksum_context_t *test_context = NULL;
519 int i = 0;
520
521 if (argc != 2)
522 {
523 fprintf(stderr, "Need a filename to act on!\n");
524 return 1;
525 }
526
527 filename = argv[1];
528 fd = open(filename, O_RDONLY);
529 if (fd < 0)
530 {
531 fprintf(stderr, "Unable to open file %s, errno %d\n", filename, errno);
532 return 1;
533 }
534
535 test_context = checksum_init_context(CHECK_ALL_USED, "test");
536 if (!test_context)
537 {
538 fprintf(stderr, "Unable to initialise checksum context\n");
539 return 1;
540 }
541
542 while(1)
543 {
544 err = read(fd, buf, sizeof(buf));
545 if (err < 0)
546 {
547 fprintf(stderr, "Failed to read from file, errno %d\n", errno);
548 return 1;
549 }
550
551 if (err == 0)
552 break; // EOF
553
554 /* else */
555 checksum_update(test_context, buf, err);
556 }
557 close(fd);
558 checksum_final(test_context);
559
560 for (i = 0; i < NUM_CHECKSUMS; i++)
561 {
562 struct checksum_info *info;
563 unsigned char r[64];
564 int j = 0;
565
566 info = checksum_information(i);
567 memset(r, 0, sizeof(r));
568
569 checksum_copy(test_context, i, r);
570
571 printf("OUR %s:\n", info->name);
572 for (j = 0; j < info->digest_size; j++)
573 printf("%2.2x", r[j]);
574 printf(" %s\n", filename);
575 printf("system checksum program (%s):\n", info->prog);
576 sprintf(buf, "%s %s", info->prog, filename);
577 system(buf);
578 printf("\n");
579 }
580 return 0;
581 }
582 #endif /* CHECKSUM_SELF_TEST */
583
584