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

Annotation of src/usr.bin/ssh/authfile.c, Revision 1.139

1.139   ! djm         1: /* $OpenBSD: authfile.c,v 1.138 2020/04/08 00:09:24 djm Exp $ */
1.1       deraadt     2: /*
1.99      markus      3:  * Copyright (c) 2000, 2013 Markus Friedl.  All rights reserved.
1.19      deraadt     4:  *
                      5:  * Redistribution and use in source and binary forms, with or without
                      6:  * modification, are permitted provided that the following conditions
                      7:  * are met:
                      8:  * 1. Redistributions of source code must retain the above copyright
                      9:  *    notice, this list of conditions and the following disclaimer.
                     10:  * 2. Redistributions in binary form must reproduce the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer in the
                     12:  *    documentation and/or other materials provided with the distribution.
                     13:  *
                     14:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     15:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     16:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     17:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     18:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
                     19:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     20:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     21:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     22:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
                     23:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.9       deraadt    24:  */
1.1       deraadt    25:
1.62      stevesk    26:
                     27: #include <sys/types.h>
                     28: #include <sys/stat.h>
1.76      deraadt    29: #include <sys/uio.h>
1.1       deraadt    30:
1.69      stevesk    31: #include <errno.h>
1.68      stevesk    32: #include <fcntl.h>
1.74      stevesk    33: #include <stdio.h>
1.73      stevesk    34: #include <stdlib.h>
1.71      stevesk    35: #include <string.h>
1.70      stevesk    36: #include <unistd.h>
1.110     deraadt    37: #include <limits.h>
1.15      markus     38:
1.25      markus     39: #include "cipher.h"
1.1       deraadt    40: #include "ssh.h"
1.25      markus     41: #include "log.h"
1.27      itojun     42: #include "authfile.h"
1.60      dtucker    43: #include "misc.h"
1.61      djm        44: #include "atomicio.h"
1.116     markus     45: #include "sshkey.h"
1.107     djm        46: #include "sshbuf.h"
                     47: #include "ssherr.h"
1.108     djm        48: #include "krl.h"
1.1       deraadt    49:
1.88      djm        50: #define MAX_KEY_FILE_SIZE      (1024 * 1024)
                     51:
1.86      djm        52: /* Save a key blob to a file */
                     53: static int
1.107     djm        54: sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename)
1.86      djm        55: {
1.137     djm        56:        int r;
                     57:        mode_t omask;
1.86      djm        58:
1.137     djm        59:        omask = umask(077);
                     60:        r = sshbuf_write_file(filename, keybuf);
                     61:        umask(omask);
                     62:        return r;
1.86      djm        63: }
                     64:
                     65: int
1.107     djm        66: sshkey_save_private(struct sshkey *key, const char *filename,
                     67:     const char *passphrase, const char *comment,
1.133     djm        68:     int format, const char *openssh_format_cipher, int openssh_format_rounds)
1.86      djm        69: {
1.107     djm        70:        struct sshbuf *keyblob = NULL;
                     71:        int r;
1.86      djm        72:
1.107     djm        73:        if ((keyblob = sshbuf_new()) == NULL)
                     74:                return SSH_ERR_ALLOC_FAIL;
                     75:        if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment,
1.133     djm        76:            format, openssh_format_cipher, openssh_format_rounds)) != 0)
1.86      djm        77:                goto out;
1.107     djm        78:        if ((r = sshkey_save_private_blob(keyblob, filename)) != 0)
1.86      djm        79:                goto out;
1.107     djm        80:        r = 0;
1.86      djm        81:  out:
1.107     djm        82:        sshbuf_free(keyblob);
                     83:        return r;
1.86      djm        84: }
                     85:
1.107     djm        86: /* XXX remove error() calls from here? */
1.63      dtucker    87: int
1.107     djm        88: sshkey_perm_ok(int fd, const char *filename)
1.15      markus     89: {
                     90:        struct stat st;
                     91:
1.132     deraadt    92:        if (fstat(fd, &st) == -1)
1.107     djm        93:                return SSH_ERR_SYSTEM_ERROR;
1.38      markus     94:        /*
                     95:         * if a key owned by the user is accessed, then we check the
                     96:         * permissions of the file. if the key owned by a different user,
                     97:         * then we don't care.
                     98:         */
                     99:        if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
1.15      markus    100:                error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
                    101:                error("@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @");
                    102:                error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1.38      markus    103:                error("Permissions 0%3.3o for '%s' are too open.",
1.54      djm       104:                    (u_int)st.st_mode & 0777, filename);
1.114     djm       105:                error("It is required that your private key files are NOT accessible by others.");
1.29      markus    106:                error("This private key will be ignored.");
1.107     djm       107:                return SSH_ERR_KEY_BAD_PERMISSIONS;
1.15      markus    108:        }
1.107     djm       109:        return 0;
1.29      markus    110: }
                    111:
1.107     djm       112: int
                    113: sshkey_load_private_type(int type, const char *filename, const char *passphrase,
1.134     dtucker   114:     struct sshkey **keyp, char **commentp)
1.86      djm       115: {
1.107     djm       116:        int fd, r;
1.99      markus    117:
1.121     djm       118:        if (keyp != NULL)
                    119:                *keyp = NULL;
1.107     djm       120:        if (commentp != NULL)
                    121:                *commentp = NULL;
1.86      djm       122:
1.134     dtucker   123:        if ((fd = open(filename, O_RDONLY)) == -1)
1.107     djm       124:                return SSH_ERR_SYSTEM_ERROR;
1.134     dtucker   125:
                    126:        r = sshkey_perm_ok(fd, filename);
                    127:        if (r != 0)
1.107     djm       128:                goto out;
1.86      djm       129:
1.109     djm       130:        r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
1.128     markus    131:        if (r == 0 && keyp && *keyp)
                    132:                r = sshkey_set_filename(*keyp, filename);
1.109     djm       133:  out:
                    134:        close(fd);
                    135:        return r;
                    136: }
                    137:
                    138: int
                    139: sshkey_load_private_type_fd(int fd, int type, const char *passphrase,
                    140:     struct sshkey **keyp, char **commentp)
                    141: {
                    142:        struct sshbuf *buffer = NULL;
                    143:        int r;
                    144:
1.121     djm       145:        if (keyp != NULL)
                    146:                *keyp = NULL;
1.137     djm       147:        if ((r = sshbuf_load_fd(fd, &buffer)) != 0 ||
1.109     djm       148:            (r = sshkey_parse_private_fileblob_type(buffer, type,
                    149:            passphrase, keyp, commentp)) != 0)
1.107     djm       150:                goto out;
1.109     djm       151:
                    152:        /* success */
1.107     djm       153:        r = 0;
                    154:  out:
1.120     mmcc      155:        sshbuf_free(buffer);
1.107     djm       156:        return r;
1.29      markus    157: }
                    158:
1.107     djm       159: /* XXX this is almost identical to sshkey_load_private_type() */
                    160: int
                    161: sshkey_load_private(const char *filename, const char *passphrase,
                    162:     struct sshkey **keyp, char **commentp)
1.88      djm       163: {
1.107     djm       164:        struct sshbuf *buffer = NULL;
                    165:        int r, fd;
1.88      djm       166:
1.121     djm       167:        if (keyp != NULL)
                    168:                *keyp = NULL;
1.107     djm       169:        if (commentp != NULL)
                    170:                *commentp = NULL;
1.88      djm       171:
1.132     deraadt   172:        if ((fd = open(filename, O_RDONLY)) == -1)
1.107     djm       173:                return SSH_ERR_SYSTEM_ERROR;
                    174:        if (sshkey_perm_ok(fd, filename) != 0) {
                    175:                r = SSH_ERR_KEY_BAD_PERMISSIONS;
                    176:                goto out;
1.29      markus    177:        }
1.137     djm       178:        if ((r = sshbuf_load_fd(fd, &buffer)) != 0 ||
1.117     tim       179:            (r = sshkey_parse_private_fileblob(buffer, passphrase, keyp,
                    180:            commentp)) != 0)
1.107     djm       181:                goto out;
1.128     markus    182:        if (keyp && *keyp &&
                    183:            (r = sshkey_set_filename(*keyp, filename)) != 0)
                    184:                goto out;
1.107     djm       185:        r = 0;
                    186:  out:
1.86      djm       187:        close(fd);
1.120     mmcc      188:        sshbuf_free(buffer);
1.107     djm       189:        return r;
1.18      markus    190: }
                    191:
1.139   ! djm       192: /* Load a pubkey from the unencrypted envelope of a new-format private key */
        !           193: static int
        !           194: sshkey_load_pubkey_from_private(const char *filename, struct sshkey **pubkeyp)
        !           195: {
        !           196:        struct sshbuf *buffer = NULL;
        !           197:        struct sshkey *pubkey = NULL;
        !           198:        int r, fd;
        !           199:
        !           200:        if (pubkeyp != NULL)
        !           201:                *pubkeyp = NULL;
        !           202:
        !           203:        if ((fd = open(filename, O_RDONLY)) == -1)
        !           204:                return SSH_ERR_SYSTEM_ERROR;
        !           205:        if ((r = sshbuf_load_fd(fd, &buffer)) != 0 ||
        !           206:            (r = sshkey_parse_pubkey_from_private_fileblob_type(buffer,
        !           207:            KEY_UNSPEC, &pubkey)) != 0)
        !           208:                goto out;
        !           209:        if ((r = sshkey_set_filename(pubkey, filename)) != 0)
        !           210:                goto out;
        !           211:        /* success */
        !           212:        if (pubkeyp != NULL) {
        !           213:                *pubkeyp = pubkey;
        !           214:                pubkey = NULL;
        !           215:        }
        !           216:        r = 0;
        !           217:  out:
        !           218:        close(fd);
        !           219:        sshbuf_free(buffer);
        !           220:        sshkey_free(pubkey);
        !           221:        return r;
        !           222: }
        !           223:
1.37      itojun    224: static int
1.138     djm       225: sshkey_try_load_public(struct sshkey **kp, const char *filename,
                    226:     char **commentp)
1.18      markus    227: {
                    228:        FILE *f;
1.129     markus    229:        char *line = NULL, *cp;
                    230:        size_t linesize = 0;
1.107     djm       231:        int r;
1.138     djm       232:        struct sshkey *k = NULL;
1.18      markus    233:
1.138     djm       234:        *kp = NULL;
1.107     djm       235:        if (commentp != NULL)
                    236:                *commentp = NULL;
                    237:        if ((f = fopen(filename, "r")) == NULL)
                    238:                return SSH_ERR_SYSTEM_ERROR;
1.138     djm       239:        if ((k = sshkey_new(KEY_UNSPEC)) == NULL) {
                    240:                fclose(f);
                    241:                return SSH_ERR_ALLOC_FAIL;
                    242:        }
1.129     markus    243:        while (getline(&line, &linesize, f) != -1) {
1.107     djm       244:                cp = line;
                    245:                switch (*cp) {
                    246:                case '#':
                    247:                case '\n':
                    248:                case '\0':
                    249:                        continue;
                    250:                }
                    251:                /* Abort loading if this looks like a private key */
                    252:                if (strncmp(cp, "-----BEGIN", 10) == 0 ||
                    253:                    strcmp(cp, "SSH PRIVATE KEY FILE") == 0)
                    254:                        break;
                    255:                /* Skip leading whitespace. */
                    256:                for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
                    257:                        ;
                    258:                if (*cp) {
                    259:                        if ((r = sshkey_read(k, &cp)) == 0) {
                    260:                                cp[strcspn(cp, "\r\n")] = '\0';
                    261:                                if (commentp) {
                    262:                                        *commentp = strdup(*cp ?
                    263:                                            cp : filename);
                    264:                                        if (*commentp == NULL)
                    265:                                                r = SSH_ERR_ALLOC_FAIL;
1.18      markus    266:                                }
1.138     djm       267:                                /* success */
                    268:                                *kp = k;
1.129     markus    269:                                free(line);
1.107     djm       270:                                fclose(f);
                    271:                                return r;
1.18      markus    272:                        }
                    273:                }
                    274:        }
1.138     djm       275:        free(k);
1.129     markus    276:        free(line);
1.107     djm       277:        fclose(f);
                    278:        return SSH_ERR_INVALID_FORMAT;
1.18      markus    279: }
                    280:
1.125     markus    281: /* load public key from any pubkey file */
1.107     djm       282: int
                    283: sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp)
1.18      markus    284: {
1.138     djm       285:        char *pubfile = NULL;
1.125     markus    286:        int r;
1.18      markus    287:
1.107     djm       288:        if (keyp != NULL)
                    289:                *keyp = NULL;
                    290:        if (commentp != NULL)
                    291:                *commentp = NULL;
                    292:
1.138     djm       293:        if ((r = sshkey_try_load_public(keyp, filename, commentp)) == 0)
1.125     markus    294:                goto out;
1.53      markus    295:
1.107     djm       296:        /* try .pub suffix */
1.138     djm       297:        if (asprintf(&pubfile, "%s.pub", filename) == -1)
1.107     djm       298:                return SSH_ERR_ALLOC_FAIL;
1.138     djm       299:        if ((r = sshkey_try_load_public(keyp, pubfile, commentp)) == 0)
1.139   ! djm       300:                goto out;
        !           301:
        !           302:        /* finally, try to extract public key from private key file */
        !           303:        if ((r = sshkey_load_pubkey_from_private(filename, keyp)) == 0)
1.125     markus    304:                goto out;
1.138     djm       305:
1.125     markus    306:  out:
1.138     djm       307:        free(pubfile);
1.107     djm       308:        return r;
1.81      djm       309: }
                    310:
                    311: /* Load the certificate associated with the named private key */
1.107     djm       312: int
                    313: sshkey_load_cert(const char *filename, struct sshkey **keyp)
1.81      djm       314: {
1.107     djm       315:        struct sshkey *pub = NULL;
                    316:        char *file = NULL;
                    317:        int r = SSH_ERR_INTERNAL_ERROR;
                    318:
1.121     djm       319:        if (keyp != NULL)
                    320:                *keyp = NULL;
1.81      djm       321:
1.107     djm       322:        if (asprintf(&file, "%s-cert.pub", filename) == -1)
                    323:                return SSH_ERR_ALLOC_FAIL;
                    324:
1.138     djm       325:        r = sshkey_try_load_public(keyp, file, NULL);
1.118     mmcc      326:        free(file);
1.119     mmcc      327:        sshkey_free(pub);
1.107     djm       328:        return r;
1.81      djm       329: }
                    330:
                    331: /* Load private key and certificate */
1.107     djm       332: int
                    333: sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
1.134     dtucker   334:     struct sshkey **keyp)
1.81      djm       335: {
1.107     djm       336:        struct sshkey *key = NULL, *cert = NULL;
                    337:        int r;
                    338:
1.121     djm       339:        if (keyp != NULL)
                    340:                *keyp = NULL;
1.81      djm       341:
                    342:        switch (type) {
1.106     markus    343: #ifdef WITH_OPENSSL
1.81      djm       344:        case KEY_RSA:
                    345:        case KEY_DSA:
1.83      djm       346:        case KEY_ECDSA:
1.116     markus    347: #endif /* WITH_OPENSSL */
1.101     djm       348:        case KEY_ED25519:
1.128     markus    349:        case KEY_XMSS:
1.107     djm       350:        case KEY_UNSPEC:
1.81      djm       351:                break;
                    352:        default:
1.107     djm       353:                return SSH_ERR_KEY_TYPE_UNKNOWN;
1.81      djm       354:        }
                    355:
1.107     djm       356:        if ((r = sshkey_load_private_type(type, filename,
1.134     dtucker   357:            passphrase, &key, NULL)) != 0 ||
1.107     djm       358:            (r = sshkey_load_cert(filename, &cert)) != 0)
                    359:                goto out;
1.81      djm       360:
                    361:        /* Make sure the private key matches the certificate */
1.107     djm       362:        if (sshkey_equal_public(key, cert) == 0) {
                    363:                r = SSH_ERR_KEY_CERT_MISMATCH;
                    364:                goto out;
1.81      djm       365:        }
                    366:
1.115     djm       367:        if ((r = sshkey_to_certified(key)) != 0 ||
1.107     djm       368:            (r = sshkey_cert_copy(cert, key)) != 0)
                    369:                goto out;
                    370:        r = 0;
1.121     djm       371:        if (keyp != NULL) {
                    372:                *keyp = key;
                    373:                key = NULL;
                    374:        }
1.107     djm       375:  out:
1.119     mmcc      376:        sshkey_free(key);
                    377:        sshkey_free(cert);
1.107     djm       378:        return r;
1.1       deraadt   379: }
1.80      djm       380:
                    381: /*
1.107     djm       382:  * Returns success if the specified "key" is listed in the file "filename",
                    383:  * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
1.108     djm       384:  * If "strict_type" is set then the key type must match exactly,
1.80      djm       385:  * otherwise a comparison that ignores certficiate data is performed.
1.108     djm       386:  * If "check_ca" is set and "key" is a certificate, then its CA key is
                    387:  * also checked and sshkey_in_file() will return success if either is found.
1.80      djm       388:  */
                    389: int
1.108     djm       390: sshkey_in_file(struct sshkey *key, const char *filename, int strict_type,
                    391:     int check_ca)
1.80      djm       392: {
                    393:        FILE *f;
1.129     markus    394:        char *line = NULL, *cp;
                    395:        size_t linesize = 0;
1.107     djm       396:        int r = 0;
                    397:        struct sshkey *pub = NULL;
1.129     markus    398:
1.107     djm       399:        int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
                    400:            strict_type ?  sshkey_equal : sshkey_equal_public;
1.80      djm       401:
1.108     djm       402:        if ((f = fopen(filename, "r")) == NULL)
                    403:                return SSH_ERR_SYSTEM_ERROR;
1.80      djm       404:
1.129     markus    405:        while (getline(&line, &linesize, f) != -1) {
1.131     djm       406:                sshkey_free(pub);
                    407:                pub = NULL;
1.80      djm       408:                cp = line;
                    409:
                    410:                /* Skip leading whitespace. */
                    411:                for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
                    412:                        ;
                    413:
                    414:                /* Skip comments and empty lines */
                    415:                switch (*cp) {
                    416:                case '#':
                    417:                case '\n':
                    418:                case '\0':
                    419:                        continue;
                    420:                }
                    421:
1.107     djm       422:                if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
                    423:                        r = SSH_ERR_ALLOC_FAIL;
                    424:                        goto out;
1.80      djm       425:                }
1.131     djm       426:                switch (r = sshkey_read(pub, &cp)) {
                    427:                case 0:
                    428:                        break;
                    429:                case SSH_ERR_KEY_LENGTH:
                    430:                        continue;
                    431:                default:
1.107     djm       432:                        goto out;
1.131     djm       433:                }
1.108     djm       434:                if (sshkey_compare(key, pub) ||
                    435:                    (check_ca && sshkey_is_cert(key) &&
                    436:                    sshkey_compare(key->cert->signature_key, pub))) {
1.107     djm       437:                        r = 0;
                    438:                        goto out;
1.80      djm       439:                }
                    440:        }
1.107     djm       441:        r = SSH_ERR_KEY_NOT_FOUND;
                    442:  out:
1.129     markus    443:        free(line);
1.119     mmcc      444:        sshkey_free(pub);
1.80      djm       445:        fclose(f);
1.107     djm       446:        return r;
1.108     djm       447: }
                    448:
                    449: /*
                    450:  * Checks whether the specified key is revoked, returning 0 if not,
                    451:  * SSH_ERR_KEY_REVOKED if it is or another error code if something
                    452:  * unexpected happened.
                    453:  * This will check both the key and, if it is a certificate, its CA key too.
                    454:  * "revoked_keys_file" may be a KRL or a one-per-line list of public keys.
                    455:  */
                    456: int
                    457: sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file)
                    458: {
                    459:        int r;
                    460:
                    461:        r = ssh_krl_file_contains_key(revoked_keys_file, key);
                    462:        /* If this was not a KRL to begin with then continue below */
                    463:        if (r != SSH_ERR_KRL_BAD_MAGIC)
                    464:                return r;
                    465:
                    466:        /*
                    467:         * If the file is not a KRL or we can't handle KRLs then attempt to
                    468:         * parse the file as a flat list of keys.
                    469:         */
                    470:        switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) {
                    471:        case 0:
                    472:                /* Key found => revoked */
                    473:                return SSH_ERR_KEY_REVOKED;
                    474:        case SSH_ERR_KEY_NOT_FOUND:
                    475:                /* Key not found => not revoked */
                    476:                return 0;
                    477:        default:
                    478:                /* Some other error occurred */
                    479:                return r;
                    480:        }
1.135     djm       481: }
                    482:
                    483: /*
                    484:  * Advanced *cpp past the end of key options, defined as the first unquoted
                    485:  * whitespace character. Returns 0 on success or -1 on failure (e.g.
                    486:  * unterminated quotes).
                    487:  */
                    488: int
                    489: sshkey_advance_past_options(char **cpp)
                    490: {
                    491:        char *cp = *cpp;
                    492:        int quoted = 0;
                    493:
                    494:        for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
                    495:                if (*cp == '\\' && cp[1] == '"')
                    496:                        cp++;   /* Skip both */
                    497:                else if (*cp == '"')
                    498:                        quoted = !quoted;
                    499:        }
                    500:        *cpp = cp;
                    501:        /* return failure for unterminated quotes */
                    502:        return (*cp == '\0' && quoted) ? -1 : 0;
1.80      djm       503: }
1.107     djm       504:
1.136     djm       505: /* Save a public key */
                    506: int
                    507: sshkey_save_public(const struct sshkey *key, const char *path,
                    508:     const char *comment)
                    509: {
                    510:        int fd, oerrno;
                    511:        FILE *f = NULL;
                    512:        int r = SSH_ERR_INTERNAL_ERROR;
                    513:
                    514:        if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
                    515:                return SSH_ERR_SYSTEM_ERROR;
                    516:        if ((f = fdopen(fd, "w")) == NULL) {
                    517:                r = SSH_ERR_SYSTEM_ERROR;
                    518:                goto fail;
                    519:        }
                    520:        if ((r = sshkey_write(key, f)) != 0)
                    521:                goto fail;
                    522:        fprintf(f, " %s\n", comment);
                    523:        if (ferror(f) || fclose(f) != 0) {
                    524:                r = SSH_ERR_SYSTEM_ERROR;
                    525:  fail:
                    526:                oerrno = errno;
                    527:                if (f != NULL)
                    528:                        fclose(f);
                    529:                else
                    530:                        close(fd);
                    531:                errno = oerrno;
                    532:                return r;
                    533:        }
                    534:        return 0;
                    535: }