[BACK]Return to certhash.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / openssl

Annotation of src/usr.bin/openssl/certhash.c, Revision 1.1

1.1     ! jsing       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:
        !            36: static struct {
        !            37:        int dryrun;
        !            38:        int verbose;
        !            39: } certhash_config;
        !            40:
        !            41: struct 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:
        !            57: struct 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:
        !            71: static struct hashinfo *
        !            72: hashinfo(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:
        !            91: static void
        !            92: hashinfo_free(struct hashinfo *hi)
        !            93: {
        !            94:        free(hi->filename);
        !            95:        free(hi->target);
        !            96:        free(hi);
        !            97: }
        !            98:
        !            99: #ifdef DEBUG
        !           100: static void
        !           101: hashinfo_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:
        !           114: static int
        !           115: hashinfo_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:
        !           130: static struct hashinfo *
        !           131: hashinfo_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:
        !           144: static void
        !           145: hashinfo_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:
        !           156: static size_t
        !           157: hashinfo_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:
        !           168: static int
        !           169: hashinfo_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:
        !           196: static char *
        !           197: hashinfo_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:
        !           208: static int
        !           209: filename_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:
        !           227: static int
        !           228: filename_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:
        !           242: static struct hashinfo *
        !           243: hashinfo_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:
        !           284: err:
        !           285:        hashinfo_free(hi);
        !           286:        hi = NULL;
        !           287:
        !           288: done:
        !           289:        free(l);
        !           290:
        !           291:        return (hi);
        !           292: }
        !           293:
        !           294: static struct hashinfo *
        !           295: certhash_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:
        !           317: err:
        !           318:        X509_free(cert);
        !           319:
        !           320:        return (hi);
        !           321: }
        !           322:
        !           323: static struct hashinfo *
        !           324: certhash_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:
        !           346: err:
        !           347:        X509_CRL_free(crl);
        !           348:
        !           349:        return (hi);
        !           350: }
        !           351:
        !           352: static int
        !           353: certhash_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:
        !           360: printf("hi->is_crl = %i\n", hi->is_crl);
        !           361:        if ((link->filename = hashinfo_linkname(hi)) == NULL)
        !           362:                goto err;
        !           363: printf("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:
        !           372: err:
        !           373:        hashinfo_free(link);
        !           374:        return (-1);
        !           375: }
        !           376:
        !           377: static void
        !           378: certhash_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:
        !           397: static void
        !           398: certhash_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:
        !           426: static int
        !           427: certhash_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:
        !           469: static int
        !           470: certhash_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:
        !           502: static int
        !           503: certhash_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:
        !           548: err:
        !           549:        BIO_free(bio);
        !           550:        if (ffd != -1)
        !           551:                close(ffd);
        !           552:
        !           553:        return (ret);
        !           554: }
        !           555:
        !           556: static int
        !           557: certhash_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:
        !           632: err:
        !           633:        ret = 1;
        !           634:
        !           635: done:
        !           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:
        !           648: static void
        !           649: certhash_usage(void)
        !           650: {
        !           651:        fprintf(stderr, "usage: certhash [-nv] dir ...\n");
        !           652:        options_usage(certhash_options);
        !           653: }
        !           654:
        !           655: int certhash_main(int argc, char **argv);
        !           656:
        !           657: int
        !           658: certhash_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: }