1*2f513db7SEd Maste /* $OpenBSD: authfile.c,v 1.131 2018/09/21 12:20:12 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" 45aa49c926SDag-Erling Smørgrav #include "misc.h" 46d4ecd108SDag-Erling Smørgrav #include "atomicio.h" 47eccfee6eSDag-Erling Smørgrav #include "sshkey.h" 48a0ee8cc6SDag-Erling Smørgrav #include "sshbuf.h" 49a0ee8cc6SDag-Erling Smørgrav #include "ssherr.h" 50bc5531deSDag-Erling Smørgrav #include "krl.h" 51511b41d2SMark Murray 52e146993eSDag-Erling Smørgrav #define MAX_KEY_FILE_SIZE (1024 * 1024) 53e146993eSDag-Erling Smørgrav 544a421b63SDag-Erling Smørgrav /* Save a key blob to a file */ 554a421b63SDag-Erling Smørgrav static int 56a0ee8cc6SDag-Erling Smørgrav sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename) 574a421b63SDag-Erling Smørgrav { 58a0ee8cc6SDag-Erling Smørgrav int fd, oerrno; 594a421b63SDag-Erling Smørgrav 60a0ee8cc6SDag-Erling Smørgrav if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) 61a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 62190cef3dSDag-Erling Smørgrav if (atomicio(vwrite, fd, sshbuf_mutable_ptr(keybuf), 63a0ee8cc6SDag-Erling Smørgrav sshbuf_len(keybuf)) != sshbuf_len(keybuf)) { 64a0ee8cc6SDag-Erling Smørgrav oerrno = errno; 654a421b63SDag-Erling Smørgrav close(fd); 664a421b63SDag-Erling Smørgrav unlink(filename); 67a0ee8cc6SDag-Erling Smørgrav errno = oerrno; 68a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 694a421b63SDag-Erling Smørgrav } 704a421b63SDag-Erling Smørgrav close(fd); 714a421b63SDag-Erling Smørgrav return 0; 724a421b63SDag-Erling Smørgrav } 734a421b63SDag-Erling Smørgrav 74e8aafc91SKris Kennaway int 75a0ee8cc6SDag-Erling Smørgrav sshkey_save_private(struct sshkey *key, const char *filename, 76a0ee8cc6SDag-Erling Smørgrav const char *passphrase, const char *comment, 77a0ee8cc6SDag-Erling Smørgrav int force_new_format, const char *new_format_cipher, int new_format_rounds) 78e8aafc91SKris Kennaway { 79a0ee8cc6SDag-Erling Smørgrav struct sshbuf *keyblob = NULL; 80a0ee8cc6SDag-Erling Smørgrav int r; 814a421b63SDag-Erling Smørgrav 82a0ee8cc6SDag-Erling Smørgrav if ((keyblob = sshbuf_new()) == NULL) 83a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 84a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment, 85a0ee8cc6SDag-Erling Smørgrav force_new_format, new_format_cipher, new_format_rounds)) != 0) 864a421b63SDag-Erling Smørgrav goto out; 87a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_save_private_blob(keyblob, filename)) != 0) 884a421b63SDag-Erling Smørgrav goto out; 89a0ee8cc6SDag-Erling Smørgrav r = 0; 904a421b63SDag-Erling Smørgrav out: 91a0ee8cc6SDag-Erling Smørgrav sshbuf_free(keyblob); 92a0ee8cc6SDag-Erling Smørgrav return r; 934a421b63SDag-Erling Smørgrav } 944a421b63SDag-Erling Smørgrav 95e146993eSDag-Erling Smørgrav /* Load a key from a fd into a buffer */ 96e146993eSDag-Erling Smørgrav int 97bc5531deSDag-Erling Smørgrav sshkey_load_file(int fd, struct sshbuf *blob) 984a421b63SDag-Erling Smørgrav { 99e146993eSDag-Erling Smørgrav u_char buf[1024]; 1004a421b63SDag-Erling Smørgrav size_t len; 1014a421b63SDag-Erling Smørgrav struct stat st; 1024f52dfbbSDag-Erling Smørgrav int r; 1034a421b63SDag-Erling Smørgrav 104a0ee8cc6SDag-Erling Smørgrav if (fstat(fd, &st) < 0) 105a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 106e146993eSDag-Erling Smørgrav if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && 107a0ee8cc6SDag-Erling Smørgrav st.st_size > MAX_KEY_FILE_SIZE) 108a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_INVALID_FORMAT; 109e146993eSDag-Erling Smørgrav for (;;) { 110e146993eSDag-Erling Smørgrav if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) { 111e146993eSDag-Erling Smørgrav if (errno == EPIPE) 112e146993eSDag-Erling Smørgrav break; 113a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_SYSTEM_ERROR; 114a0ee8cc6SDag-Erling Smørgrav goto out; 1154a421b63SDag-Erling Smørgrav } 116a0ee8cc6SDag-Erling Smørgrav if ((r = sshbuf_put(blob, buf, len)) != 0) 117a0ee8cc6SDag-Erling Smørgrav goto out; 118a0ee8cc6SDag-Erling Smørgrav if (sshbuf_len(blob) > MAX_KEY_FILE_SIZE) { 119a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 120a0ee8cc6SDag-Erling Smørgrav goto out; 121e146993eSDag-Erling Smørgrav } 122e146993eSDag-Erling Smørgrav } 123e146993eSDag-Erling Smørgrav if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && 124a0ee8cc6SDag-Erling Smørgrav st.st_size != (off_t)sshbuf_len(blob)) { 125a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_FILE_CHANGED; 126a0ee8cc6SDag-Erling Smørgrav goto out; 127a0ee8cc6SDag-Erling Smørgrav } 128a0ee8cc6SDag-Erling Smørgrav r = 0; 129a0ee8cc6SDag-Erling Smørgrav 130a0ee8cc6SDag-Erling Smørgrav out: 131a0ee8cc6SDag-Erling Smørgrav explicit_bzero(buf, sizeof(buf)); 132a0ee8cc6SDag-Erling Smørgrav if (r != 0) 133a0ee8cc6SDag-Erling Smørgrav sshbuf_reset(blob); 134a0ee8cc6SDag-Erling Smørgrav return r; 135e146993eSDag-Erling Smørgrav } 136e146993eSDag-Erling Smørgrav 137e8aafc91SKris Kennaway 138a0ee8cc6SDag-Erling Smørgrav /* XXX remove error() calls from here? */ 139a0ee8cc6SDag-Erling Smørgrav int 140a0ee8cc6SDag-Erling Smørgrav sshkey_perm_ok(int fd, const char *filename) 141e8aafc91SKris Kennaway { 142e8aafc91SKris Kennaway struct stat st; 143e8aafc91SKris Kennaway 144af12a3e7SDag-Erling Smørgrav if (fstat(fd, &st) < 0) 145a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 146af12a3e7SDag-Erling Smørgrav /* 147af12a3e7SDag-Erling Smørgrav * if a key owned by the user is accessed, then we check the 148af12a3e7SDag-Erling Smørgrav * permissions of the file. if the key owned by a different user, 149af12a3e7SDag-Erling Smørgrav * then we don't care. 150af12a3e7SDag-Erling Smørgrav */ 151989dd127SDag-Erling Smørgrav #ifdef HAVE_CYGWIN 152989dd127SDag-Erling Smørgrav if (check_ntsec(filename)) 153989dd127SDag-Erling Smørgrav #endif 154af12a3e7SDag-Erling Smørgrav if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) { 155e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 156e8aafc91SKris Kennaway error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); 157e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 158af12a3e7SDag-Erling Smørgrav error("Permissions 0%3.3o for '%s' are too open.", 159cf2b5f3bSDag-Erling Smørgrav (u_int)st.st_mode & 0777, filename); 160557f75e5SDag-Erling Smørgrav error("It is required that your private key files are NOT accessible by others."); 161ca3176e7SBrian Feldman error("This private key will be ignored."); 162a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_KEY_BAD_PERMISSIONS; 163a0ee8cc6SDag-Erling Smørgrav } 164e8aafc91SKris Kennaway return 0; 165e8aafc91SKris Kennaway } 166ca3176e7SBrian Feldman 167a0ee8cc6SDag-Erling Smørgrav /* XXX kill perm_ok now that we have SSH_ERR_KEY_BAD_PERMISSIONS? */ 168a0ee8cc6SDag-Erling Smørgrav int 169a0ee8cc6SDag-Erling Smørgrav sshkey_load_private_type(int type, const char *filename, const char *passphrase, 170a0ee8cc6SDag-Erling Smørgrav struct sshkey **keyp, char **commentp, int *perm_ok) 1714a421b63SDag-Erling Smørgrav { 172a0ee8cc6SDag-Erling Smørgrav int fd, r; 173f7167e0eSDag-Erling Smørgrav 174076ad2f8SDag-Erling Smørgrav if (keyp != NULL) 175a0ee8cc6SDag-Erling Smørgrav *keyp = NULL; 176a0ee8cc6SDag-Erling Smørgrav if (commentp != NULL) 177a0ee8cc6SDag-Erling Smørgrav *commentp = NULL; 1784a421b63SDag-Erling Smørgrav 179a0ee8cc6SDag-Erling Smørgrav if ((fd = open(filename, O_RDONLY)) < 0) { 180b15c8340SDag-Erling Smørgrav if (perm_ok != NULL) 181b15c8340SDag-Erling Smørgrav *perm_ok = 0; 182a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 183b15c8340SDag-Erling Smørgrav } 184a0ee8cc6SDag-Erling Smørgrav if (sshkey_perm_ok(fd, filename) != 0) { 185333ee039SDag-Erling Smørgrav if (perm_ok != NULL) 186333ee039SDag-Erling Smørgrav *perm_ok = 0; 187a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_KEY_BAD_PERMISSIONS; 188a0ee8cc6SDag-Erling Smørgrav goto out; 189e8aafc91SKris Kennaway } 190333ee039SDag-Erling Smørgrav if (perm_ok != NULL) 191333ee039SDag-Erling Smørgrav *perm_ok = 1; 1924a421b63SDag-Erling Smørgrav 193bc5531deSDag-Erling Smørgrav r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp); 19447dd1d1bSDag-Erling Smørgrav if (r == 0 && keyp && *keyp) 19547dd1d1bSDag-Erling Smørgrav r = sshkey_set_filename(*keyp, filename); 196bc5531deSDag-Erling Smørgrav out: 197bc5531deSDag-Erling Smørgrav close(fd); 198bc5531deSDag-Erling Smørgrav return r; 199bc5531deSDag-Erling Smørgrav } 200bc5531deSDag-Erling Smørgrav 201bc5531deSDag-Erling Smørgrav int 202bc5531deSDag-Erling Smørgrav sshkey_load_private_type_fd(int fd, int type, const char *passphrase, 203bc5531deSDag-Erling Smørgrav struct sshkey **keyp, char **commentp) 204bc5531deSDag-Erling Smørgrav { 205bc5531deSDag-Erling Smørgrav struct sshbuf *buffer = NULL; 206bc5531deSDag-Erling Smørgrav int r; 207bc5531deSDag-Erling Smørgrav 208076ad2f8SDag-Erling Smørgrav if (keyp != NULL) 209076ad2f8SDag-Erling Smørgrav *keyp = NULL; 210a0ee8cc6SDag-Erling Smørgrav if ((buffer = sshbuf_new()) == NULL) { 211a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 212a0ee8cc6SDag-Erling Smørgrav goto out; 213ca3176e7SBrian Feldman } 214bc5531deSDag-Erling Smørgrav if ((r = sshkey_load_file(fd, buffer)) != 0 || 215bc5531deSDag-Erling Smørgrav (r = sshkey_parse_private_fileblob_type(buffer, type, 216bc5531deSDag-Erling Smørgrav passphrase, keyp, commentp)) != 0) 217a0ee8cc6SDag-Erling Smørgrav goto out; 218bc5531deSDag-Erling Smørgrav 219bc5531deSDag-Erling Smørgrav /* success */ 220a0ee8cc6SDag-Erling Smørgrav r = 0; 221a0ee8cc6SDag-Erling Smørgrav out: 222a0ee8cc6SDag-Erling Smørgrav sshbuf_free(buffer); 223a0ee8cc6SDag-Erling Smørgrav return r; 2244a421b63SDag-Erling Smørgrav } 225ca3176e7SBrian Feldman 226a0ee8cc6SDag-Erling Smørgrav /* XXX this is almost identical to sshkey_load_private_type() */ 227a0ee8cc6SDag-Erling Smørgrav int 228a0ee8cc6SDag-Erling Smørgrav sshkey_load_private(const char *filename, const char *passphrase, 229a0ee8cc6SDag-Erling Smørgrav struct sshkey **keyp, char **commentp) 230e146993eSDag-Erling Smørgrav { 231a0ee8cc6SDag-Erling Smørgrav struct sshbuf *buffer = NULL; 232a0ee8cc6SDag-Erling Smørgrav int r, fd; 233e146993eSDag-Erling Smørgrav 234076ad2f8SDag-Erling Smørgrav if (keyp != NULL) 235a0ee8cc6SDag-Erling Smørgrav *keyp = NULL; 236a0ee8cc6SDag-Erling Smørgrav if (commentp != NULL) 237a0ee8cc6SDag-Erling Smørgrav *commentp = NULL; 238a0ee8cc6SDag-Erling Smørgrav 239a0ee8cc6SDag-Erling Smørgrav if ((fd = open(filename, O_RDONLY)) < 0) 240a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 241a0ee8cc6SDag-Erling Smørgrav if (sshkey_perm_ok(fd, filename) != 0) { 242a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_KEY_BAD_PERMISSIONS; 243a0ee8cc6SDag-Erling Smørgrav goto out; 244e146993eSDag-Erling Smørgrav } 245e146993eSDag-Erling Smørgrav 246a0ee8cc6SDag-Erling Smørgrav if ((buffer = sshbuf_new()) == NULL) { 247a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 248a0ee8cc6SDag-Erling Smørgrav goto out; 249b15c8340SDag-Erling Smørgrav } 250bc5531deSDag-Erling Smørgrav if ((r = sshkey_load_file(fd, buffer)) != 0 || 251acc1a9efSDag-Erling Smørgrav (r = sshkey_parse_private_fileblob(buffer, passphrase, keyp, 252acc1a9efSDag-Erling Smørgrav commentp)) != 0) 253a0ee8cc6SDag-Erling Smørgrav goto out; 25447dd1d1bSDag-Erling Smørgrav if (keyp && *keyp && 25547dd1d1bSDag-Erling Smørgrav (r = sshkey_set_filename(*keyp, filename)) != 0) 25647dd1d1bSDag-Erling Smørgrav goto out; 257a0ee8cc6SDag-Erling Smørgrav r = 0; 258a0ee8cc6SDag-Erling Smørgrav out: 259e8aafc91SKris Kennaway close(fd); 260a0ee8cc6SDag-Erling Smørgrav sshbuf_free(buffer); 261a0ee8cc6SDag-Erling Smørgrav return r; 262e8aafc91SKris Kennaway } 263c2d3a559SKris Kennaway 264af12a3e7SDag-Erling Smørgrav static int 265a0ee8cc6SDag-Erling Smørgrav sshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp) 266c2d3a559SKris Kennaway { 267c2d3a559SKris Kennaway FILE *f; 268190cef3dSDag-Erling Smørgrav char *line = NULL, *cp; 269190cef3dSDag-Erling Smørgrav size_t linesize = 0; 270a0ee8cc6SDag-Erling Smørgrav int r; 271c2d3a559SKris Kennaway 272a0ee8cc6SDag-Erling Smørgrav if (commentp != NULL) 273a0ee8cc6SDag-Erling Smørgrav *commentp = NULL; 274a0ee8cc6SDag-Erling Smørgrav if ((f = fopen(filename, "r")) == NULL) 275a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 276190cef3dSDag-Erling Smørgrav while (getline(&line, &linesize, f) != -1) { 277c2d3a559SKris Kennaway cp = line; 278c2d3a559SKris Kennaway switch (*cp) { 279c2d3a559SKris Kennaway case '#': 280c2d3a559SKris Kennaway case '\n': 281c2d3a559SKris Kennaway case '\0': 282c2d3a559SKris Kennaway continue; 283c2d3a559SKris Kennaway } 284e146993eSDag-Erling Smørgrav /* Abort loading if this looks like a private key */ 285a0ee8cc6SDag-Erling Smørgrav if (strncmp(cp, "-----BEGIN", 10) == 0 || 286a0ee8cc6SDag-Erling Smørgrav strcmp(cp, "SSH PRIVATE KEY FILE") == 0) 287e146993eSDag-Erling Smørgrav break; 288c2d3a559SKris Kennaway /* Skip leading whitespace. */ 289c2d3a559SKris Kennaway for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) 290c2d3a559SKris Kennaway ; 291c2d3a559SKris Kennaway if (*cp) { 292a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_read(k, &cp)) == 0) { 293e146993eSDag-Erling Smørgrav cp[strcspn(cp, "\r\n")] = '\0'; 294e146993eSDag-Erling Smørgrav if (commentp) { 295a0ee8cc6SDag-Erling Smørgrav *commentp = strdup(*cp ? 296e146993eSDag-Erling Smørgrav cp : filename); 297a0ee8cc6SDag-Erling Smørgrav if (*commentp == NULL) 298a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 299e146993eSDag-Erling Smørgrav } 300190cef3dSDag-Erling Smørgrav free(line); 301c2d3a559SKris Kennaway fclose(f); 302a0ee8cc6SDag-Erling Smørgrav return r; 303c2d3a559SKris Kennaway } 304c2d3a559SKris Kennaway } 305c2d3a559SKris Kennaway } 306190cef3dSDag-Erling Smørgrav free(line); 307c2d3a559SKris Kennaway fclose(f); 308a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_INVALID_FORMAT; 309c2d3a559SKris Kennaway } 310c2d3a559SKris Kennaway 3114f52dfbbSDag-Erling Smørgrav /* load public key from any pubkey file */ 312a0ee8cc6SDag-Erling Smørgrav int 313a0ee8cc6SDag-Erling Smørgrav sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp) 314c2d3a559SKris Kennaway { 315a0ee8cc6SDag-Erling Smørgrav struct sshkey *pub = NULL; 3164f52dfbbSDag-Erling Smørgrav char *file = NULL; 3174f52dfbbSDag-Erling Smørgrav int r; 318c2d3a559SKris Kennaway 319a0ee8cc6SDag-Erling Smørgrav if (keyp != NULL) 320a0ee8cc6SDag-Erling Smørgrav *keyp = NULL; 321a0ee8cc6SDag-Erling Smørgrav if (commentp != NULL) 322a0ee8cc6SDag-Erling Smørgrav *commentp = NULL; 323a0ee8cc6SDag-Erling Smørgrav 324a0ee8cc6SDag-Erling Smørgrav if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) 325a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 326a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) { 3274f52dfbbSDag-Erling Smørgrav if (keyp != NULL) { 328a0ee8cc6SDag-Erling Smørgrav *keyp = pub; 3294f52dfbbSDag-Erling Smørgrav pub = NULL; 3304f52dfbbSDag-Erling Smørgrav } 3314f52dfbbSDag-Erling Smørgrav r = 0; 3324f52dfbbSDag-Erling Smørgrav goto out; 333a0ee8cc6SDag-Erling Smørgrav } 334a0ee8cc6SDag-Erling Smørgrav sshkey_free(pub); 335a0ee8cc6SDag-Erling Smørgrav 336a0ee8cc6SDag-Erling Smørgrav /* try .pub suffix */ 3374f52dfbbSDag-Erling Smørgrav if (asprintf(&file, "%s.pub", filename) == -1) 338a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 3394f52dfbbSDag-Erling Smørgrav if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { 3404f52dfbbSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 3414f52dfbbSDag-Erling Smørgrav goto out; 342a0ee8cc6SDag-Erling Smørgrav } 3434f52dfbbSDag-Erling Smørgrav if ((r = sshkey_try_load_public(pub, file, commentp)) == 0) { 3444f52dfbbSDag-Erling Smørgrav if (keyp != NULL) { 3454f52dfbbSDag-Erling Smørgrav *keyp = pub; 3464f52dfbbSDag-Erling Smørgrav pub = NULL; 3474f52dfbbSDag-Erling Smørgrav } 3484f52dfbbSDag-Erling Smørgrav r = 0; 3494f52dfbbSDag-Erling Smørgrav } 3504f52dfbbSDag-Erling Smørgrav out: 3514f52dfbbSDag-Erling Smørgrav free(file); 352a0ee8cc6SDag-Erling Smørgrav sshkey_free(pub); 353a0ee8cc6SDag-Erling Smørgrav return r; 354c2d3a559SKris Kennaway } 355b15c8340SDag-Erling Smørgrav 356e2f6069cSDag-Erling Smørgrav /* Load the certificate associated with the named private key */ 357a0ee8cc6SDag-Erling Smørgrav int 358a0ee8cc6SDag-Erling Smørgrav sshkey_load_cert(const char *filename, struct sshkey **keyp) 359e2f6069cSDag-Erling Smørgrav { 360a0ee8cc6SDag-Erling Smørgrav struct sshkey *pub = NULL; 361a0ee8cc6SDag-Erling Smørgrav char *file = NULL; 362a0ee8cc6SDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 363e2f6069cSDag-Erling Smørgrav 364076ad2f8SDag-Erling Smørgrav if (keyp != NULL) 365a0ee8cc6SDag-Erling Smørgrav *keyp = NULL; 366a0ee8cc6SDag-Erling Smørgrav 367a0ee8cc6SDag-Erling Smørgrav if (asprintf(&file, "%s-cert.pub", filename) == -1) 368a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 369a0ee8cc6SDag-Erling Smørgrav 370a0ee8cc6SDag-Erling Smørgrav if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { 371a0ee8cc6SDag-Erling Smørgrav goto out; 372e2f6069cSDag-Erling Smørgrav } 373a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_try_load_public(pub, file, NULL)) != 0) 374a0ee8cc6SDag-Erling Smørgrav goto out; 375076ad2f8SDag-Erling Smørgrav /* success */ 376076ad2f8SDag-Erling Smørgrav if (keyp != NULL) { 377a0ee8cc6SDag-Erling Smørgrav *keyp = pub; 378a0ee8cc6SDag-Erling Smørgrav pub = NULL; 379076ad2f8SDag-Erling Smørgrav } 380a0ee8cc6SDag-Erling Smørgrav r = 0; 381a0ee8cc6SDag-Erling Smørgrav out: 382e4a9863fSDag-Erling Smørgrav free(file); 383a0ee8cc6SDag-Erling Smørgrav sshkey_free(pub); 384a0ee8cc6SDag-Erling Smørgrav return r; 385e2f6069cSDag-Erling Smørgrav } 386e2f6069cSDag-Erling Smørgrav 387e2f6069cSDag-Erling Smørgrav /* Load private key and certificate */ 388a0ee8cc6SDag-Erling Smørgrav int 389a0ee8cc6SDag-Erling Smørgrav sshkey_load_private_cert(int type, const char *filename, const char *passphrase, 390a0ee8cc6SDag-Erling Smørgrav struct sshkey **keyp, int *perm_ok) 391e2f6069cSDag-Erling Smørgrav { 392a0ee8cc6SDag-Erling Smørgrav struct sshkey *key = NULL, *cert = NULL; 393a0ee8cc6SDag-Erling Smørgrav int r; 394a0ee8cc6SDag-Erling Smørgrav 395076ad2f8SDag-Erling Smørgrav if (keyp != NULL) 396a0ee8cc6SDag-Erling Smørgrav *keyp = NULL; 397e2f6069cSDag-Erling Smørgrav 398e2f6069cSDag-Erling Smørgrav switch (type) { 399a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_OPENSSL 400e2f6069cSDag-Erling Smørgrav case KEY_RSA: 401e2f6069cSDag-Erling Smørgrav case KEY_DSA: 4024a421b63SDag-Erling Smørgrav case KEY_ECDSA: 403a0ee8cc6SDag-Erling Smørgrav #endif /* WITH_OPENSSL */ 404eccfee6eSDag-Erling Smørgrav case KEY_ED25519: 40547dd1d1bSDag-Erling Smørgrav case KEY_XMSS: 406a0ee8cc6SDag-Erling Smørgrav case KEY_UNSPEC: 407e2f6069cSDag-Erling Smørgrav break; 408e2f6069cSDag-Erling Smørgrav default: 409a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_KEY_TYPE_UNKNOWN; 410e2f6069cSDag-Erling Smørgrav } 411e2f6069cSDag-Erling Smørgrav 412a0ee8cc6SDag-Erling Smørgrav if ((r = sshkey_load_private_type(type, filename, 413a0ee8cc6SDag-Erling Smørgrav passphrase, &key, NULL, perm_ok)) != 0 || 414a0ee8cc6SDag-Erling Smørgrav (r = sshkey_load_cert(filename, &cert)) != 0) 415a0ee8cc6SDag-Erling Smørgrav goto out; 416e2f6069cSDag-Erling Smørgrav 417e2f6069cSDag-Erling Smørgrav /* Make sure the private key matches the certificate */ 418a0ee8cc6SDag-Erling Smørgrav if (sshkey_equal_public(key, cert) == 0) { 419a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_KEY_CERT_MISMATCH; 420a0ee8cc6SDag-Erling Smørgrav goto out; 421e2f6069cSDag-Erling Smørgrav } 422e2f6069cSDag-Erling Smørgrav 423eccfee6eSDag-Erling Smørgrav if ((r = sshkey_to_certified(key)) != 0 || 424a0ee8cc6SDag-Erling Smørgrav (r = sshkey_cert_copy(cert, key)) != 0) 425a0ee8cc6SDag-Erling Smørgrav goto out; 426a0ee8cc6SDag-Erling Smørgrav r = 0; 427076ad2f8SDag-Erling Smørgrav if (keyp != NULL) { 428a0ee8cc6SDag-Erling Smørgrav *keyp = key; 429a0ee8cc6SDag-Erling Smørgrav key = NULL; 430076ad2f8SDag-Erling Smørgrav } 431a0ee8cc6SDag-Erling Smørgrav out: 432a0ee8cc6SDag-Erling Smørgrav sshkey_free(key); 433a0ee8cc6SDag-Erling Smørgrav sshkey_free(cert); 434a0ee8cc6SDag-Erling Smørgrav return r; 435e2f6069cSDag-Erling Smørgrav } 436e2f6069cSDag-Erling Smørgrav 437b15c8340SDag-Erling Smørgrav /* 438a0ee8cc6SDag-Erling Smørgrav * Returns success if the specified "key" is listed in the file "filename", 439a0ee8cc6SDag-Erling Smørgrav * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error. 440bc5531deSDag-Erling Smørgrav * If "strict_type" is set then the key type must match exactly, 441b15c8340SDag-Erling Smørgrav * otherwise a comparison that ignores certficiate data is performed. 442bc5531deSDag-Erling Smørgrav * If "check_ca" is set and "key" is a certificate, then its CA key is 443bc5531deSDag-Erling Smørgrav * also checked and sshkey_in_file() will return success if either is found. 444b15c8340SDag-Erling Smørgrav */ 445b15c8340SDag-Erling Smørgrav int 446bc5531deSDag-Erling Smørgrav sshkey_in_file(struct sshkey *key, const char *filename, int strict_type, 447bc5531deSDag-Erling Smørgrav int check_ca) 448b15c8340SDag-Erling Smørgrav { 449b15c8340SDag-Erling Smørgrav FILE *f; 450190cef3dSDag-Erling Smørgrav char *line = NULL, *cp; 451190cef3dSDag-Erling Smørgrav size_t linesize = 0; 452a0ee8cc6SDag-Erling Smørgrav int r = 0; 453a0ee8cc6SDag-Erling Smørgrav struct sshkey *pub = NULL; 454190cef3dSDag-Erling Smørgrav 455a0ee8cc6SDag-Erling Smørgrav int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) = 456a0ee8cc6SDag-Erling Smørgrav strict_type ? sshkey_equal : sshkey_equal_public; 457b15c8340SDag-Erling Smørgrav 458bc5531deSDag-Erling Smørgrav if ((f = fopen(filename, "r")) == NULL) 459a0ee8cc6SDag-Erling Smørgrav return SSH_ERR_SYSTEM_ERROR; 460b15c8340SDag-Erling Smørgrav 461190cef3dSDag-Erling Smørgrav while (getline(&line, &linesize, f) != -1) { 462*2f513db7SEd Maste sshkey_free(pub); 463*2f513db7SEd Maste pub = NULL; 464b15c8340SDag-Erling Smørgrav cp = line; 465b15c8340SDag-Erling Smørgrav 466b15c8340SDag-Erling Smørgrav /* Skip leading whitespace. */ 467b15c8340SDag-Erling Smørgrav for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) 468b15c8340SDag-Erling Smørgrav ; 469b15c8340SDag-Erling Smørgrav 470b15c8340SDag-Erling Smørgrav /* Skip comments and empty lines */ 471b15c8340SDag-Erling Smørgrav switch (*cp) { 472b15c8340SDag-Erling Smørgrav case '#': 473b15c8340SDag-Erling Smørgrav case '\n': 474b15c8340SDag-Erling Smørgrav case '\0': 475b15c8340SDag-Erling Smørgrav continue; 476b15c8340SDag-Erling Smørgrav } 477b15c8340SDag-Erling Smørgrav 478a0ee8cc6SDag-Erling Smørgrav if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { 479a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 480a0ee8cc6SDag-Erling Smørgrav goto out; 481b15c8340SDag-Erling Smørgrav } 482*2f513db7SEd Maste switch (r = sshkey_read(pub, &cp)) { 483*2f513db7SEd Maste case 0: 484*2f513db7SEd Maste break; 485*2f513db7SEd Maste case SSH_ERR_KEY_LENGTH: 486*2f513db7SEd Maste continue; 487*2f513db7SEd Maste default: 488a0ee8cc6SDag-Erling Smørgrav goto out; 489*2f513db7SEd Maste } 490bc5531deSDag-Erling Smørgrav if (sshkey_compare(key, pub) || 491bc5531deSDag-Erling Smørgrav (check_ca && sshkey_is_cert(key) && 492bc5531deSDag-Erling Smørgrav sshkey_compare(key->cert->signature_key, pub))) { 493a0ee8cc6SDag-Erling Smørgrav r = 0; 494a0ee8cc6SDag-Erling Smørgrav goto out; 495b15c8340SDag-Erling Smørgrav } 496b15c8340SDag-Erling Smørgrav } 497a0ee8cc6SDag-Erling Smørgrav r = SSH_ERR_KEY_NOT_FOUND; 498a0ee8cc6SDag-Erling Smørgrav out: 499190cef3dSDag-Erling Smørgrav free(line); 500a0ee8cc6SDag-Erling Smørgrav sshkey_free(pub); 501b15c8340SDag-Erling Smørgrav fclose(f); 502a0ee8cc6SDag-Erling Smørgrav return r; 503b15c8340SDag-Erling Smørgrav } 504a0ee8cc6SDag-Erling Smørgrav 505bc5531deSDag-Erling Smørgrav /* 506bc5531deSDag-Erling Smørgrav * Checks whether the specified key is revoked, returning 0 if not, 507bc5531deSDag-Erling Smørgrav * SSH_ERR_KEY_REVOKED if it is or another error code if something 508bc5531deSDag-Erling Smørgrav * unexpected happened. 509bc5531deSDag-Erling Smørgrav * This will check both the key and, if it is a certificate, its CA key too. 510bc5531deSDag-Erling Smørgrav * "revoked_keys_file" may be a KRL or a one-per-line list of public keys. 511bc5531deSDag-Erling Smørgrav */ 512bc5531deSDag-Erling Smørgrav int 513bc5531deSDag-Erling Smørgrav sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file) 514bc5531deSDag-Erling Smørgrav { 515bc5531deSDag-Erling Smørgrav int r; 516bc5531deSDag-Erling Smørgrav 517bc5531deSDag-Erling Smørgrav r = ssh_krl_file_contains_key(revoked_keys_file, key); 518bc5531deSDag-Erling Smørgrav /* If this was not a KRL to begin with then continue below */ 519bc5531deSDag-Erling Smørgrav if (r != SSH_ERR_KRL_BAD_MAGIC) 520bc5531deSDag-Erling Smørgrav return r; 521bc5531deSDag-Erling Smørgrav 522bc5531deSDag-Erling Smørgrav /* 523bc5531deSDag-Erling Smørgrav * If the file is not a KRL or we can't handle KRLs then attempt to 524bc5531deSDag-Erling Smørgrav * parse the file as a flat list of keys. 525bc5531deSDag-Erling Smørgrav */ 526bc5531deSDag-Erling Smørgrav switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) { 527bc5531deSDag-Erling Smørgrav case 0: 528bc5531deSDag-Erling Smørgrav /* Key found => revoked */ 529bc5531deSDag-Erling Smørgrav return SSH_ERR_KEY_REVOKED; 530bc5531deSDag-Erling Smørgrav case SSH_ERR_KEY_NOT_FOUND: 531bc5531deSDag-Erling Smørgrav /* Key not found => not revoked */ 532bc5531deSDag-Erling Smørgrav return 0; 533bc5531deSDag-Erling Smørgrav default: 534bc5531deSDag-Erling Smørgrav /* Some other error occurred */ 535bc5531deSDag-Erling Smørgrav return r; 536bc5531deSDag-Erling Smørgrav } 537bc5531deSDag-Erling Smørgrav } 538bc5531deSDag-Erling Smørgrav 539