summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/usr.bin/openssl/Makefile16
-rw-r--r--src/usr.bin/openssl/certhash.c674
-rw-r--r--src/usr.bin/openssl/progs.h4
3 files changed, 685 insertions, 9 deletions
diff --git a/src/usr.bin/openssl/Makefile b/src/usr.bin/openssl/Makefile
index 1619163a13..04a24c8c59 100644
--- a/src/usr.bin/openssl/Makefile
+++ b/src/usr.bin/openssl/Makefile
@@ -1,4 +1,4 @@
1# $OpenBSD: Makefile,v 1.4 2014/12/03 22:16:02 bcook Exp $ 1# $OpenBSD: Makefile,v 1.5 2015/02/10 15:29:34 jsing Exp $
2 2
3PROG= openssl 3PROG= openssl
4LDADD= -lssl -lcrypto 4LDADD= -lssl -lcrypto
@@ -17,12 +17,12 @@ CFLAGS+= -Wunused
17 17
18CFLAGS+= -DLIBRESSL_INTERNAL 18CFLAGS+= -DLIBRESSL_INTERNAL
19 19
20SRCS= apps.c apps_posix.c asn1pars.c ca.c ciphers.c cms.c crl.c crl2p7.c \ 20SRCS= apps.c apps_posix.c asn1pars.c ca.c certhash.c ciphers.c cms.c crl.c \
21 dgst.c dh.c dhparam.c dsa.c dsaparam.c ec.c ecparam.c enc.c engine.c \ 21 crl2p7.c dgst.c dh.c dhparam.c dsa.c dsaparam.c ec.c ecparam.c enc.c \
22 errstr.c gendh.c gendsa.c genpkey.c genrsa.c nseq.c ocsp.c openssl.c \ 22 engine.c errstr.c gendh.c gendsa.c genpkey.c genrsa.c nseq.c ocsp.c \
23 passwd.c pkcs12.c pkcs7.c pkcs8.c pkey.c pkeyparam.c pkeyutl.c prime.c \ 23 openssl.c passwd.c pkcs12.c pkcs7.c pkcs8.c pkey.c pkeyparam.c \
24 rand.c req.c rsa.c rsautl.c s_cb.c s_client.c s_server.c s_socket.c \ 24 pkeyutl.c prime.c rand.c req.c rsa.c rsautl.c s_cb.c s_client.c \
25 s_time.c sess_id.c smime.c speed.c spkac.c ts.c verify.c version.c \ 25 s_server.c s_socket.c s_time.c sess_id.c smime.c speed.c spkac.c ts.c \
26 x509.c 26 verify.c version.c x509.c
27 27
28.include <bsd.prog.mk> 28.include <bsd.prog.mk>
diff --git a/src/usr.bin/openssl/certhash.c b/src/usr.bin/openssl/certhash.c
new file mode 100644
index 0000000000..39e8324ea0
--- /dev/null
+++ b/src/usr.bin/openssl/certhash.c
@@ -0,0 +1,674 @@
1/*
2 * Copyright (c) 2014, 2015 Joel Sing <jsing@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <sys/param.h>
18#include <sys/types.h>
19#include <sys/limits.h>
20#include <sys/stat.h>
21
22#include <errno.h>
23#include <dirent.h>
24#include <fcntl.h>
25#include <stdio.h>
26#include <string.h>
27#include <unistd.h>
28
29#include <openssl/bio.h>
30#include <openssl/evp.h>
31#include <openssl/pem.h>
32#include <openssl/x509.h>
33
34#include "apps.h"
35
36static struct {
37 int dryrun;
38 int verbose;
39} certhash_config;
40
41struct option certhash_options[] = {
42 {
43 .name = "n",
44 .desc = "Perform a dry-run - do not make any changes",
45 .type = OPTION_FLAG,
46 .opt.flag = &certhash_config.dryrun,
47 },
48 {
49 .name = "v",
50 .desc = "Verbose",
51 .type = OPTION_FLAG,
52 .opt.flag = &certhash_config.verbose,
53 },
54 { NULL },
55};
56
57struct hashinfo {
58 char *filename;
59 char *target;
60 unsigned long hash;
61 unsigned int index;
62 unsigned char fingerprint[EVP_MAX_MD_SIZE];
63 int is_crl;
64 int is_dup;
65 int exists;
66 int changed;
67 struct hashinfo *reference;
68 struct hashinfo *next;
69};
70
71static struct hashinfo *
72hashinfo(const char *filename, unsigned long hash, unsigned char *fingerprint)
73{
74 struct hashinfo *hi;
75
76 if ((hi = calloc(1, sizeof(*hi))) == NULL)
77 return (NULL);
78 if (filename != NULL) {
79 if ((hi->filename = strdup(filename)) == NULL) {
80 free(hi);
81 return (NULL);
82 }
83 }
84 hi->hash = hash;
85 if (fingerprint != NULL)
86 memcpy(hi->fingerprint, fingerprint, sizeof(hi->fingerprint));
87
88 return (hi);
89}
90
91static void
92hashinfo_free(struct hashinfo *hi)
93{
94 free(hi->filename);
95 free(hi->target);
96 free(hi);
97}
98
99#ifdef DEBUG
100static void
101hashinfo_print(struct hashinfo *hi)
102{
103 int i;
104
105 printf("hashinfo %s %08lx %u %i\n", hi->filename, hi->hash,
106 hi->index, hi->is_crl);
107 for (i = 0; i < (int)EVP_MAX_MD_SIZE; i++) {
108 printf("%02X%c", hi->fingerprint[i],
109 (i + 1 == (int)EVP_MAX_MD_SIZE) ? '\n' : ':');
110 }
111}
112#endif
113
114static int
115hashinfo_compare(const void *a, const void *b)
116{
117 struct hashinfo *hia = *(struct hashinfo **)a;
118 struct hashinfo *hib = *(struct hashinfo **)b;
119 int rv;
120
121 rv = hia->hash - hib->hash;
122 if (rv != 0)
123 return (rv);
124 rv = bcmp(hia->fingerprint, hib->fingerprint, sizeof(hia->fingerprint));
125 if (rv != 0)
126 return (rv);
127 return strcmp(hia->filename, hib->filename);
128}
129
130static struct hashinfo *
131hashinfo_chain(struct hashinfo *head, struct hashinfo *entry)
132{
133 struct hashinfo *hi = head;
134
135 if (hi == NULL)
136 return (entry);
137 while (hi->next != NULL)
138 hi = hi->next;
139 hi->next = entry;
140
141 return (head);
142}
143
144static void
145hashinfo_chain_free(struct hashinfo *hi)
146{
147 struct hashinfo *next;
148
149 while (hi != NULL) {
150 next = hi->next;
151 hashinfo_free(hi);
152 hi = next;
153 }
154}
155
156static size_t
157hashinfo_chain_length(struct hashinfo *hi)
158{
159 int len = 0;
160
161 while (hi != NULL) {
162 len++;
163 hi = hi->next;
164 }
165 return (len);
166}
167
168static int
169hashinfo_chain_sort(struct hashinfo **head)
170{
171 struct hashinfo **list, *entry;
172 size_t len;
173 int i;
174
175 if (*head == NULL)
176 return (0);
177
178 len = hashinfo_chain_length(*head);
179 if ((list = reallocarray(NULL, len, sizeof(struct hashinfo *))) == NULL)
180 return (-1);
181
182 for (entry = *head, i = 0; entry != NULL; entry = entry->next, i++)
183 list[i] = entry;
184 qsort(list, len, sizeof(struct hashinfo *), hashinfo_compare);
185
186 *head = entry = list[0];
187 for (i = 1; i < len; i++) {
188 entry->next = list[i];
189 entry = list[i];
190 }
191 entry->next = NULL;
192
193 return (0);
194}
195
196static char *
197hashinfo_linkname(struct hashinfo *hi)
198{
199 char *filename;
200
201 if (asprintf(&filename, "%08lx.%s%u", hi->hash,
202 (hi->is_crl ? "r" : ""), hi->index) == -1)
203 return (NULL);
204
205 return (filename);
206}
207
208static int
209filename_is_hash(const char *filename)
210{
211 const char *p = filename;
212
213 while ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f'))
214 p++;
215 if (*p++ != '.')
216 return (0);
217 if (*p == 'r') /* CRL format. */
218 p++;
219 while (*p >= '0' && *p <= '9')
220 p++;
221 if (*p != '\0')
222 return (0);
223
224 return (1);
225}
226
227static int
228filename_is_pem(const char *filename)
229{
230 const char *q, *p = filename;
231
232 if ((q = strchr(p, '\0')) == NULL)
233 return (0);
234 if ((q - p) < 4)
235 return (0);
236 if (strncmp((q - 4), ".pem", 4) != 0)
237 return (0);
238
239 return (1);
240}
241
242static struct hashinfo *
243hashinfo_from_linkname(const char *linkname, const char *target)
244{
245 struct hashinfo *hi = NULL;
246 const char *errstr;
247 char *l, *p, *ep;
248 long long val;
249
250 if ((l = strdup(linkname)) == NULL)
251 goto err;
252 if ((p = strchr(l, '.')) == NULL)
253 goto err;
254 *p++ = '\0';
255
256 if ((hi = hashinfo(linkname, 0, NULL)) == NULL)
257 goto err;
258 if ((hi->target = strdup(target)) == NULL)
259 goto err;
260
261 errno = 0;
262 val = strtoll(l, &ep, 16);
263 if (l[0] == '\0' || *ep != '\0')
264 goto err;
265 if (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
266 goto err;
267 if (val < 0 || val > ULONG_MAX)
268 goto err;
269 hi->hash = (unsigned long)val;
270
271 if (*p == 'r') {
272 hi->is_crl = 1;
273 p++;
274 }
275
276 val = strtonum(p, 0, 0xffffffff, &errstr);
277 if (errstr != NULL)
278 goto err;
279
280 hi->index = (unsigned int)val;
281
282 goto done;
283
284err:
285 hashinfo_free(hi);
286 hi = NULL;
287
288done:
289 free(l);
290
291 return (hi);
292}
293
294static struct hashinfo *
295certhash_cert(BIO *bio, const char *filename)
296{
297 unsigned char fingerprint[EVP_MAX_MD_SIZE];
298 struct hashinfo *hi = NULL;
299 const EVP_MD *digest;
300 X509 *cert = NULL;
301 unsigned long hash;
302 unsigned int len;
303
304 if ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) == NULL)
305 goto err;
306
307 hash = X509_subject_name_hash(cert);
308
309 digest = EVP_sha256();
310 if (X509_digest(cert, digest, fingerprint, &len) != 1) {
311 fprintf(stderr, "out of memory\n");
312 goto err;
313 }
314
315 hi = hashinfo(filename, hash, fingerprint);
316
317err:
318 X509_free(cert);
319
320 return (hi);
321}
322
323static struct hashinfo *
324certhash_crl(BIO *bio, const char *filename)
325{
326 unsigned char fingerprint[EVP_MAX_MD_SIZE];
327 struct hashinfo *hi = NULL;
328 const EVP_MD *digest;
329 X509_CRL *crl = NULL;
330 unsigned long hash;
331 unsigned int len;
332
333 if ((crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL)) == NULL)
334 return (NULL);
335
336 hash = X509_NAME_hash(X509_CRL_get_issuer(crl));
337
338 digest = EVP_sha256();
339 if (X509_CRL_digest(crl, digest, fingerprint, &len) != 1) {
340 fprintf(stderr, "out of memory\n");
341 goto err;
342 }
343
344 hi = hashinfo(filename, hash, fingerprint);
345
346err:
347 X509_CRL_free(crl);
348
349 return (hi);
350}
351
352static int
353certhash_addlink(struct hashinfo **links, struct hashinfo *hi)
354{
355 struct hashinfo *link = NULL;
356
357 if ((link = hashinfo(NULL, hi->hash, hi->fingerprint)) == NULL)
358 goto err;
359
360printf("hi->is_crl = %i\n", hi->is_crl);
361 if ((link->filename = hashinfo_linkname(hi)) == NULL)
362 goto err;
363printf("filename = %s\n", link->filename);
364
365 link->reference = hi;
366 link->changed = 1;
367 *links = hashinfo_chain(*links, link);
368 hi->reference = link;
369
370 return (0);
371
372err:
373 hashinfo_free(link);
374 return (-1);
375}
376
377static void
378certhash_findlink(struct hashinfo *links, struct hashinfo *hi)
379{
380 struct hashinfo *link;
381
382 for (link = links; link != NULL; link = link->next) {
383 if (link->is_crl == hi->is_crl &&
384 link->hash == hi->hash &&
385 link->index == hi->index &&
386 link->reference == NULL) {
387 link->reference = hi;
388 if (link->target == NULL ||
389 strcmp(link->target, hi->filename) != 0)
390 link->changed = 1;
391 hi->reference = link;
392 break;
393 }
394 }
395}
396
397static void
398certhash_index(struct hashinfo *head, const char *name)
399{
400 struct hashinfo *last, *entry;
401 int index = 0;
402
403 last = NULL;
404 for (entry = head; entry != NULL; entry = entry->next) {
405 if (last != NULL) {
406 if (entry->hash == last->hash) {
407 if (bcmp(entry->fingerprint, last->fingerprint,
408 sizeof(entry->fingerprint)) == 0) {
409 fprintf(stderr, "WARNING: duplicate %s "
410 "in %s (using %s), ignoring...\n",
411 name, entry->filename,
412 last->filename);
413 entry->is_dup = 1;
414 continue;
415 }
416 index++;
417 } else {
418 index = 0;
419 }
420 }
421 entry->index = index;
422 last = entry;
423 }
424}
425
426static int
427certhash_merge(struct hashinfo **links, struct hashinfo **certs,
428 struct hashinfo **crls)
429{
430 struct hashinfo *cert, *crl;
431
432 /* Pass 1 - sort and index entries. */
433 if (hashinfo_chain_sort(certs) == -1)
434 return (-1);
435 if (hashinfo_chain_sort(crls) == -1)
436 return (-1);
437 certhash_index(*certs, "certificate");
438 certhash_index(*crls, "CRL");
439
440 /* Pass 2 - map to existing links. */
441 for (cert = *certs; cert != NULL; cert = cert->next) {
442 if (cert->is_dup == 1)
443 continue;
444 certhash_findlink(*links, cert);
445 }
446 for (crl = *crls; crl != NULL; crl = crl->next) {
447 if (crl->is_dup == 1)
448 continue;
449 certhash_findlink(*links, crl);
450 }
451
452 /* Pass 3 - determine missing links. */
453 for (cert = *certs; cert != NULL; cert = cert->next) {
454 if (cert->is_dup == 1 || cert->reference != NULL)
455 continue;
456 if (certhash_addlink(links, cert) == -1)
457 return (-1);
458 }
459 for (crl = *crls; crl != NULL; crl = crl->next) {
460 if (crl->is_dup == 1 || crl->reference != NULL)
461 continue;
462 if (certhash_addlink(links, crl) == -1)
463 return (-1);
464 }
465
466 return (0);
467}
468
469static int
470certhash_link(int dfd, struct dirent *dep, struct hashinfo **links)
471{
472 struct hashinfo *hi = NULL;
473 char target[MAXPATHLEN];
474 struct stat sb;
475 int n;
476
477 if (fstatat(dfd, dep->d_name, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
478 fprintf(stderr, "failed to stat %s\n", dep->d_name);
479 return (-1);
480 }
481 if (!S_ISLNK(sb.st_mode))
482 return (0);
483
484 n = readlinkat(dfd, dep->d_name, target, sizeof(target));
485 if (n == -1) {
486 fprintf(stderr, "failed to readlink %s\n", dep->d_name);
487 return (-1);
488 }
489 target[n] = '\0';
490
491 hi = hashinfo_from_linkname(dep->d_name, target);
492 if (hi == NULL) {
493 fprintf(stderr, "failed to get hash info %s\n", dep->d_name);
494 return (-1);
495 }
496 hi->exists = 1;
497 *links = hashinfo_chain(*links, hi);
498
499 return (0);
500}
501
502static int
503certhash_file(int dfd, struct dirent *dep, struct hashinfo **certs,
504 struct hashinfo **crls)
505{
506 struct hashinfo *hi = NULL;
507 int has_cert, has_crl;
508 int ffd, ret = -1;
509 BIO *bio = NULL;
510 FILE *f;
511
512 has_cert = has_crl = 0;
513
514 if ((ffd = openat(dfd, dep->d_name, O_RDONLY)) == -1) {
515 fprintf(stderr, "failed to open %s\n", dep->d_name);
516 goto err;
517 }
518 if ((f = fdopen(ffd, "r")) == NULL) {
519 fprintf(stderr, "failed to fdopen %s\n", dep->d_name);
520 goto err;
521 }
522 if ((bio = BIO_new_fp(f, BIO_CLOSE)) == NULL) {
523 fprintf(stderr, "failed to create bio\n");
524 goto err;
525 }
526
527 if ((hi = certhash_cert(bio, dep->d_name)) != NULL) {
528 has_cert = 1;
529 *certs = hashinfo_chain(*certs, hi);
530 }
531
532 if (BIO_reset(bio) != 0) {
533 fprintf(stderr, "BIO_reset failed\n");
534 goto err;
535 }
536
537 if ((hi = certhash_crl(bio, dep->d_name)) != NULL) {
538 has_crl = hi->is_crl = 1;
539 *crls = hashinfo_chain(*crls, hi);
540 }
541
542 if (!has_cert && !has_crl)
543 fprintf(stderr, "PEM file %s does not contain a certificate "
544 "or CRL, ignoring...\n", dep->d_name);
545
546 ret = 0;
547
548err:
549 BIO_free(bio);
550 if (ffd != -1)
551 close(ffd);
552
553 return (ret);
554}
555
556static int
557certhash_directory(const char *path)
558{
559 struct hashinfo *links = NULL, *certs = NULL, *crls = NULL, *link;
560 int dfd = -1, ret = 0;
561 struct dirent *dep;
562 DIR *dip = NULL;
563
564 if ((dfd = open(path, O_DIRECTORY)) == -1) {
565 fprintf(stderr, "failed to open directory %s\n", path);
566 goto err;
567 }
568 if ((dip = fdopendir(dfd)) == NULL) {
569 fprintf(stderr, "failed to open directory %s\n", path);
570 goto err;
571 }
572
573 if (certhash_config.verbose)
574 fprintf(stdout, "scanning directory %s\n", path);
575
576 /* Create lists of existing hash links, certs and CRLs. */
577 while ((dep = readdir(dip)) != NULL) {
578 if (filename_is_hash(dep->d_name)) {
579 if (certhash_link(dfd, dep, &links) == -1)
580 goto err;
581 }
582 if (filename_is_pem(dep->d_name)) {
583 if (certhash_file(dfd, dep, &certs, &crls) == -1)
584 goto err;
585 }
586 }
587
588 if (certhash_merge(&links, &certs, &crls) == -1) {
589 fprintf(stderr, "certhash merge failed\n");
590 goto err;
591 }
592
593 /* Remove spurious links. */
594 for (link = links; link != NULL; link = link->next) {
595 if (link->exists == 0 ||
596 (link->reference != NULL && link->changed == 0))
597 continue;
598 if (certhash_config.verbose)
599 fprintf(stdout, "%s link %s -> %s\n",
600 (certhash_config.dryrun ? "would remove" :
601 "removing"), link->filename, link->target);
602 if (certhash_config.dryrun)
603 continue;
604 if (unlinkat(dfd, link->filename, 0) == -1) {
605 fprintf(stderr, "failed to remove link %s\n",
606 link->filename);
607 goto err;
608 }
609 }
610
611 /* Create missing links. */
612 for (link = links; link != NULL; link = link->next) {
613 if (link->exists == 1 && link->changed == 0)
614 continue;
615 if (certhash_config.verbose)
616 fprintf(stdout, "%s link %s -> %s\n",
617 (certhash_config.dryrun ? "would create" :
618 "creating"), link->filename,
619 link->reference->filename);
620 if (certhash_config.dryrun)
621 continue;
622 if (symlinkat(link->reference->filename, dfd,
623 link->filename) == -1) {
624 fprintf(stderr, "failed to create link %s -> %s\n",
625 link->filename, link->reference->filename);
626 goto err;
627 }
628 }
629
630 goto done;
631
632err:
633 ret = 1;
634
635done:
636 hashinfo_chain_free(certs);
637 hashinfo_chain_free(crls);
638 hashinfo_chain_free(links);
639
640 if (dip != NULL)
641 closedir(dip);
642 else if (dfd != -1)
643 close(dfd);
644
645 return (ret);
646}
647
648static void
649certhash_usage(void)
650{
651 fprintf(stderr, "usage: certhash [-nv] dir ...\n");
652 options_usage(certhash_options);
653}
654
655int certhash_main(int argc, char **argv);
656
657int
658certhash_main(int argc, char **argv)
659{
660 int argsused;
661 int i, ret = 0;
662
663 memset(&certhash_config, 0, sizeof(certhash_config));
664
665 if (options_parse(argc, argv, certhash_options, NULL, &argsused) != 0) {
666 certhash_usage();
667 return (1);
668 }
669
670 for (i = argsused; i < argc; i++)
671 ret |= certhash_directory(argv[i]);
672
673 return (ret);
674}
diff --git a/src/usr.bin/openssl/progs.h b/src/usr.bin/openssl/progs.h
index 6f957c6f7c..e1494e1147 100644
--- a/src/usr.bin/openssl/progs.h
+++ b/src/usr.bin/openssl/progs.h
@@ -1,8 +1,9 @@
1/* $OpenBSD: progs.h,v 1.1 2014/08/26 17:47:25 jsing Exp $ */ 1/* $OpenBSD: progs.h,v 1.2 2015/02/10 15:29:34 jsing Exp $ */
2/* Public domain */ 2/* Public domain */
3 3
4extern int asn1parse_main(int argc, char *argv[]); 4extern int asn1parse_main(int argc, char *argv[]);
5extern int ca_main(int argc, char *argv[]); 5extern int ca_main(int argc, char *argv[]);
6extern int certhash_main(int argc, char *argv[]);
6extern int ciphers_main(int argc, char *argv[]); 7extern int ciphers_main(int argc, char *argv[]);
7extern int cms_main(int argc, char *argv[]); 8extern int cms_main(int argc, char *argv[]);
8extern int crl2pkcs7_main(int argc, char *argv[]); 9extern int crl2pkcs7_main(int argc, char *argv[]);
@@ -66,6 +67,7 @@ FUNCTION functions[] = {
66 /* General functions. */ 67 /* General functions. */
67 { FUNC_TYPE_GENERAL, "asn1parse", asn1parse_main }, 68 { FUNC_TYPE_GENERAL, "asn1parse", asn1parse_main },
68 { FUNC_TYPE_GENERAL, "ca", ca_main }, 69 { FUNC_TYPE_GENERAL, "ca", ca_main },
70 { FUNC_TYPE_GENERAL, "certhash", certhash_main },
69 { FUNC_TYPE_GENERAL, "ciphers", ciphers_main }, 71 { FUNC_TYPE_GENERAL, "ciphers", ciphers_main },
70#ifndef OPENSSL_NO_CMS 72#ifndef OPENSSL_NO_CMS
71 { FUNC_TYPE_GENERAL, "cms", cms_main }, 73 { FUNC_TYPE_GENERAL, "cms", cms_main },