[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.5

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/stat.h>
                     20:
                     21: #include <errno.h>
                     22: #include <dirent.h>
                     23: #include <fcntl.h>
1.2       bcook      24: #include <limits.h>
1.1       jsing      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);
1.3       guenther  124:        rv = memcmp(hia->fingerprint, hib->fingerprint,
                    125:            sizeof(hia->fingerprint));
1.1       jsing     126:        if (rv != 0)
                    127:                return (rv);
                    128:        return strcmp(hia->filename, hib->filename);
                    129: }
                    130:
                    131: static struct hashinfo *
                    132: hashinfo_chain(struct hashinfo *head, struct hashinfo *entry)
                    133: {
                    134:        struct hashinfo *hi = head;
                    135:
                    136:        if (hi == NULL)
                    137:                return (entry);
                    138:        while (hi->next != NULL)
                    139:                hi = hi->next;
                    140:        hi->next = entry;
                    141:
                    142:        return (head);
                    143: }
                    144:
                    145: static void
                    146: hashinfo_chain_free(struct hashinfo *hi)
                    147: {
                    148:        struct hashinfo *next;
                    149:
                    150:        while (hi != NULL) {
                    151:                next = hi->next;
                    152:                hashinfo_free(hi);
                    153:                hi = next;
                    154:        }
                    155: }
                    156:
                    157: static size_t
                    158: hashinfo_chain_length(struct hashinfo *hi)
                    159: {
                    160:        int len = 0;
                    161:
                    162:        while (hi != NULL) {
                    163:                len++;
                    164:                hi = hi->next;
                    165:        }
                    166:        return (len);
                    167: }
                    168:
                    169: static int
                    170: hashinfo_chain_sort(struct hashinfo **head)
                    171: {
                    172:        struct hashinfo **list, *entry;
                    173:        size_t len;
                    174:        int i;
                    175:
                    176:        if (*head == NULL)
                    177:                return (0);
                    178:
                    179:        len = hashinfo_chain_length(*head);
                    180:        if ((list = reallocarray(NULL, len, sizeof(struct hashinfo *))) == NULL)
                    181:                return (-1);
                    182:
                    183:        for (entry = *head, i = 0; entry != NULL; entry = entry->next, i++)
                    184:                list[i] = entry;
                    185:        qsort(list, len, sizeof(struct hashinfo *), hashinfo_compare);
                    186:
                    187:        *head = entry = list[0];
                    188:        for (i = 1; i < len; i++) {
                    189:                entry->next = list[i];
                    190:                entry = list[i];
                    191:        }
                    192:        entry->next = NULL;
                    193:
1.4       beck      194:        free(list);
1.1       jsing     195:        return (0);
                    196: }
                    197:
                    198: static char *
                    199: hashinfo_linkname(struct hashinfo *hi)
                    200: {
                    201:        char *filename;
                    202:
                    203:        if (asprintf(&filename, "%08lx.%s%u", hi->hash,
                    204:            (hi->is_crl ? "r" : ""), hi->index) == -1)
                    205:                return (NULL);
                    206:
                    207:        return (filename);
                    208: }
                    209:
                    210: static int
                    211: filename_is_hash(const char *filename)
                    212: {
                    213:        const char *p = filename;
                    214:
                    215:        while ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f'))
                    216:                p++;
                    217:        if (*p++ != '.')
                    218:                return (0);
                    219:        if (*p == 'r')          /* CRL format. */
                    220:                p++;
                    221:        while (*p >= '0' && *p <= '9')
                    222:                p++;
                    223:        if (*p != '\0')
                    224:                return (0);
                    225:
                    226:        return (1);
                    227: }
                    228:
                    229: static int
                    230: filename_is_pem(const char *filename)
                    231: {
                    232:        const char *q, *p = filename;
                    233:
                    234:        if ((q = strchr(p, '\0')) == NULL)
                    235:                return (0);
                    236:        if ((q - p) < 4)
                    237:                return (0);
                    238:        if (strncmp((q - 4), ".pem", 4) != 0)
                    239:                return (0);
                    240:
                    241:        return (1);
                    242: }
                    243:
                    244: static struct hashinfo *
                    245: hashinfo_from_linkname(const char *linkname, const char *target)
                    246: {
                    247:        struct hashinfo *hi = NULL;
                    248:        const char *errstr;
                    249:        char *l, *p, *ep;
                    250:        long long val;
                    251:
                    252:        if ((l = strdup(linkname)) == NULL)
                    253:                goto err;
                    254:        if ((p = strchr(l, '.')) == NULL)
                    255:                goto err;
                    256:        *p++ = '\0';
                    257:
                    258:        if ((hi = hashinfo(linkname, 0, NULL)) == NULL)
                    259:                goto err;
                    260:        if ((hi->target = strdup(target)) == NULL)
                    261:                goto err;
                    262:
                    263:        errno = 0;
                    264:        val = strtoll(l, &ep, 16);
                    265:        if (l[0] == '\0' || *ep != '\0')
                    266:                goto err;
                    267:        if (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
                    268:                goto err;
                    269:        if (val < 0 || val > ULONG_MAX)
                    270:                goto err;
                    271:        hi->hash = (unsigned long)val;
                    272:
                    273:        if (*p == 'r') {
                    274:                hi->is_crl = 1;
                    275:                p++;
                    276:        }
                    277:
                    278:        val = strtonum(p, 0, 0xffffffff, &errstr);
                    279:        if (errstr != NULL)
                    280:                goto err;
                    281:
                    282:        hi->index = (unsigned int)val;
                    283:
                    284:        goto done;
                    285:
                    286: err:
                    287:        hashinfo_free(hi);
                    288:        hi = NULL;
                    289:
                    290: done:
                    291:        free(l);
                    292:
                    293:        return (hi);
                    294: }
                    295:
                    296: static struct hashinfo *
                    297: certhash_cert(BIO *bio, const char *filename)
                    298: {
                    299:        unsigned char fingerprint[EVP_MAX_MD_SIZE];
                    300:        struct hashinfo *hi = NULL;
                    301:        const EVP_MD *digest;
                    302:        X509 *cert = NULL;
                    303:        unsigned long hash;
                    304:        unsigned int len;
                    305:
                    306:        if ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) == NULL)
                    307:                goto err;
                    308:
                    309:        hash = X509_subject_name_hash(cert);
                    310:
                    311:        digest = EVP_sha256();
                    312:        if (X509_digest(cert, digest, fingerprint, &len) != 1) {
                    313:                fprintf(stderr, "out of memory\n");
                    314:                goto err;
                    315:        }
                    316:
                    317:        hi = hashinfo(filename, hash, fingerprint);
                    318:
                    319: err:
                    320:        X509_free(cert);
                    321:
                    322:        return (hi);
                    323: }
                    324:
                    325: static struct hashinfo *
                    326: certhash_crl(BIO *bio, const char *filename)
                    327: {
                    328:        unsigned char fingerprint[EVP_MAX_MD_SIZE];
                    329:        struct hashinfo *hi = NULL;
                    330:        const EVP_MD *digest;
                    331:        X509_CRL *crl = NULL;
                    332:        unsigned long hash;
                    333:        unsigned int len;
                    334:
                    335:        if ((crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL)) == NULL)
                    336:                return (NULL);
                    337:
                    338:        hash = X509_NAME_hash(X509_CRL_get_issuer(crl));
                    339:
                    340:        digest = EVP_sha256();
                    341:        if (X509_CRL_digest(crl, digest, fingerprint, &len) != 1) {
                    342:                fprintf(stderr, "out of memory\n");
                    343:                goto err;
                    344:        }
                    345:
                    346:        hi = hashinfo(filename, hash, fingerprint);
                    347:
                    348: err:
                    349:        X509_CRL_free(crl);
                    350:
                    351:        return (hi);
                    352: }
                    353:
                    354: static int
                    355: certhash_addlink(struct hashinfo **links, struct hashinfo *hi)
                    356: {
                    357:        struct hashinfo *link = NULL;
                    358:
                    359:        if ((link = hashinfo(NULL, hi->hash, hi->fingerprint)) == NULL)
                    360:                goto err;
                    361:
                    362: printf("hi->is_crl = %i\n", hi->is_crl);
                    363:        if ((link->filename = hashinfo_linkname(hi)) == NULL)
                    364:                goto err;
                    365: printf("filename = %s\n", link->filename);
                    366:
                    367:        link->reference = hi;
                    368:        link->changed = 1;
                    369:        *links = hashinfo_chain(*links, link);
                    370:        hi->reference = link;
                    371:
                    372:        return (0);
                    373:
                    374: err:
                    375:        hashinfo_free(link);
                    376:        return (-1);
                    377: }
                    378:
                    379: static void
                    380: certhash_findlink(struct hashinfo *links, struct hashinfo *hi)
                    381: {
                    382:        struct hashinfo *link;
                    383:
                    384:        for (link = links; link != NULL; link = link->next) {
                    385:                if (link->is_crl == hi->is_crl &&
                    386:                    link->hash == hi->hash &&
                    387:                    link->index == hi->index &&
                    388:                    link->reference == NULL) {
                    389:                        link->reference = hi;
                    390:                        if (link->target == NULL ||
                    391:                            strcmp(link->target, hi->filename) != 0)
                    392:                                link->changed = 1;
                    393:                        hi->reference = link;
                    394:                        break;
                    395:                }
                    396:        }
                    397: }
                    398:
                    399: static void
                    400: certhash_index(struct hashinfo *head, const char *name)
                    401: {
                    402:        struct hashinfo *last, *entry;
                    403:        int index = 0;
                    404:
                    405:        last = NULL;
                    406:        for (entry = head; entry != NULL; entry = entry->next) {
                    407:                if (last != NULL) {
                    408:                        if (entry->hash == last->hash) {
1.3       guenther  409:                                if (memcmp(entry->fingerprint,
                    410:                                    last->fingerprint,
1.1       jsing     411:                                    sizeof(entry->fingerprint)) == 0) {
                    412:                                        fprintf(stderr, "WARNING: duplicate %s "
                    413:                                            "in %s (using %s), ignoring...\n",
                    414:                                            name, entry->filename,
                    415:                                            last->filename);
                    416:                                        entry->is_dup = 1;
                    417:                                        continue;
                    418:                                }
                    419:                                index++;
                    420:                        } else {
                    421:                                index = 0;
                    422:                        }
                    423:                }
                    424:                entry->index = index;
                    425:                last = entry;
                    426:        }
                    427: }
                    428:
                    429: static int
                    430: certhash_merge(struct hashinfo **links, struct hashinfo **certs,
                    431:     struct hashinfo **crls)
                    432: {
                    433:        struct hashinfo *cert, *crl;
                    434:
                    435:        /* Pass 1 - sort and index entries. */
                    436:        if (hashinfo_chain_sort(certs) == -1)
                    437:                return (-1);
                    438:        if (hashinfo_chain_sort(crls) == -1)
                    439:                return (-1);
                    440:        certhash_index(*certs, "certificate");
                    441:        certhash_index(*crls, "CRL");
                    442:
                    443:        /* Pass 2 - map to existing links. */
                    444:        for (cert = *certs; cert != NULL; cert = cert->next) {
                    445:                if (cert->is_dup == 1)
                    446:                        continue;
                    447:                certhash_findlink(*links, cert);
                    448:        }
                    449:        for (crl = *crls; crl != NULL; crl = crl->next) {
                    450:                if (crl->is_dup == 1)
                    451:                        continue;
                    452:                certhash_findlink(*links, crl);
                    453:        }
                    454:
                    455:        /* Pass 3 - determine missing links. */
                    456:        for (cert = *certs; cert != NULL; cert = cert->next) {
                    457:                if (cert->is_dup == 1 || cert->reference != NULL)
                    458:                        continue;
                    459:                if (certhash_addlink(links, cert) == -1)
                    460:                        return (-1);
                    461:        }
                    462:        for (crl = *crls; crl != NULL; crl = crl->next) {
                    463:                if (crl->is_dup == 1 || crl->reference != NULL)
                    464:                        continue;
                    465:                if (certhash_addlink(links, crl) == -1)
                    466:                        return (-1);
                    467:        }
                    468:
                    469:        return (0);
                    470: }
                    471:
                    472: static int
1.5     ! guenther  473: certhash_link(struct dirent *dep, struct hashinfo **links)
1.1       jsing     474: {
                    475:        struct hashinfo *hi = NULL;
                    476:        char target[MAXPATHLEN];
                    477:        struct stat sb;
                    478:        int n;
                    479:
1.5     ! guenther  480:        if (lstat(dep->d_name, &sb) == -1) {
1.1       jsing     481:                fprintf(stderr, "failed to stat %s\n", dep->d_name);
                    482:                return (-1);
                    483:        }
                    484:        if (!S_ISLNK(sb.st_mode))
                    485:                return (0);
                    486:
1.5     ! guenther  487:        n = readlink(dep->d_name, target, sizeof(target) - 1);
1.1       jsing     488:        if (n == -1) {
                    489:                fprintf(stderr, "failed to readlink %s\n", dep->d_name);
                    490:                return (-1);
                    491:        }
                    492:        target[n] = '\0';
                    493:
                    494:        hi = hashinfo_from_linkname(dep->d_name, target);
                    495:        if (hi == NULL) {
                    496:                fprintf(stderr, "failed to get hash info %s\n", dep->d_name);
                    497:                return (-1);
                    498:        }
                    499:        hi->exists = 1;
                    500:        *links = hashinfo_chain(*links, hi);
                    501:
                    502:        return (0);
                    503: }
                    504:
                    505: static int
1.5     ! guenther  506: certhash_file(struct dirent *dep, struct hashinfo **certs,
1.1       jsing     507:     struct hashinfo **crls)
                    508: {
                    509:        struct hashinfo *hi = NULL;
                    510:        int has_cert, has_crl;
1.5     ! guenther  511:        int ret = -1;
1.1       jsing     512:        BIO *bio = NULL;
                    513:        FILE *f;
                    514:
                    515:        has_cert = has_crl = 0;
                    516:
1.5     ! guenther  517:        if ((f = fopen(dep->d_name, "r")) == NULL) {
        !           518:                fprintf(stderr, "failed to fopen %s\n", dep->d_name);
1.1       jsing     519:                goto err;
                    520:        }
                    521:        if ((bio = BIO_new_fp(f, BIO_CLOSE)) == NULL) {
                    522:                fprintf(stderr, "failed to create bio\n");
1.5     ! guenther  523:                fclose(f);
1.1       jsing     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:
                    551:        return (ret);
                    552: }
                    553:
                    554: static int
                    555: certhash_directory(const char *path)
                    556: {
                    557:        struct hashinfo *links = NULL, *certs = NULL, *crls = NULL, *link;
1.5     ! guenther  558:        int ret = 0;
1.1       jsing     559:        struct dirent *dep;
                    560:        DIR *dip = NULL;
                    561:
1.5     ! guenther  562:        if ((dip = opendir(".")) == NULL) {
1.1       jsing     563:                fprintf(stderr, "failed to open directory %s\n", path);
                    564:                goto err;
                    565:        }
                    566:
                    567:        if (certhash_config.verbose)
                    568:                fprintf(stdout, "scanning directory %s\n", path);
                    569:
                    570:        /* Create lists of existing hash links, certs and CRLs. */
                    571:        while ((dep = readdir(dip)) != NULL) {
                    572:                if (filename_is_hash(dep->d_name)) {
1.5     ! guenther  573:                        if (certhash_link(dep, &links) == -1)
1.1       jsing     574:                                goto err;
                    575:                }
                    576:                if (filename_is_pem(dep->d_name)) {
1.5     ! guenther  577:                        if (certhash_file(dep, &certs, &crls) == -1)
1.1       jsing     578:                                goto err;
                    579:                }
                    580:        }
                    581:
                    582:        if (certhash_merge(&links, &certs, &crls) == -1) {
                    583:                fprintf(stderr, "certhash merge failed\n");
                    584:                goto err;
                    585:        }
                    586:
                    587:        /* Remove spurious links. */
                    588:        for (link = links; link != NULL; link = link->next) {
                    589:                if (link->exists == 0 ||
                    590:                    (link->reference != NULL && link->changed == 0))
                    591:                        continue;
                    592:                if (certhash_config.verbose)
                    593:                        fprintf(stdout, "%s link %s -> %s\n",
                    594:                            (certhash_config.dryrun ? "would remove" :
                    595:                                "removing"), link->filename, link->target);
                    596:                if (certhash_config.dryrun)
                    597:                        continue;
1.5     ! guenther  598:                if (unlink(link->filename) == -1) {
1.1       jsing     599:                        fprintf(stderr, "failed to remove link %s\n",
                    600:                            link->filename);
                    601:                        goto err;
                    602:                }
                    603:        }
                    604:
                    605:        /* Create missing links. */
                    606:        for (link = links; link != NULL; link = link->next) {
                    607:                if (link->exists == 1 && link->changed == 0)
                    608:                        continue;
                    609:                if (certhash_config.verbose)
                    610:                        fprintf(stdout, "%s link %s -> %s\n",
                    611:                            (certhash_config.dryrun ? "would create" :
                    612:                                "creating"), link->filename,
                    613:                            link->reference->filename);
                    614:                if (certhash_config.dryrun)
                    615:                        continue;
1.5     ! guenther  616:                if (symlink(link->reference->filename, link->filename) == -1) {
1.1       jsing     617:                        fprintf(stderr, "failed to create link %s -> %s\n",
                    618:                            link->filename, link->reference->filename);
                    619:                        goto err;
                    620:                }
                    621:        }
                    622:
                    623:        goto done;
                    624:
                    625: err:
                    626:        ret = 1;
                    627:
                    628: done:
                    629:        hashinfo_chain_free(certs);
                    630:        hashinfo_chain_free(crls);
                    631:        hashinfo_chain_free(links);
                    632:
                    633:        if (dip != NULL)
                    634:                closedir(dip);
                    635:        return (ret);
                    636: }
                    637:
                    638: static void
                    639: certhash_usage(void)
                    640: {
                    641:        fprintf(stderr, "usage: certhash [-nv] dir ...\n");
                    642:        options_usage(certhash_options);
                    643: }
                    644:
                    645: int certhash_main(int argc, char **argv);
                    646:
                    647: int
                    648: certhash_main(int argc, char **argv)
                    649: {
                    650:        int argsused;
1.5     ! guenther  651:        int i, cwdfd, ret = 0;
1.1       jsing     652:
                    653:        memset(&certhash_config, 0, sizeof(certhash_config));
                    654:
                    655:        if (options_parse(argc, argv, certhash_options, NULL, &argsused) != 0) {
                    656:                 certhash_usage();
                    657:                 return (1);
                    658:         }
                    659:
1.5     ! guenther  660:        if ((cwdfd = open(".", O_DIRECTORY)) == -1) {
        !           661:                perror("failed to open current directory");
        !           662:                return (1);
        !           663:        }
        !           664:
        !           665:        for (i = argsused; i < argc; i++) {
        !           666:                if (chdir(argv[i]) == -1) {
        !           667:                        fprintf(stderr,
        !           668:                            "failed to change to directory %s: %s\n",
        !           669:                            argv[i], strerror(errno));
        !           670:                        ret = 1;
        !           671:                        continue;
        !           672:                }
1.1       jsing     673:                ret |= certhash_directory(argv[i]);
1.5     ! guenther  674:                if (fchdir(cwdfd) == -1) {
        !           675:                        perror("failed to restore current directory");
        !           676:                        ret = 1;
        !           677:                        break;          /* can't continue safely */
        !           678:                }
        !           679:        }
        !           680:        close(cwdfd);
1.1       jsing     681:
                    682:        return (ret);
                    683: }