Annotation of src/usr.bin/ssh/authfile.c, Revision 1.107
1.107 ! djm 1: /* $OpenBSD: authfile.c,v 1.106 2014/04/29 18:01:49 markus 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.72 stevesk 29: #include <sys/param.h>
1.76 deraadt 30: #include <sys/uio.h>
1.1 deraadt 31:
1.69 stevesk 32: #include <errno.h>
1.68 stevesk 33: #include <fcntl.h>
1.74 stevesk 34: #include <stdio.h>
1.73 stevesk 35: #include <stdlib.h>
1.71 stevesk 36: #include <string.h>
1.70 stevesk 37: #include <unistd.h>
1.15 markus 38:
1.25 markus 39: #include "cipher.h"
40: #include "key.h"
1.1 deraadt 41: #include "ssh.h"
1.25 markus 42: #include "log.h"
1.27 itojun 43: #include "authfile.h"
1.44 markus 44: #include "rsa.h"
1.60 dtucker 45: #include "misc.h"
1.61 djm 46: #include "atomicio.h"
1.107 ! djm 47: #include "sshbuf.h"
! 48: #include "ssherr.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.107 ! djm 56: int fd, oerrno;
1.86 djm 57:
1.107 ! djm 58: if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
! 59: return SSH_ERR_SYSTEM_ERROR;
! 60: if (atomicio(vwrite, fd, (u_char *)sshbuf_ptr(keybuf),
! 61: sshbuf_len(keybuf)) != sshbuf_len(keybuf)) {
! 62: oerrno = errno;
1.86 djm 63: close(fd);
64: unlink(filename);
1.107 ! djm 65: errno = oerrno;
! 66: return SSH_ERR_SYSTEM_ERROR;
1.86 djm 67: }
68: close(fd);
1.107 ! djm 69: return 0;
1.86 djm 70: }
71:
72: int
1.107 ! djm 73: sshkey_save_private(struct sshkey *key, const char *filename,
! 74: const char *passphrase, const char *comment,
! 75: int force_new_format, const char *new_format_cipher, int new_format_rounds)
1.86 djm 76: {
1.107 ! djm 77: struct sshbuf *keyblob = NULL;
! 78: int r;
1.86 djm 79:
1.107 ! djm 80: if ((keyblob = sshbuf_new()) == NULL)
! 81: return SSH_ERR_ALLOC_FAIL;
! 82: if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment,
! 83: force_new_format, new_format_cipher, new_format_rounds)) != 0)
1.86 djm 84: goto out;
1.107 ! djm 85: if ((r = sshkey_save_private_blob(keyblob, filename)) != 0)
1.86 djm 86: goto out;
1.107 ! djm 87: r = 0;
1.86 djm 88: out:
1.107 ! djm 89: sshbuf_free(keyblob);
! 90: return r;
1.86 djm 91: }
92:
1.88 djm 93: /* Load a key from a fd into a buffer */
94: int
1.107 ! djm 95: sshkey_load_file(int fd, const char *filename, struct sshbuf *blob)
1.86 djm 96: {
1.88 djm 97: u_char buf[1024];
1.86 djm 98: size_t len;
1.51 fgsch 99: struct stat st;
1.107 ! djm 100: int r;
1.8 markus 101:
1.107 ! djm 102: if (fstat(fd, &st) < 0)
! 103: return SSH_ERR_SYSTEM_ERROR;
1.88 djm 104: if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
1.107 ! djm 105: st.st_size > MAX_KEY_FILE_SIZE)
! 106: return SSH_ERR_INVALID_FORMAT;
1.88 djm 107: for (;;) {
108: if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) {
109: if (errno == EPIPE)
110: break;
1.107 ! djm 111: r = SSH_ERR_SYSTEM_ERROR;
! 112: goto out;
1.88 djm 113: }
1.107 ! djm 114: if ((r = sshbuf_put(blob, buf, len)) != 0)
! 115: goto out;
! 116: if (sshbuf_len(blob) > MAX_KEY_FILE_SIZE) {
! 117: r = SSH_ERR_INVALID_FORMAT;
! 118: goto out;
1.88 djm 119: }
120: }
121: if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
1.107 ! djm 122: st.st_size != (off_t)sshbuf_len(blob)) {
! 123: r = SSH_ERR_FILE_CHANGED;
! 124: goto out;
1.8 markus 125: }
1.107 ! djm 126: r = 0;
1.88 djm 127:
1.107 ! djm 128: out:
! 129: explicit_bzero(buf, sizeof(buf));
! 130: if (r != 0)
! 131: sshbuf_reset(blob);
! 132: return r;
1.86 djm 133: }
1.8 markus 134:
1.106 markus 135: #ifdef WITH_SSH1
1.86 djm 136: /*
137: * Loads the public part of the ssh v1 key file. Returns NULL if an error was
138: * encountered (the file does not exist or is not readable), and the key
139: * otherwise.
140: */
1.107 ! djm 141: static int
! 142: sshkey_load_public_rsa1(int fd, const char *filename,
! 143: struct sshkey **keyp, char **commentp)
1.86 djm 144: {
1.107 ! djm 145: struct sshbuf *b = NULL;
! 146: int r;
1.86 djm 147:
1.107 ! djm 148: *keyp = NULL;
! 149: if (commentp != NULL)
! 150: *commentp = NULL;
1.8 markus 151:
1.107 ! djm 152: if ((b = sshbuf_new()) == NULL)
! 153: return SSH_ERR_ALLOC_FAIL;
! 154: if ((r = sshkey_load_file(fd, filename, b)) != 0)
! 155: goto out;
! 156: if ((r = sshkey_parse_public_rsa1_fileblob(b, keyp, commentp)) != 0)
! 157: goto out;
! 158: r = 0;
! 159: out:
! 160: sshbuf_free(b);
! 161: return r;
1.1 deraadt 162: }
1.107 ! djm 163: #endif /* WITH_SSH1 */
1.1 deraadt 164:
1.107 ! djm 165: #ifdef WITH_OPENSSL
! 166: /* XXX Deprecate? */
! 167: int
! 168: sshkey_load_private_pem(int fd, int type, const char *passphrase,
! 169: struct sshkey **keyp, char **commentp)
1.1 deraadt 170: {
1.107 ! djm 171: struct sshbuf *buffer = NULL;
! 172: int r;
1.8 markus 173:
1.107 ! djm 174: *keyp = NULL;
1.97 djm 175: if (commentp != NULL)
1.107 ! djm 176: *commentp = NULL;
1.15 markus 177:
1.107 ! djm 178: if ((buffer = sshbuf_new()) == NULL)
! 179: return SSH_ERR_ALLOC_FAIL;
! 180: if ((r = sshkey_load_file(fd, NULL, buffer)) != 0)
! 181: goto out;
! 182: if ((r = sshkey_parse_private_pem_fileblob(buffer, type, passphrase,
! 183: keyp, commentp)) != 0)
! 184: goto out;
! 185: r = 0;
! 186: out:
! 187: sshbuf_free(buffer);
! 188: return r;
1.86 djm 189: }
1.107 ! djm 190: #endif /* WITH_OPENSSL */
1.86 djm 191:
1.107 ! djm 192: /* XXX remove error() calls from here? */
1.63 dtucker 193: int
1.107 ! djm 194: sshkey_perm_ok(int fd, const char *filename)
1.15 markus 195: {
196: struct stat st;
197:
1.38 markus 198: if (fstat(fd, &st) < 0)
1.107 ! djm 199: return SSH_ERR_SYSTEM_ERROR;
1.38 markus 200: /*
201: * if a key owned by the user is accessed, then we check the
202: * permissions of the file. if the key owned by a different user,
203: * then we don't care.
204: */
205: if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
1.15 markus 206: error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
207: error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @");
208: error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1.38 markus 209: error("Permissions 0%3.3o for '%s' are too open.",
1.54 djm 210: (u_int)st.st_mode & 0777, filename);
1.15 markus 211: error("It is recommended that your private key files are NOT accessible by others.");
1.29 markus 212: error("This private key will be ignored.");
1.107 ! djm 213: return SSH_ERR_KEY_BAD_PERMISSIONS;
1.15 markus 214: }
1.107 ! djm 215: return 0;
1.29 markus 216: }
217:
1.107 ! djm 218: /* XXX kill perm_ok now that we have SSH_ERR_KEY_BAD_PERMISSIONS? */
! 219: int
! 220: sshkey_load_private_type(int type, const char *filename, const char *passphrase,
! 221: struct sshkey **keyp, char **commentp, int *perm_ok)
1.86 djm 222: {
1.107 ! djm 223: int fd, r;
! 224: struct sshbuf *buffer = NULL;
1.99 markus 225:
1.107 ! djm 226: *keyp = NULL;
! 227: if (commentp != NULL)
! 228: *commentp = NULL;
1.86 djm 229:
1.107 ! djm 230: if ((fd = open(filename, O_RDONLY)) < 0) {
1.78 dtucker 231: if (perm_ok != NULL)
232: *perm_ok = 0;
1.107 ! djm 233: return SSH_ERR_SYSTEM_ERROR;
1.78 dtucker 234: }
1.107 ! djm 235: if (sshkey_perm_ok(fd, filename) != 0) {
1.67 dtucker 236: if (perm_ok != NULL)
237: *perm_ok = 0;
1.107 ! djm 238: r = SSH_ERR_KEY_BAD_PERMISSIONS;
! 239: goto out;
1.29 markus 240: }
1.67 dtucker 241: if (perm_ok != NULL)
242: *perm_ok = 1;
1.86 djm 243:
1.107 ! djm 244: if ((buffer = sshbuf_new()) == NULL) {
! 245: r = SSH_ERR_ALLOC_FAIL;
! 246: goto out;
1.15 markus 247: }
1.107 ! djm 248: if ((r = sshkey_load_file(fd, filename, buffer)) != 0)
! 249: goto out;
! 250: if ((r = sshkey_parse_private_fileblob_type(buffer, type, passphrase,
! 251: keyp, commentp)) != 0)
! 252: goto out;
! 253: r = 0;
! 254: out:
1.86 djm 255: close(fd);
1.107 ! djm 256: if (buffer != NULL)
! 257: sshbuf_free(buffer);
! 258: return r;
1.29 markus 259: }
260:
1.107 ! djm 261: /* XXX this is almost identical to sshkey_load_private_type() */
! 262: int
! 263: sshkey_load_private(const char *filename, const char *passphrase,
! 264: struct sshkey **keyp, char **commentp)
1.88 djm 265: {
1.107 ! djm 266: struct sshbuf *buffer = NULL;
! 267: int r, fd;
1.88 djm 268:
1.107 ! djm 269: *keyp = NULL;
! 270: if (commentp != NULL)
! 271: *commentp = NULL;
1.88 djm 272:
1.107 ! djm 273: if ((fd = open(filename, O_RDONLY)) < 0)
! 274: return SSH_ERR_SYSTEM_ERROR;
! 275: if (sshkey_perm_ok(fd, filename) != 0) {
! 276: r = SSH_ERR_KEY_BAD_PERMISSIONS;
! 277: goto out;
1.29 markus 278: }
1.86 djm 279:
1.107 ! djm 280: if ((buffer = sshbuf_new()) == NULL) {
! 281: r = SSH_ERR_ALLOC_FAIL;
! 282: goto out;
1.86 djm 283: }
1.107 ! djm 284: if ((r = sshkey_load_file(fd, filename, buffer)) != 0 ||
! 285: (r = sshkey_parse_private_fileblob(buffer, passphrase, filename,
! 286: keyp, commentp)) != 0)
! 287: goto out;
! 288: r = 0;
! 289: out:
1.86 djm 290: close(fd);
1.107 ! djm 291: if (buffer != NULL)
! 292: sshbuf_free(buffer);
! 293: return r;
1.18 markus 294: }
295:
1.37 itojun 296: static int
1.107 ! djm 297: sshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp)
1.18 markus 298: {
299: FILE *f;
1.59 dtucker 300: char line[SSH_MAX_PUBKEY_BYTES];
1.18 markus 301: char *cp;
1.60 dtucker 302: u_long linenum = 0;
1.107 ! djm 303: int r;
1.18 markus 304:
1.107 ! djm 305: if (commentp != NULL)
! 306: *commentp = NULL;
! 307: if ((f = fopen(filename, "r")) == NULL)
! 308: return SSH_ERR_SYSTEM_ERROR;
! 309: while (read_keyfile_line(f, filename, line, sizeof(line),
! 310: &linenum) != -1) {
! 311: cp = line;
! 312: switch (*cp) {
! 313: case '#':
! 314: case '\n':
! 315: case '\0':
! 316: continue;
! 317: }
! 318: /* Abort loading if this looks like a private key */
! 319: if (strncmp(cp, "-----BEGIN", 10) == 0 ||
! 320: strcmp(cp, "SSH PRIVATE KEY FILE") == 0)
! 321: break;
! 322: /* Skip leading whitespace. */
! 323: for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
! 324: ;
! 325: if (*cp) {
! 326: if ((r = sshkey_read(k, &cp)) == 0) {
! 327: cp[strcspn(cp, "\r\n")] = '\0';
! 328: if (commentp) {
! 329: *commentp = strdup(*cp ?
! 330: cp : filename);
! 331: if (*commentp == NULL)
! 332: r = SSH_ERR_ALLOC_FAIL;
1.18 markus 333: }
1.107 ! djm 334: fclose(f);
! 335: return r;
1.18 markus 336: }
337: }
338: }
1.107 ! djm 339: fclose(f);
! 340: return SSH_ERR_INVALID_FORMAT;
1.18 markus 341: }
342:
1.29 markus 343: /* load public key from ssh v1 private or any pubkey file */
1.107 ! djm 344: int
! 345: sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp)
1.18 markus 346: {
1.107 ! djm 347: struct sshkey *pub = NULL;
1.29 markus 348: char file[MAXPATHLEN];
1.107 ! djm 349: int r, fd;
1.18 markus 350:
1.107 ! djm 351: if (keyp != NULL)
! 352: *keyp = NULL;
! 353: if (commentp != NULL)
! 354: *commentp = NULL;
! 355:
! 356: if ((fd = open(filename, O_RDONLY)) < 0)
! 357: goto skip;
1.106 markus 358: #ifdef WITH_SSH1
1.53 markus 359: /* try rsa1 private key */
1.107 ! djm 360: r = sshkey_load_public_rsa1(fd, filename, keyp, commentp);
! 361: close(fd);
! 362: switch (r) {
! 363: case SSH_ERR_INTERNAL_ERROR:
! 364: case SSH_ERR_ALLOC_FAIL:
! 365: case SSH_ERR_INVALID_ARGUMENT:
! 366: case SSH_ERR_SYSTEM_ERROR:
! 367: case 0:
! 368: return r;
! 369: }
! 370: #endif /* WITH_SSH1 */
! 371:
! 372: /* try ssh2 public key */
! 373: if ((pub = sshkey_new(KEY_UNSPEC)) == NULL)
! 374: return SSH_ERR_ALLOC_FAIL;
! 375: if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) {
! 376: if (keyp != NULL)
! 377: *keyp = pub;
! 378: return 0;
! 379: }
! 380: sshkey_free(pub);
1.53 markus 381:
1.107 ! djm 382: #ifdef WITH_SSH1
1.53 markus 383: /* try rsa1 public key */
1.107 ! djm 384: if ((pub = sshkey_new(KEY_RSA1)) == NULL)
! 385: return SSH_ERR_ALLOC_FAIL;
! 386: if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) {
! 387: if (keyp != NULL)
! 388: *keyp = pub;
! 389: return 0;
! 390: }
! 391: sshkey_free(pub);
! 392: #endif /* WITH_SSH1 */
1.53 markus 393:
1.107 ! djm 394: skip:
! 395: /* try .pub suffix */
! 396: if ((pub = sshkey_new(KEY_UNSPEC)) == NULL)
! 397: return SSH_ERR_ALLOC_FAIL;
! 398: r = SSH_ERR_ALLOC_FAIL; /* in case strlcpy or strlcat fail */
1.29 markus 399: if ((strlcpy(file, filename, sizeof file) < sizeof(file)) &&
400: (strlcat(file, ".pub", sizeof file) < sizeof(file)) &&
1.107 ! djm 401: (r = sshkey_try_load_public(pub, file, commentp)) == 0) {
! 402: if (keyp != NULL)
! 403: *keyp = pub;
! 404: return 0;
! 405: }
! 406: sshkey_free(pub);
! 407: return r;
1.81 djm 408: }
409:
410: /* Load the certificate associated with the named private key */
1.107 ! djm 411: int
! 412: sshkey_load_cert(const char *filename, struct sshkey **keyp)
1.81 djm 413: {
1.107 ! djm 414: struct sshkey *pub = NULL;
! 415: char *file = NULL;
! 416: int r = SSH_ERR_INTERNAL_ERROR;
! 417:
! 418: *keyp = NULL;
1.81 djm 419:
1.107 ! djm 420: if (asprintf(&file, "%s-cert.pub", filename) == -1)
! 421: return SSH_ERR_ALLOC_FAIL;
! 422:
! 423: if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
! 424: goto out;
! 425: }
! 426: if ((r = sshkey_try_load_public(pub, file, NULL)) != 0)
! 427: goto out;
! 428:
! 429: *keyp = pub;
! 430: pub = NULL;
! 431: r = 0;
! 432:
! 433: out:
! 434: if (file != NULL)
1.97 djm 435: free(file);
1.107 ! djm 436: if (pub != NULL)
! 437: sshkey_free(pub);
! 438: return r;
1.81 djm 439: }
440:
441: /* Load private key and certificate */
1.107 ! djm 442: int
! 443: sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
! 444: struct sshkey **keyp, int *perm_ok)
1.81 djm 445: {
1.107 ! djm 446: struct sshkey *key = NULL, *cert = NULL;
! 447: int r;
! 448:
! 449: *keyp = NULL;
1.81 djm 450:
451: switch (type) {
1.106 markus 452: #ifdef WITH_OPENSSL
1.81 djm 453: case KEY_RSA:
454: case KEY_DSA:
1.83 djm 455: case KEY_ECDSA:
1.101 djm 456: case KEY_ED25519:
1.107 ! djm 457: #endif /* WITH_OPENSSL */
! 458: case KEY_UNSPEC:
1.81 djm 459: break;
460: default:
1.107 ! djm 461: return SSH_ERR_KEY_TYPE_UNKNOWN;
1.81 djm 462: }
463:
1.107 ! djm 464: if ((r = sshkey_load_private_type(type, filename,
! 465: passphrase, &key, NULL, perm_ok)) != 0 ||
! 466: (r = sshkey_load_cert(filename, &cert)) != 0)
! 467: goto out;
1.81 djm 468:
469: /* Make sure the private key matches the certificate */
1.107 ! djm 470: if (sshkey_equal_public(key, cert) == 0) {
! 471: r = SSH_ERR_KEY_CERT_MISMATCH;
! 472: goto out;
1.81 djm 473: }
474:
1.107 ! djm 475: if ((r = sshkey_to_certified(key, sshkey_cert_is_legacy(cert))) != 0 ||
! 476: (r = sshkey_cert_copy(cert, key)) != 0)
! 477: goto out;
! 478: r = 0;
! 479: *keyp = key;
! 480: key = NULL;
! 481: out:
! 482: if (key != NULL)
! 483: sshkey_free(key);
! 484: if (cert != NULL)
! 485: sshkey_free(cert);
! 486: return r;
1.1 deraadt 487: }
1.80 djm 488:
489: /*
1.107 ! djm 490: * Returns success if the specified "key" is listed in the file "filename",
! 491: * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
1.80 djm 492: * If strict_type is set then the key type must match exactly,
493: * otherwise a comparison that ignores certficiate data is performed.
494: */
495: int
1.107 ! djm 496: sshkey_in_file(struct sshkey *key, const char *filename, int strict_type)
1.80 djm 497: {
498: FILE *f;
499: char line[SSH_MAX_PUBKEY_BYTES];
500: char *cp;
501: u_long linenum = 0;
1.107 ! djm 502: int r = 0;
! 503: struct sshkey *pub = NULL;
! 504: int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
! 505: strict_type ? sshkey_equal : sshkey_equal_public;
1.80 djm 506:
507: if ((f = fopen(filename, "r")) == NULL) {
1.107 ! djm 508: if (errno == ENOENT)
! 509: return SSH_ERR_KEY_NOT_FOUND;
! 510: else
! 511: return SSH_ERR_SYSTEM_ERROR;
1.80 djm 512: }
513:
514: while (read_keyfile_line(f, filename, line, sizeof(line),
1.107 ! djm 515: &linenum) != -1) {
1.80 djm 516: cp = line;
517:
518: /* Skip leading whitespace. */
519: for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
520: ;
521:
522: /* Skip comments and empty lines */
523: switch (*cp) {
524: case '#':
525: case '\n':
526: case '\0':
527: continue;
528: }
529:
1.107 ! djm 530: if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
! 531: r = SSH_ERR_ALLOC_FAIL;
! 532: goto out;
1.80 djm 533: }
1.107 ! djm 534: if ((r = sshkey_read(pub, &cp)) != 0)
! 535: goto out;
! 536: if (sshkey_compare(key, pub)) {
! 537: r = 0;
! 538: goto out;
1.80 djm 539: }
1.107 ! djm 540: sshkey_free(pub);
! 541: pub = NULL;
1.80 djm 542: }
1.107 ! djm 543: r = SSH_ERR_KEY_NOT_FOUND;
! 544: out:
! 545: if (pub != NULL)
! 546: sshkey_free(pub);
1.80 djm 547: fclose(f);
1.107 ! djm 548: return r;
1.80 djm 549: }
1.107 ! djm 550: