Annotation of src/usr.bin/ssh/authfile.c, Revision 1.128
1.128 ! markus 1: /* $OpenBSD: authfile.c,v 1.127 2017/07/01 13:50:45 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.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.109 djm 95: sshkey_load_file(int fd, 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.126 deraadt 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.1 deraadt 135:
1.107 djm 136: /* XXX remove error() calls from here? */
1.63 dtucker 137: int
1.107 djm 138: sshkey_perm_ok(int fd, const char *filename)
1.15 markus 139: {
140: struct stat st;
141:
1.38 markus 142: if (fstat(fd, &st) < 0)
1.107 djm 143: return SSH_ERR_SYSTEM_ERROR;
1.38 markus 144: /*
145: * if a key owned by the user is accessed, then we check the
146: * permissions of the file. if the key owned by a different user,
147: * then we don't care.
148: */
149: if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
1.15 markus 150: error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
151: error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @");
152: error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1.38 markus 153: error("Permissions 0%3.3o for '%s' are too open.",
1.54 djm 154: (u_int)st.st_mode & 0777, filename);
1.114 djm 155: error("It is required that your private key files are NOT accessible by others.");
1.29 markus 156: error("This private key will be ignored.");
1.107 djm 157: return SSH_ERR_KEY_BAD_PERMISSIONS;
1.15 markus 158: }
1.107 djm 159: return 0;
1.29 markus 160: }
161:
1.107 djm 162: /* XXX kill perm_ok now that we have SSH_ERR_KEY_BAD_PERMISSIONS? */
163: int
164: sshkey_load_private_type(int type, const char *filename, const char *passphrase,
165: struct sshkey **keyp, char **commentp, int *perm_ok)
1.86 djm 166: {
1.107 djm 167: int fd, r;
1.99 markus 168:
1.121 djm 169: if (keyp != NULL)
170: *keyp = NULL;
1.107 djm 171: if (commentp != NULL)
172: *commentp = NULL;
1.86 djm 173:
1.107 djm 174: if ((fd = open(filename, O_RDONLY)) < 0) {
1.78 dtucker 175: if (perm_ok != NULL)
176: *perm_ok = 0;
1.107 djm 177: return SSH_ERR_SYSTEM_ERROR;
1.78 dtucker 178: }
1.107 djm 179: if (sshkey_perm_ok(fd, filename) != 0) {
1.67 dtucker 180: if (perm_ok != NULL)
181: *perm_ok = 0;
1.107 djm 182: r = SSH_ERR_KEY_BAD_PERMISSIONS;
183: goto out;
1.29 markus 184: }
1.67 dtucker 185: if (perm_ok != NULL)
186: *perm_ok = 1;
1.86 djm 187:
1.109 djm 188: r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
1.128 ! markus 189: if (r == 0 && keyp && *keyp)
! 190: r = sshkey_set_filename(*keyp, filename);
1.109 djm 191: out:
192: close(fd);
193: return r;
194: }
195:
196: int
197: sshkey_load_private_type_fd(int fd, int type, const char *passphrase,
198: struct sshkey **keyp, char **commentp)
199: {
200: struct sshbuf *buffer = NULL;
201: int r;
202:
1.121 djm 203: if (keyp != NULL)
204: *keyp = NULL;
1.107 djm 205: if ((buffer = sshbuf_new()) == NULL) {
206: r = SSH_ERR_ALLOC_FAIL;
207: goto out;
1.15 markus 208: }
1.109 djm 209: if ((r = sshkey_load_file(fd, buffer)) != 0 ||
210: (r = sshkey_parse_private_fileblob_type(buffer, type,
211: passphrase, keyp, commentp)) != 0)
1.107 djm 212: goto out;
1.109 djm 213:
214: /* success */
1.107 djm 215: r = 0;
216: out:
1.120 mmcc 217: sshbuf_free(buffer);
1.107 djm 218: return r;
1.29 markus 219: }
220:
1.107 djm 221: /* XXX this is almost identical to sshkey_load_private_type() */
222: int
223: sshkey_load_private(const char *filename, const char *passphrase,
224: struct sshkey **keyp, char **commentp)
1.88 djm 225: {
1.107 djm 226: struct sshbuf *buffer = NULL;
227: int r, fd;
1.88 djm 228:
1.121 djm 229: if (keyp != NULL)
230: *keyp = NULL;
1.107 djm 231: if (commentp != NULL)
232: *commentp = NULL;
1.88 djm 233:
1.107 djm 234: if ((fd = open(filename, O_RDONLY)) < 0)
235: return SSH_ERR_SYSTEM_ERROR;
236: if (sshkey_perm_ok(fd, filename) != 0) {
237: r = SSH_ERR_KEY_BAD_PERMISSIONS;
238: goto out;
1.29 markus 239: }
1.86 djm 240:
1.107 djm 241: if ((buffer = sshbuf_new()) == NULL) {
242: r = SSH_ERR_ALLOC_FAIL;
243: goto out;
1.86 djm 244: }
1.109 djm 245: if ((r = sshkey_load_file(fd, buffer)) != 0 ||
1.117 tim 246: (r = sshkey_parse_private_fileblob(buffer, passphrase, keyp,
247: commentp)) != 0)
1.107 djm 248: goto out;
1.128 ! markus 249: if (keyp && *keyp &&
! 250: (r = sshkey_set_filename(*keyp, filename)) != 0)
! 251: goto out;
1.107 djm 252: r = 0;
253: out:
1.86 djm 254: close(fd);
1.120 mmcc 255: sshbuf_free(buffer);
1.107 djm 256: return r;
1.18 markus 257: }
258:
1.37 itojun 259: static int
1.107 djm 260: sshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp)
1.18 markus 261: {
262: FILE *f;
1.59 dtucker 263: char line[SSH_MAX_PUBKEY_BYTES];
1.18 markus 264: char *cp;
1.60 dtucker 265: u_long linenum = 0;
1.107 djm 266: int r;
1.18 markus 267:
1.107 djm 268: if (commentp != NULL)
269: *commentp = NULL;
270: if ((f = fopen(filename, "r")) == NULL)
271: return SSH_ERR_SYSTEM_ERROR;
272: while (read_keyfile_line(f, filename, line, sizeof(line),
273: &linenum) != -1) {
274: cp = line;
275: switch (*cp) {
276: case '#':
277: case '\n':
278: case '\0':
279: continue;
280: }
281: /* Abort loading if this looks like a private key */
282: if (strncmp(cp, "-----BEGIN", 10) == 0 ||
283: strcmp(cp, "SSH PRIVATE KEY FILE") == 0)
284: break;
285: /* Skip leading whitespace. */
286: for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
287: ;
288: if (*cp) {
289: if ((r = sshkey_read(k, &cp)) == 0) {
290: cp[strcspn(cp, "\r\n")] = '\0';
291: if (commentp) {
292: *commentp = strdup(*cp ?
293: cp : filename);
294: if (*commentp == NULL)
295: r = SSH_ERR_ALLOC_FAIL;
1.18 markus 296: }
1.107 djm 297: fclose(f);
298: return r;
1.18 markus 299: }
300: }
301: }
1.107 djm 302: fclose(f);
303: return SSH_ERR_INVALID_FORMAT;
1.18 markus 304: }
305:
1.125 markus 306: /* load public key from any pubkey file */
1.107 djm 307: int
308: sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp)
1.18 markus 309: {
1.107 djm 310: struct sshkey *pub = NULL;
1.125 markus 311: char *file = NULL;
312: int r;
1.18 markus 313:
1.107 djm 314: if (keyp != NULL)
315: *keyp = NULL;
316: if (commentp != NULL)
317: *commentp = NULL;
318:
319: if ((pub = sshkey_new(KEY_UNSPEC)) == NULL)
320: return SSH_ERR_ALLOC_FAIL;
321: if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) {
1.125 markus 322: if (keyp != NULL) {
1.107 djm 323: *keyp = pub;
1.125 markus 324: pub = NULL;
325: }
326: r = 0;
327: goto out;
1.107 djm 328: }
329: sshkey_free(pub);
1.53 markus 330:
1.107 djm 331: /* try .pub suffix */
1.125 markus 332: if (asprintf(&file, "%s.pub", filename) == -1)
1.107 djm 333: return SSH_ERR_ALLOC_FAIL;
1.125 markus 334: if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
335: r = SSH_ERR_ALLOC_FAIL;
336: goto out;
337: }
338: if ((r = sshkey_try_load_public(pub, file, commentp)) == 0) {
339: if (keyp != NULL) {
1.107 djm 340: *keyp = pub;
1.125 markus 341: pub = NULL;
342: }
343: r = 0;
1.107 djm 344: }
1.125 markus 345: out:
346: free(file);
1.107 djm 347: sshkey_free(pub);
348: return r;
1.81 djm 349: }
350:
351: /* Load the certificate associated with the named private key */
1.107 djm 352: int
353: sshkey_load_cert(const char *filename, struct sshkey **keyp)
1.81 djm 354: {
1.107 djm 355: struct sshkey *pub = NULL;
356: char *file = NULL;
357: int r = SSH_ERR_INTERNAL_ERROR;
358:
1.121 djm 359: if (keyp != NULL)
360: *keyp = NULL;
1.81 djm 361:
1.107 djm 362: if (asprintf(&file, "%s-cert.pub", filename) == -1)
363: return SSH_ERR_ALLOC_FAIL;
364:
365: if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
366: goto out;
367: }
368: if ((r = sshkey_try_load_public(pub, file, NULL)) != 0)
369: goto out;
1.121 djm 370: /* success */
371: if (keyp != NULL) {
372: *keyp = pub;
373: pub = NULL;
374: }
1.107 djm 375: r = 0;
376: out:
1.118 mmcc 377: free(file);
1.119 mmcc 378: sshkey_free(pub);
1.107 djm 379: return r;
1.81 djm 380: }
381:
382: /* Load private key and certificate */
1.107 djm 383: int
384: sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
385: struct sshkey **keyp, int *perm_ok)
1.81 djm 386: {
1.107 djm 387: struct sshkey *key = NULL, *cert = NULL;
388: int r;
389:
1.121 djm 390: if (keyp != NULL)
391: *keyp = NULL;
1.81 djm 392:
393: switch (type) {
1.106 markus 394: #ifdef WITH_OPENSSL
1.81 djm 395: case KEY_RSA:
396: case KEY_DSA:
1.83 djm 397: case KEY_ECDSA:
1.116 markus 398: #endif /* WITH_OPENSSL */
1.101 djm 399: case KEY_ED25519:
1.128 ! markus 400: case KEY_XMSS:
1.107 djm 401: case KEY_UNSPEC:
1.81 djm 402: break;
403: default:
1.107 djm 404: return SSH_ERR_KEY_TYPE_UNKNOWN;
1.81 djm 405: }
406:
1.107 djm 407: if ((r = sshkey_load_private_type(type, filename,
408: passphrase, &key, NULL, perm_ok)) != 0 ||
409: (r = sshkey_load_cert(filename, &cert)) != 0)
410: goto out;
1.81 djm 411:
412: /* Make sure the private key matches the certificate */
1.107 djm 413: if (sshkey_equal_public(key, cert) == 0) {
414: r = SSH_ERR_KEY_CERT_MISMATCH;
415: goto out;
1.81 djm 416: }
417:
1.115 djm 418: if ((r = sshkey_to_certified(key)) != 0 ||
1.107 djm 419: (r = sshkey_cert_copy(cert, key)) != 0)
420: goto out;
421: r = 0;
1.121 djm 422: if (keyp != NULL) {
423: *keyp = key;
424: key = NULL;
425: }
1.107 djm 426: out:
1.119 mmcc 427: sshkey_free(key);
428: sshkey_free(cert);
1.107 djm 429: return r;
1.1 deraadt 430: }
1.80 djm 431:
432: /*
1.107 djm 433: * Returns success if the specified "key" is listed in the file "filename",
434: * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
1.108 djm 435: * If "strict_type" is set then the key type must match exactly,
1.80 djm 436: * otherwise a comparison that ignores certficiate data is performed.
1.108 djm 437: * If "check_ca" is set and "key" is a certificate, then its CA key is
438: * also checked and sshkey_in_file() will return success if either is found.
1.80 djm 439: */
440: int
1.108 djm 441: sshkey_in_file(struct sshkey *key, const char *filename, int strict_type,
442: int check_ca)
1.80 djm 443: {
444: FILE *f;
445: char line[SSH_MAX_PUBKEY_BYTES];
446: char *cp;
447: u_long linenum = 0;
1.107 djm 448: int r = 0;
449: struct sshkey *pub = NULL;
450: int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
451: strict_type ? sshkey_equal : sshkey_equal_public;
1.80 djm 452:
1.108 djm 453: if ((f = fopen(filename, "r")) == NULL)
454: return SSH_ERR_SYSTEM_ERROR;
1.80 djm 455:
456: while (read_keyfile_line(f, filename, line, sizeof(line),
1.107 djm 457: &linenum) != -1) {
1.80 djm 458: cp = line;
459:
460: /* Skip leading whitespace. */
461: for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
462: ;
463:
464: /* Skip comments and empty lines */
465: switch (*cp) {
466: case '#':
467: case '\n':
468: case '\0':
469: continue;
470: }
471:
1.107 djm 472: if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
473: r = SSH_ERR_ALLOC_FAIL;
474: goto out;
1.80 djm 475: }
1.107 djm 476: if ((r = sshkey_read(pub, &cp)) != 0)
477: goto out;
1.108 djm 478: if (sshkey_compare(key, pub) ||
479: (check_ca && sshkey_is_cert(key) &&
480: sshkey_compare(key->cert->signature_key, pub))) {
1.107 djm 481: r = 0;
482: goto out;
1.80 djm 483: }
1.107 djm 484: sshkey_free(pub);
485: pub = NULL;
1.80 djm 486: }
1.107 djm 487: r = SSH_ERR_KEY_NOT_FOUND;
488: out:
1.119 mmcc 489: sshkey_free(pub);
1.80 djm 490: fclose(f);
1.107 djm 491: return r;
1.108 djm 492: }
493:
494: /*
495: * Checks whether the specified key is revoked, returning 0 if not,
496: * SSH_ERR_KEY_REVOKED if it is or another error code if something
497: * unexpected happened.
498: * This will check both the key and, if it is a certificate, its CA key too.
499: * "revoked_keys_file" may be a KRL or a one-per-line list of public keys.
500: */
501: int
502: sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file)
503: {
504: int r;
505:
506: r = ssh_krl_file_contains_key(revoked_keys_file, key);
507: /* If this was not a KRL to begin with then continue below */
508: if (r != SSH_ERR_KRL_BAD_MAGIC)
509: return r;
510:
511: /*
512: * If the file is not a KRL or we can't handle KRLs then attempt to
513: * parse the file as a flat list of keys.
514: */
515: switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) {
516: case 0:
517: /* Key found => revoked */
518: return SSH_ERR_KEY_REVOKED;
519: case SSH_ERR_KEY_NOT_FOUND:
520: /* Key not found => not revoked */
521: return 0;
522: default:
523: /* Some other error occurred */
524: return r;
525: }
1.80 djm 526: }
1.107 djm 527: