1*076ad2f8SDag-Erling Smørgrav /* $OpenBSD: authfile.c,v 1.121 2016/04/09 12:39:30 djm Exp $ */ 2511b41d2SMark Murray /* 3f7167e0eSDag-Erling Smørgrav * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved. 4c2d3a559SKris Kennaway * 5c2d3a559SKris Kennaway * Redistribution and use in source and binary forms, with or without 6c2d3a559SKris Kennaway * modification, are permitted provided that the following conditions 7c2d3a559SKris Kennaway * are met: 8c2d3a559SKris Kennaway * 1. Redistributions of source code must retain the above copyright 9c2d3a559SKris Kennaway * notice, this list of conditions and the following disclaimer. 10c2d3a559SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 11c2d3a559SKris Kennaway * notice, this list of conditions and the following disclaimer in the 12c2d3a559SKris Kennaway * documentation and/or other materials provided with the distribution. 13c2d3a559SKris Kennaway * 14c2d3a559SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15c2d3a559SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16c2d3a559SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17c2d3a559SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18c2d3a559SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19c2d3a559SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20c2d3a559SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21c2d3a559SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22c2d3a559SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23c2d3a559SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24511b41d2SMark Murray */ 25511b41d2SMark Murray 26511b41d2SMark Murray #include "includes.h" 27333ee039SDag-Erling Smørgrav 28333ee039SDag-Erling Smørgrav #include <sys/types.h> 29333ee039SDag-Erling Smørgrav #include <sys/stat.h> 30333ee039SDag-Erling Smørgrav #include <sys/uio.h> 31511b41d2SMark Murray 32333ee039SDag-Erling Smørgrav #include <errno.h> 33333ee039SDag-Erling Smørgrav #include <fcntl.h> 34333ee039SDag-Erling Smørgrav #include <stdio.h> 35a0ee8cc6SDag-Erling Smørgrav #include <stdarg.h> 36333ee039SDag-Erling Smørgrav #include <stdlib.h> 37333ee039SDag-Erling Smørgrav #include <string.h> 38333ee039SDag-Erling Smørgrav #include <unistd.h> 39bc5531deSDag-Erling Smørgrav #include <limits.h> 40333ee039SDag-Erling Smørgrav 41333ee039SDag-Erling Smørgrav #include "cipher.h" 42ca3176e7SBrian Feldman #include "ssh.h" 43ca3176e7SBrian Feldman #include "log.h" 44ca3176e7SBrian Feldman #include "authfile.h" 45af12a3e7SDag-Erling Smørgrav #include "rsa.h" 46aa49c926SDag-Erling Smørgrav #include "misc.h" 47d4ecd108SDag-Erling Smørgrav #include "atomicio.h" 48eccfee6eSDag-Erling Smørgrav #include "sshkey.h" 49a0ee8cc6SDag-Erling Smørgrav #include "sshbuf.h" 50a0ee8cc6SDag-Erling Smørgrav #include "ssherr.h" 51bc5531deSDag-Erling Smørgrav #include "krl.h" 52511b41d2SMark Murray 53e146993eSDag-Erling Smørgrav #define MAX_KEY_FILE_SIZE (1024 * 1024) 54e146993eSDag-Erling Smørgrav 554a421b63SDag-Erling Smørgrav /* Save a key blob to a file */ 564a421b63SDag-Erling Smørgrav static int 57a0ee8cc6SDag-Erling Smørgrav sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename) 584a421b63SDag-Erling Smørgrav { 59a0ee8cc6SDag-Erling Smørgrav int fd, oerrno; 604a421b63SDag-Erling Smørgrav 61a0ee8cc6SDag-Erling Smørgrav if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) 62a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 63a0ee8cc6SDag-Erling Smørgrav if (atomicio(vwrite, fd, (u_char *)sshbuf_ptr(keybuf), 64a0ee8cc6SDag-Erling Smørgrav sshbuf_len(keybuf)) != sshbuf_len(keybuf)) { 65a0ee8cc6SDag-Erling Smørgrav oerrno = errno; 664a421b63SDag-Erling Smørgrav close(fd); 674a421b63SDag-Erling Smørgrav unlink(filename); 68a0ee8cc6SDag-Erling Smørgrav errno = oerrno; 69a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 704a421b63SDag-Erling Smørgrav } 714a421b63SDag-Erling Smørgrav close(fd); 724a421b63SDag-Erling Smørgrav return 0; 734a421b63SDag-Erling Smørgrav } 744a421b63SDag-Erling Smørgrav 75e8aafc91SKris Kennaway int 76a0ee8cc6SDag-Erling Smørgrav sshkey_save_private(struct sshkey *key, const char *filename, 77a0ee8cc6SDag-Erling Smørgrav const char *passphrase, const char *comment, 78a0ee8cc6SDag-Erling Smørgrav int force_new_format, const char *new_format_cipher, int new_format_rounds) 79e8aafc91SKris Kennaway { 80a0ee8cc6SDag-Erling Smørgrav struct sshbuf *keyblob = NULL; 81a0ee8cc6SDag-Erling Smørgrav int r; 824a421b63SDag-Erling Smørgrav 83a0ee8cc6SDag-Erling Smørgrav if ((keyblob = sshbuf_new()) == NULL) 84a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 85a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment, 86a0ee8cc6SDag-Erling Smørgrav force_new_format, new_format_cipher, new_format_rounds)) != 0) 874a421b63SDag-Erling Smørgrav goto out; 88a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_save_private_blob(keyblob, filename)) != 0) 894a421b63SDag-Erling Smørgrav goto out; 90a0ee8cc6SDag-Erling Smørgrav r = 0; 914a421b63SDag-Erling Smørgrav out: 92a0ee8cc6SDag-Erling Smørgrav sshbuf_free(keyblob); 93a0ee8cc6SDag-Erling Smørgrav return r; 944a421b63SDag-Erling Smørgrav } 954a421b63SDag-Erling Smørgrav 96e146993eSDag-Erling Smørgrav /* Load a key from a fd into a buffer */ 97e146993eSDag-Erling Smørgrav int 98bc5531deSDag-Erling Smørgrav sshkey_load_file(int fd, struct sshbuf *blob) 994a421b63SDag-Erling Smørgrav { 100e146993eSDag-Erling Smørgrav u_char buf[1024]; 1014a421b63SDag-Erling Smørgrav size_t len; 1024a421b63SDag-Erling Smørgrav struct stat st; 103a0ee8cc6SDag-Erling Smørgrav int r; 1044a421b63SDag-Erling Smørgrav 105a0ee8cc6SDag-Erling Smørgrav if (fstat(fd, &st) < 0) 106a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 107e146993eSDag-Erling Smørgrav if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && 108a0ee8cc6SDag-Erling Smørgrav st.st_size > MAX_KEY_FILE_SIZE) 109a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_INVALID_FORMAT; 110e146993eSDag-Erling Smørgrav for (;;) { 111e146993eSDag-Erling Smørgrav if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) { 112e146993eSDag-Erling Smørgrav if (errno == EPIPE) 113e146993eSDag-Erling Smørgrav break; 114a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_SYSTEM_ERROR; 115a0ee8cc6SDag-Erling Smørgrav goto out; 1164a421b63SDag-Erling Smørgrav } 117a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_put(blob, buf, len)) != 0) 118a0ee8cc6SDag-Erling Smørgrav goto out; 119a0ee8cc6SDag-Erling Smørgrav if (sshbuf_len(blob) > MAX_KEY_FILE_SIZE) { 120a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 121a0ee8cc6SDag-Erling Smørgrav goto out; 122e146993eSDag-Erling Smørgrav } 123e146993eSDag-Erling Smørgrav } 124e146993eSDag-Erling Smørgrav if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && 125a0ee8cc6SDag-Erling Smørgrav st.st_size != (off_t)sshbuf_len(blob)) { 126a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_FILE_CHANGED; 127a0ee8cc6SDag-Erling Smørgrav goto out; 128a0ee8cc6SDag-Erling Smørgrav } 129a0ee8cc6SDag-Erling Smørgrav r = 0; 130a0ee8cc6SDag-Erling Smørgrav 131a0ee8cc6SDag-Erling Smørgrav out: 132a0ee8cc6SDag-Erling Smørgrav explicit_bzero(buf, sizeof(buf)); 133a0ee8cc6SDag-Erling Smørgrav if (r != 0) 134a0ee8cc6SDag-Erling Smørgrav sshbuf_reset(blob); 135a0ee8cc6SDag-Erling Smørgrav return r; 136e146993eSDag-Erling Smørgrav } 137e146993eSDag-Erling Smørgrav 138a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_SSH1 139511b41d2SMark Murray /* 140ca3176e7SBrian Feldman * Loads the public part of the ssh v1 key file. Returns NULL if an error was 141ca3176e7SBrian Feldman * encountered (the file does not exist or is not readable), and the key 142511b41d2SMark Murray * otherwise. 143511b41d2SMark Murray */ 144a0ee8cc6SDag-Erling Smørgrav static int 145bc5531deSDag-Erling Smørgrav sshkey_load_public_rsa1(int fd, struct sshkey **keyp, char **commentp) 146511b41d2SMark Murray { 147a0ee8cc6SDag-Erling Smørgrav struct sshbuf *b = NULL; 148a0ee8cc6SDag-Erling Smørgrav int r; 149511b41d2SMark Murray 150*076ad2f8SDag-Erling Smørgrav if (keyp != NULL) 151a0ee8cc6SDag-Erling Smørgrav *keyp = NULL; 152e4a9863fSDag-Erling Smørgrav if (commentp != NULL) 153a0ee8cc6SDag-Erling Smørgrav *commentp = NULL; 154e8aafc91SKris Kennaway 155a0ee8cc6SDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) 156a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 157bc5531deSDag-Erling Smørgrav if ((r = sshkey_load_file(fd, b)) != 0) 158a0ee8cc6SDag-Erling Smørgrav goto out; 159a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_parse_public_rsa1_fileblob(b, keyp, commentp)) != 0) 160a0ee8cc6SDag-Erling Smørgrav goto out; 161a0ee8cc6SDag-Erling Smørgrav r = 0; 162a0ee8cc6SDag-Erling Smørgrav out: 163a0ee8cc6SDag-Erling Smørgrav sshbuf_free(b); 164a0ee8cc6SDag-Erling Smørgrav return r; 165a0ee8cc6SDag-Erling Smørgrav } 166a0ee8cc6SDag-Erling Smørgrav #endif /* WITH_SSH1 */ 167e8aafc91SKris Kennaway 168a0ee8cc6SDag-Erling Smørgrav /* XXX remove error() calls from here? */ 169a0ee8cc6SDag-Erling Smørgrav int 170a0ee8cc6SDag-Erling Smørgrav sshkey_perm_ok(int fd, const char *filename) 171e8aafc91SKris Kennaway { 172e8aafc91SKris Kennaway struct stat st; 173e8aafc91SKris Kennaway 174af12a3e7SDag-Erling Smørgrav if (fstat(fd, &st) < 0) 175a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 176af12a3e7SDag-Erling Smørgrav /* 177af12a3e7SDag-Erling Smørgrav * if a key owned by the user is accessed, then we check the 178af12a3e7SDag-Erling Smørgrav * permissions of the file. if the key owned by a different user, 179af12a3e7SDag-Erling Smørgrav * then we don't care. 180af12a3e7SDag-Erling Smørgrav */ 181989dd127SDag-Erling Smørgrav #ifdef HAVE_CYGWIN 182989dd127SDag-Erling Smørgrav if (check_ntsec(filename)) 183989dd127SDag-Erling Smørgrav #endif 184af12a3e7SDag-Erling Smørgrav if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) { 185e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 186e8aafc91SKris Kennaway error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); 187e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 188af12a3e7SDag-Erling Smørgrav error("Permissions 0%3.3o for '%s' are too open.", 189cf2b5f3bSDag-Erling Smørgrav (u_int)st.st_mode & 0777, filename); 190557f75e5SDag-Erling Smørgrav error("It is required that your private key files are NOT accessible by others."); 191ca3176e7SBrian Feldman error("This private key will be ignored."); 192a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_KEY_BAD_PERMISSIONS; 193a0ee8cc6SDag-Erling Smørgrav } 194e8aafc91SKris Kennaway return 0; 195e8aafc91SKris Kennaway } 196ca3176e7SBrian Feldman 197a0ee8cc6SDag-Erling Smørgrav /* XXX kill perm_ok now that we have SSH_ERR_KEY_BAD_PERMISSIONS? */ 198a0ee8cc6SDag-Erling Smørgrav int 199a0ee8cc6SDag-Erling Smørgrav sshkey_load_private_type(int type, const char *filename, const char *passphrase, 200a0ee8cc6SDag-Erling Smørgrav struct sshkey **keyp, char **commentp, int *perm_ok) 2014a421b63SDag-Erling Smørgrav { 202a0ee8cc6SDag-Erling Smørgrav int fd, r; 203f7167e0eSDag-Erling Smørgrav 204*076ad2f8SDag-Erling Smørgrav if (keyp != NULL) 205a0ee8cc6SDag-Erling Smørgrav *keyp = NULL; 206a0ee8cc6SDag-Erling Smørgrav if (commentp != NULL) 207a0ee8cc6SDag-Erling Smørgrav *commentp = NULL; 2084a421b63SDag-Erling Smørgrav 209a0ee8cc6SDag-Erling Smørgrav if ((fd = open(filename, O_RDONLY)) < 0) { 210b15c8340SDag-Erling Smørgrav if (perm_ok != NULL) 211b15c8340SDag-Erling Smørgrav *perm_ok = 0; 212a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 213b15c8340SDag-Erling Smørgrav } 214a0ee8cc6SDag-Erling Smørgrav if (sshkey_perm_ok(fd, filename) != 0) { 215333ee039SDag-Erling Smørgrav if (perm_ok != NULL) 216333ee039SDag-Erling Smørgrav *perm_ok = 0; 217a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_KEY_BAD_PERMISSIONS; 218a0ee8cc6SDag-Erling Smørgrav goto out; 219e8aafc91SKris Kennaway } 220333ee039SDag-Erling Smørgrav if (perm_ok != NULL) 221333ee039SDag-Erling Smørgrav *perm_ok = 1; 2224a421b63SDag-Erling Smørgrav 223bc5531deSDag-Erling Smørgrav r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp); 224bc5531deSDag-Erling Smørgrav out: 225bc5531deSDag-Erling Smørgrav close(fd); 226bc5531deSDag-Erling Smørgrav return r; 227bc5531deSDag-Erling Smørgrav } 228bc5531deSDag-Erling Smørgrav 229bc5531deSDag-Erling Smørgrav int 230bc5531deSDag-Erling Smørgrav sshkey_load_private_type_fd(int fd, int type, const char *passphrase, 231bc5531deSDag-Erling Smørgrav struct sshkey **keyp, char **commentp) 232bc5531deSDag-Erling Smørgrav { 233bc5531deSDag-Erling Smørgrav struct sshbuf *buffer = NULL; 234bc5531deSDag-Erling Smørgrav int r; 235bc5531deSDag-Erling Smørgrav 236*076ad2f8SDag-Erling Smørgrav if (keyp != NULL) 237*076ad2f8SDag-Erling Smørgrav *keyp = NULL; 238a0ee8cc6SDag-Erling Smørgrav if ((buffer = sshbuf_new()) == NULL) { 239a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 240a0ee8cc6SDag-Erling Smørgrav goto out; 241ca3176e7SBrian Feldman } 242bc5531deSDag-Erling Smørgrav if ((r = sshkey_load_file(fd, buffer)) != 0 || 243bc5531deSDag-Erling Smørgrav (r = sshkey_parse_private_fileblob_type(buffer, type, 244bc5531deSDag-Erling Smørgrav passphrase, keyp, commentp)) != 0) 245a0ee8cc6SDag-Erling Smørgrav goto out; 246bc5531deSDag-Erling Smørgrav 247bc5531deSDag-Erling Smørgrav /* success */ 248a0ee8cc6SDag-Erling Smørgrav r = 0; 249a0ee8cc6SDag-Erling Smørgrav out: 250a0ee8cc6SDag-Erling Smørgrav sshbuf_free(buffer); 251a0ee8cc6SDag-Erling Smørgrav return r; 2524a421b63SDag-Erling Smørgrav } 253ca3176e7SBrian Feldman 254a0ee8cc6SDag-Erling Smørgrav /* XXX this is almost identical to sshkey_load_private_type() */ 255a0ee8cc6SDag-Erling Smørgrav int 256a0ee8cc6SDag-Erling Smørgrav sshkey_load_private(const char *filename, const char *passphrase, 257a0ee8cc6SDag-Erling Smørgrav struct sshkey **keyp, char **commentp) 258e146993eSDag-Erling Smørgrav { 259a0ee8cc6SDag-Erling Smørgrav struct sshbuf *buffer = NULL; 260a0ee8cc6SDag-Erling Smørgrav int r, fd; 261e146993eSDag-Erling Smørgrav 262*076ad2f8SDag-Erling Smørgrav if (keyp != NULL) 263a0ee8cc6SDag-Erling Smørgrav *keyp = NULL; 264a0ee8cc6SDag-Erling Smørgrav if (commentp != NULL) 265a0ee8cc6SDag-Erling Smørgrav *commentp = NULL; 266a0ee8cc6SDag-Erling Smørgrav 267a0ee8cc6SDag-Erling Smørgrav if ((fd = open(filename, O_RDONLY)) < 0) 268a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 269a0ee8cc6SDag-Erling Smørgrav if (sshkey_perm_ok(fd, filename) != 0) { 270a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_KEY_BAD_PERMISSIONS; 271a0ee8cc6SDag-Erling Smørgrav goto out; 272e146993eSDag-Erling Smørgrav } 273e146993eSDag-Erling Smørgrav 274a0ee8cc6SDag-Erling Smørgrav if ((buffer = sshbuf_new()) == NULL) { 275a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 276a0ee8cc6SDag-Erling Smørgrav goto out; 277b15c8340SDag-Erling Smørgrav } 278bc5531deSDag-Erling Smørgrav if ((r = sshkey_load_file(fd, buffer)) != 0 || 279acc1a9efSDag-Erling Smørgrav (r = sshkey_parse_private_fileblob(buffer, passphrase, keyp, 280acc1a9efSDag-Erling Smørgrav commentp)) != 0) 281a0ee8cc6SDag-Erling Smørgrav goto out; 282a0ee8cc6SDag-Erling Smørgrav r = 0; 283a0ee8cc6SDag-Erling Smørgrav out: 284e8aafc91SKris Kennaway close(fd); 285a0ee8cc6SDag-Erling Smørgrav sshbuf_free(buffer); 286a0ee8cc6SDag-Erling Smørgrav return r; 287e8aafc91SKris Kennaway } 288c2d3a559SKris Kennaway 289af12a3e7SDag-Erling Smørgrav static int 290a0ee8cc6SDag-Erling Smørgrav sshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp) 291c2d3a559SKris Kennaway { 292c2d3a559SKris Kennaway FILE *f; 293aa49c926SDag-Erling Smørgrav char line[SSH_MAX_PUBKEY_BYTES]; 294c2d3a559SKris Kennaway char *cp; 295aa49c926SDag-Erling Smørgrav u_long linenum = 0; 296a0ee8cc6SDag-Erling Smørgrav int r; 297c2d3a559SKris Kennaway 298a0ee8cc6SDag-Erling Smørgrav if (commentp != NULL) 299a0ee8cc6SDag-Erling Smørgrav *commentp = NULL; 300a0ee8cc6SDag-Erling Smørgrav if ((f = fopen(filename, "r")) == NULL) 301a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 302aa49c926SDag-Erling Smørgrav while (read_keyfile_line(f, filename, line, sizeof(line), 303aa49c926SDag-Erling Smørgrav &linenum) != -1) { 304c2d3a559SKris Kennaway cp = line; 305c2d3a559SKris Kennaway switch (*cp) { 306c2d3a559SKris Kennaway case '#': 307c2d3a559SKris Kennaway case '\n': 308c2d3a559SKris Kennaway case '\0': 309c2d3a559SKris Kennaway continue; 310c2d3a559SKris Kennaway } 311e146993eSDag-Erling Smørgrav /* Abort loading if this looks like a private key */ 312a0ee8cc6SDag-Erling Smørgrav if (strncmp(cp, "-----BEGIN", 10) == 0 || 313a0ee8cc6SDag-Erling Smørgrav strcmp(cp, "SSH PRIVATE KEY FILE") == 0) 314e146993eSDag-Erling Smørgrav break; 315c2d3a559SKris Kennaway /* Skip leading whitespace. */ 316c2d3a559SKris Kennaway for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) 317c2d3a559SKris Kennaway ; 318c2d3a559SKris Kennaway if (*cp) { 319a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_read(k, &cp)) == 0) { 320e146993eSDag-Erling Smørgrav cp[strcspn(cp, "\r\n")] = '\0'; 321e146993eSDag-Erling Smørgrav if (commentp) { 322a0ee8cc6SDag-Erling Smørgrav *commentp = strdup(*cp ? 323e146993eSDag-Erling Smørgrav cp : filename); 324a0ee8cc6SDag-Erling Smørgrav if (*commentp == NULL) 325a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 326e146993eSDag-Erling Smørgrav } 327c2d3a559SKris Kennaway fclose(f); 328a0ee8cc6SDag-Erling Smørgrav return r; 329c2d3a559SKris Kennaway } 330c2d3a559SKris Kennaway } 331c2d3a559SKris Kennaway } 332c2d3a559SKris Kennaway fclose(f); 333a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_INVALID_FORMAT; 334c2d3a559SKris Kennaway } 335c2d3a559SKris Kennaway 336ca3176e7SBrian Feldman /* load public key from ssh v1 private or any pubkey file */ 337a0ee8cc6SDag-Erling Smørgrav int 338a0ee8cc6SDag-Erling Smørgrav sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp) 339c2d3a559SKris Kennaway { 340a0ee8cc6SDag-Erling Smørgrav struct sshkey *pub = NULL; 341bc5531deSDag-Erling Smørgrav char file[PATH_MAX]; 342a0ee8cc6SDag-Erling Smørgrav int r, fd; 343c2d3a559SKris Kennaway 344a0ee8cc6SDag-Erling Smørgrav if (keyp != NULL) 345a0ee8cc6SDag-Erling Smørgrav *keyp = NULL; 346a0ee8cc6SDag-Erling Smørgrav if (commentp != NULL) 347a0ee8cc6SDag-Erling Smørgrav *commentp = NULL; 348a0ee8cc6SDag-Erling Smørgrav 349bc5531deSDag-Erling Smørgrav /* XXX should load file once and attempt to parse each format */ 350bc5531deSDag-Erling Smørgrav 351a0ee8cc6SDag-Erling Smørgrav if ((fd = open(filename, O_RDONLY)) < 0) 352a0ee8cc6SDag-Erling Smørgrav goto skip; 353a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_SSH1 354cf2b5f3bSDag-Erling Smørgrav /* try rsa1 private key */ 355bc5531deSDag-Erling Smørgrav r = sshkey_load_public_rsa1(fd, keyp, commentp); 356a0ee8cc6SDag-Erling Smørgrav close(fd); 357a0ee8cc6SDag-Erling Smørgrav switch (r) { 358a0ee8cc6SDag-Erling Smørgrav case SSH_ERR_INTERNAL_ERROR: 359a0ee8cc6SDag-Erling Smørgrav case SSH_ERR_ALLOC_FAIL: 360a0ee8cc6SDag-Erling Smørgrav case SSH_ERR_INVALID_ARGUMENT: 361a0ee8cc6SDag-Erling Smørgrav case SSH_ERR_SYSTEM_ERROR: 362a0ee8cc6SDag-Erling Smørgrav case 0: 363a0ee8cc6SDag-Erling Smørgrav return r; 364a0ee8cc6SDag-Erling Smørgrav } 365557f75e5SDag-Erling Smørgrav #else /* WITH_SSH1 */ 366557f75e5SDag-Erling Smørgrav close(fd); 367a0ee8cc6SDag-Erling Smørgrav #endif /* WITH_SSH1 */ 368cf2b5f3bSDag-Erling Smørgrav 369cf2b5f3bSDag-Erling Smørgrav /* try ssh2 public key */ 370a0ee8cc6SDag-Erling Smørgrav if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) 371a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 372a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) { 373a0ee8cc6SDag-Erling Smørgrav if (keyp != NULL) 374a0ee8cc6SDag-Erling Smørgrav *keyp = pub; 375a0ee8cc6SDag-Erling Smørgrav return 0; 376a0ee8cc6SDag-Erling Smørgrav } 377a0ee8cc6SDag-Erling Smørgrav sshkey_free(pub); 378a0ee8cc6SDag-Erling Smørgrav 379a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_SSH1 380a0ee8cc6SDag-Erling Smørgrav /* try rsa1 public key */ 381a0ee8cc6SDag-Erling Smørgrav if ((pub = sshkey_new(KEY_RSA1)) == NULL) 382a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 383a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) { 384a0ee8cc6SDag-Erling Smørgrav if (keyp != NULL) 385a0ee8cc6SDag-Erling Smørgrav *keyp = pub; 386a0ee8cc6SDag-Erling Smørgrav return 0; 387a0ee8cc6SDag-Erling Smørgrav } 388a0ee8cc6SDag-Erling Smørgrav sshkey_free(pub); 389a0ee8cc6SDag-Erling Smørgrav #endif /* WITH_SSH1 */ 390a0ee8cc6SDag-Erling Smørgrav 391a0ee8cc6SDag-Erling Smørgrav skip: 392a0ee8cc6SDag-Erling Smørgrav /* try .pub suffix */ 393a0ee8cc6SDag-Erling Smørgrav if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) 394a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 395a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; /* in case strlcpy or strlcat fail */ 396ca3176e7SBrian Feldman if ((strlcpy(file, filename, sizeof file) < sizeof(file)) && 397ca3176e7SBrian Feldman (strlcat(file, ".pub", sizeof file) < sizeof(file)) && 398a0ee8cc6SDag-Erling Smørgrav (r = sshkey_try_load_public(pub, file, commentp)) == 0) { 399a0ee8cc6SDag-Erling Smørgrav if (keyp != NULL) 400a0ee8cc6SDag-Erling Smørgrav *keyp = pub; 401a0ee8cc6SDag-Erling Smørgrav return 0; 402a0ee8cc6SDag-Erling Smørgrav } 403a0ee8cc6SDag-Erling Smørgrav sshkey_free(pub); 404bc5531deSDag-Erling Smørgrav 405a0ee8cc6SDag-Erling Smørgrav return r; 406c2d3a559SKris Kennaway } 407b15c8340SDag-Erling Smørgrav 408e2f6069cSDag-Erling Smørgrav /* Load the certificate associated with the named private key */ 409a0ee8cc6SDag-Erling Smørgrav int 410a0ee8cc6SDag-Erling Smørgrav sshkey_load_cert(const char *filename, struct sshkey **keyp) 411e2f6069cSDag-Erling Smørgrav { 412a0ee8cc6SDag-Erling Smørgrav struct sshkey *pub = NULL; 413a0ee8cc6SDag-Erling Smørgrav char *file = NULL; 414a0ee8cc6SDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 415e2f6069cSDag-Erling Smørgrav 416*076ad2f8SDag-Erling Smørgrav if (keyp != NULL) 417a0ee8cc6SDag-Erling Smørgrav *keyp = NULL; 418a0ee8cc6SDag-Erling Smørgrav 419a0ee8cc6SDag-Erling Smørgrav if (asprintf(&file, "%s-cert.pub", filename) == -1) 420a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 421a0ee8cc6SDag-Erling Smørgrav 422a0ee8cc6SDag-Erling Smørgrav if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { 423a0ee8cc6SDag-Erling Smørgrav goto out; 424e2f6069cSDag-Erling Smørgrav } 425a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_try_load_public(pub, file, NULL)) != 0) 426a0ee8cc6SDag-Erling Smørgrav goto out; 427*076ad2f8SDag-Erling Smørgrav /* success */ 428*076ad2f8SDag-Erling Smørgrav if (keyp != NULL) { 429a0ee8cc6SDag-Erling Smørgrav *keyp = pub; 430a0ee8cc6SDag-Erling Smørgrav pub = NULL; 431*076ad2f8SDag-Erling Smørgrav } 432a0ee8cc6SDag-Erling Smørgrav r = 0; 433a0ee8cc6SDag-Erling Smørgrav out: 434e4a9863fSDag-Erling Smørgrav free(file); 435a0ee8cc6SDag-Erling Smørgrav sshkey_free(pub); 436a0ee8cc6SDag-Erling Smørgrav return r; 437e2f6069cSDag-Erling Smørgrav } 438e2f6069cSDag-Erling Smørgrav 439e2f6069cSDag-Erling Smørgrav /* Load private key and certificate */ 440a0ee8cc6SDag-Erling Smørgrav int 441a0ee8cc6SDag-Erling Smørgrav sshkey_load_private_cert(int type, const char *filename, const char *passphrase, 442a0ee8cc6SDag-Erling Smørgrav struct sshkey **keyp, int *perm_ok) 443e2f6069cSDag-Erling Smørgrav { 444a0ee8cc6SDag-Erling Smørgrav struct sshkey *key = NULL, *cert = NULL; 445a0ee8cc6SDag-Erling Smørgrav int r; 446a0ee8cc6SDag-Erling Smørgrav 447*076ad2f8SDag-Erling Smørgrav if (keyp != NULL) 448a0ee8cc6SDag-Erling Smørgrav *keyp = NULL; 449e2f6069cSDag-Erling Smørgrav 450e2f6069cSDag-Erling Smørgrav switch (type) { 451a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_OPENSSL 452e2f6069cSDag-Erling Smørgrav case KEY_RSA: 453e2f6069cSDag-Erling Smørgrav case KEY_DSA: 4544a421b63SDag-Erling Smørgrav case KEY_ECDSA: 455a0ee8cc6SDag-Erling Smørgrav #endif /* WITH_OPENSSL */ 456eccfee6eSDag-Erling Smørgrav case KEY_ED25519: 457a0ee8cc6SDag-Erling Smørgrav case KEY_UNSPEC: 458e2f6069cSDag-Erling Smørgrav break; 459e2f6069cSDag-Erling Smørgrav default: 460a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_KEY_TYPE_UNKNOWN; 461e2f6069cSDag-Erling Smørgrav } 462e2f6069cSDag-Erling Smørgrav 463a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_load_private_type(type, filename, 464a0ee8cc6SDag-Erling Smørgrav passphrase, &key, NULL, perm_ok)) != 0 || 465a0ee8cc6SDag-Erling Smørgrav (r = sshkey_load_cert(filename, &cert)) != 0) 466a0ee8cc6SDag-Erling Smørgrav goto out; 467e2f6069cSDag-Erling Smørgrav 468e2f6069cSDag-Erling Smørgrav /* Make sure the private key matches the certificate */ 469a0ee8cc6SDag-Erling Smørgrav if (sshkey_equal_public(key, cert) == 0) { 470a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_KEY_CERT_MISMATCH; 471a0ee8cc6SDag-Erling Smørgrav goto out; 472e2f6069cSDag-Erling Smørgrav } 473e2f6069cSDag-Erling Smørgrav 474eccfee6eSDag-Erling Smørgrav if ((r = sshkey_to_certified(key)) != 0 || 475a0ee8cc6SDag-Erling Smørgrav (r = sshkey_cert_copy(cert, key)) != 0) 476a0ee8cc6SDag-Erling Smørgrav goto out; 477a0ee8cc6SDag-Erling Smørgrav r = 0; 478*076ad2f8SDag-Erling Smørgrav if (keyp != NULL) { 479a0ee8cc6SDag-Erling Smørgrav *keyp = key; 480a0ee8cc6SDag-Erling Smørgrav key = NULL; 481*076ad2f8SDag-Erling Smørgrav } 482a0ee8cc6SDag-Erling Smørgrav out: 483a0ee8cc6SDag-Erling Smørgrav sshkey_free(key); 484a0ee8cc6SDag-Erling Smørgrav sshkey_free(cert); 485a0ee8cc6SDag-Erling Smørgrav return r; 486e2f6069cSDag-Erling Smørgrav } 487e2f6069cSDag-Erling Smørgrav 488b15c8340SDag-Erling Smørgrav /* 489a0ee8cc6SDag-Erling Smørgrav * Returns success if the specified "key" is listed in the file "filename", 490a0ee8cc6SDag-Erling Smørgrav * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error. 491bc5531deSDag-Erling Smørgrav * If "strict_type" is set then the key type must match exactly, 492b15c8340SDag-Erling Smørgrav * otherwise a comparison that ignores certficiate data is performed. 493bc5531deSDag-Erling Smørgrav * If "check_ca" is set and "key" is a certificate, then its CA key is 494bc5531deSDag-Erling Smørgrav * also checked and sshkey_in_file() will return success if either is found. 495b15c8340SDag-Erling Smørgrav */ 496b15c8340SDag-Erling Smørgrav int 497bc5531deSDag-Erling Smørgrav sshkey_in_file(struct sshkey *key, const char *filename, int strict_type, 498bc5531deSDag-Erling Smørgrav int check_ca) 499b15c8340SDag-Erling Smørgrav { 500b15c8340SDag-Erling Smørgrav FILE *f; 501b15c8340SDag-Erling Smørgrav char line[SSH_MAX_PUBKEY_BYTES]; 502b15c8340SDag-Erling Smørgrav char *cp; 503b15c8340SDag-Erling Smørgrav u_long linenum = 0; 504a0ee8cc6SDag-Erling Smørgrav int r = 0; 505a0ee8cc6SDag-Erling Smørgrav struct sshkey *pub = NULL; 506a0ee8cc6SDag-Erling Smørgrav int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) = 507a0ee8cc6SDag-Erling Smørgrav strict_type ? sshkey_equal : sshkey_equal_public; 508b15c8340SDag-Erling Smørgrav 509bc5531deSDag-Erling Smørgrav if ((f = fopen(filename, "r")) == NULL) 510a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 511b15c8340SDag-Erling Smørgrav 512b15c8340SDag-Erling Smørgrav while (read_keyfile_line(f, filename, line, sizeof(line), 513b15c8340SDag-Erling Smørgrav &linenum) != -1) { 514b15c8340SDag-Erling Smørgrav cp = line; 515b15c8340SDag-Erling Smørgrav 516b15c8340SDag-Erling Smørgrav /* Skip leading whitespace. */ 517b15c8340SDag-Erling Smørgrav for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) 518b15c8340SDag-Erling Smørgrav ; 519b15c8340SDag-Erling Smørgrav 520b15c8340SDag-Erling Smørgrav /* Skip comments and empty lines */ 521b15c8340SDag-Erling Smørgrav switch (*cp) { 522b15c8340SDag-Erling Smørgrav case '#': 523b15c8340SDag-Erling Smørgrav case '\n': 524b15c8340SDag-Erling Smørgrav case '\0': 525b15c8340SDag-Erling Smørgrav continue; 526b15c8340SDag-Erling Smørgrav } 527b15c8340SDag-Erling Smørgrav 528a0ee8cc6SDag-Erling Smørgrav if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { 529a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 530a0ee8cc6SDag-Erling Smørgrav goto out; 531b15c8340SDag-Erling Smørgrav } 532a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_read(pub, &cp)) != 0) 533a0ee8cc6SDag-Erling Smørgrav goto out; 534bc5531deSDag-Erling Smørgrav if (sshkey_compare(key, pub) || 535bc5531deSDag-Erling Smørgrav (check_ca && sshkey_is_cert(key) && 536bc5531deSDag-Erling Smørgrav sshkey_compare(key->cert->signature_key, pub))) { 537a0ee8cc6SDag-Erling Smørgrav r = 0; 538a0ee8cc6SDag-Erling Smørgrav goto out; 539b15c8340SDag-Erling Smørgrav } 540a0ee8cc6SDag-Erling Smørgrav sshkey_free(pub); 541a0ee8cc6SDag-Erling Smørgrav pub = NULL; 542b15c8340SDag-Erling Smørgrav } 543a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_KEY_NOT_FOUND; 544a0ee8cc6SDag-Erling Smørgrav out: 545a0ee8cc6SDag-Erling Smørgrav sshkey_free(pub); 546b15c8340SDag-Erling Smørgrav fclose(f); 547a0ee8cc6SDag-Erling Smørgrav return r; 548b15c8340SDag-Erling Smørgrav } 549a0ee8cc6SDag-Erling Smørgrav 550bc5531deSDag-Erling Smørgrav /* 551bc5531deSDag-Erling Smørgrav * Checks whether the specified key is revoked, returning 0 if not, 552bc5531deSDag-Erling Smørgrav * SSH_ERR_KEY_REVOKED if it is or another error code if something 553bc5531deSDag-Erling Smørgrav * unexpected happened. 554bc5531deSDag-Erling Smørgrav * This will check both the key and, if it is a certificate, its CA key too. 555bc5531deSDag-Erling Smørgrav * "revoked_keys_file" may be a KRL or a one-per-line list of public keys. 556bc5531deSDag-Erling Smørgrav */ 557bc5531deSDag-Erling Smørgrav int 558bc5531deSDag-Erling Smørgrav sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file) 559bc5531deSDag-Erling Smørgrav { 560bc5531deSDag-Erling Smørgrav int r; 561bc5531deSDag-Erling Smørgrav 562bc5531deSDag-Erling Smørgrav r = ssh_krl_file_contains_key(revoked_keys_file, key); 563bc5531deSDag-Erling Smørgrav /* If this was not a KRL to begin with then continue below */ 564bc5531deSDag-Erling Smørgrav if (r != SSH_ERR_KRL_BAD_MAGIC) 565bc5531deSDag-Erling Smørgrav return r; 566bc5531deSDag-Erling Smørgrav 567bc5531deSDag-Erling Smørgrav /* 568bc5531deSDag-Erling Smørgrav * If the file is not a KRL or we can't handle KRLs then attempt to 569bc5531deSDag-Erling Smørgrav * parse the file as a flat list of keys. 570bc5531deSDag-Erling Smørgrav */ 571bc5531deSDag-Erling Smørgrav switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) { 572bc5531deSDag-Erling Smørgrav case 0: 573bc5531deSDag-Erling Smørgrav /* Key found => revoked */ 574bc5531deSDag-Erling Smørgrav return SSH_ERR_KEY_REVOKED; 575bc5531deSDag-Erling Smørgrav case SSH_ERR_KEY_NOT_FOUND: 576bc5531deSDag-Erling Smørgrav /* Key not found => not revoked */ 577bc5531deSDag-Erling Smørgrav return 0; 578bc5531deSDag-Erling Smørgrav default: 579bc5531deSDag-Erling Smørgrav /* Some other error occurred */ 580bc5531deSDag-Erling Smørgrav return r; 581bc5531deSDag-Erling Smørgrav } 582bc5531deSDag-Erling Smørgrav } 583bc5531deSDag-Erling Smørgrav 584