1511b41d2SMark Murray /* 2511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 3511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4511b41d2SMark Murray * All rights reserved 5511b41d2SMark Murray * This file contains functions for reading and writing identity files, and 6511b41d2SMark Murray * for reading the passphrase from the user. 7511b41d2SMark Murray * 8c2d3a559SKris Kennaway * As far as I am concerned, the code I have written for this software 9c2d3a559SKris Kennaway * can be used freely for any purpose. Any derived versions of this 10c2d3a559SKris Kennaway * software must be clearly marked as such, and if the derived work is 11c2d3a559SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 12c2d3a559SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 13c2d3a559SKris Kennaway * 14c2d3a559SKris Kennaway * 15c2d3a559SKris Kennaway * Copyright (c) 2000 Markus Friedl. All rights reserved. 16c2d3a559SKris Kennaway * 17c2d3a559SKris Kennaway * Redistribution and use in source and binary forms, with or without 18c2d3a559SKris Kennaway * modification, are permitted provided that the following conditions 19c2d3a559SKris Kennaway * are met: 20c2d3a559SKris Kennaway * 1. Redistributions of source code must retain the above copyright 21c2d3a559SKris Kennaway * notice, this list of conditions and the following disclaimer. 22c2d3a559SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 23c2d3a559SKris Kennaway * notice, this list of conditions and the following disclaimer in the 24c2d3a559SKris Kennaway * documentation and/or other materials provided with the distribution. 25c2d3a559SKris Kennaway * 26c2d3a559SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27c2d3a559SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28c2d3a559SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29c2d3a559SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30c2d3a559SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31c2d3a559SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32c2d3a559SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33c2d3a559SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34c2d3a559SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35c2d3a559SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36511b41d2SMark Murray */ 37511b41d2SMark Murray 38511b41d2SMark Murray #include "includes.h" 39cf2b5f3bSDag-Erling Smørgrav RCSID("$OpenBSD: authfile.c,v 1.54 2003/05/24 09:30:39 djm Exp $"); 40511b41d2SMark Murray 41ca3176e7SBrian Feldman #include <openssl/err.h> 42e8aafc91SKris Kennaway #include <openssl/evp.h> 43ca3176e7SBrian Feldman #include <openssl/pem.h> 44e8aafc91SKris Kennaway 45ca3176e7SBrian Feldman #include "cipher.h" 46511b41d2SMark Murray #include "xmalloc.h" 47511b41d2SMark Murray #include "buffer.h" 48511b41d2SMark Murray #include "bufaux.h" 49e8aafc91SKris Kennaway #include "key.h" 50ca3176e7SBrian Feldman #include "ssh.h" 51ca3176e7SBrian Feldman #include "log.h" 52ca3176e7SBrian Feldman #include "authfile.h" 53af12a3e7SDag-Erling Smørgrav #include "rsa.h" 54511b41d2SMark Murray 55ca3176e7SBrian Feldman /* Version identification string for SSH v1 identity files. */ 56ca3176e7SBrian Feldman static const char authfile_id_string[] = 57ca3176e7SBrian Feldman "SSH PRIVATE KEY FILE FORMAT 1.1\n"; 58511b41d2SMark Murray 59511b41d2SMark Murray /* 60511b41d2SMark Murray * Saves the authentication (private) key in a file, encrypting it with 61511b41d2SMark Murray * passphrase. The identification of the file (lowest 64 bits of n) will 62511b41d2SMark Murray * precede the key to provide identification of the key without needing a 63511b41d2SMark Murray * passphrase. 64511b41d2SMark Murray */ 65511b41d2SMark Murray 66af12a3e7SDag-Erling Smørgrav static int 67ca3176e7SBrian Feldman key_save_private_rsa1(Key *key, const char *filename, const char *passphrase, 68ca3176e7SBrian Feldman const char *comment) 69511b41d2SMark Murray { 70511b41d2SMark Murray Buffer buffer, encrypted; 71af12a3e7SDag-Erling Smørgrav u_char buf[100], *cp; 72af12a3e7SDag-Erling Smørgrav int fd, i, cipher_num; 7309958426SBrian Feldman CipherContext ciphercontext; 7409958426SBrian Feldman Cipher *cipher; 75511b41d2SMark Murray u_int32_t rand; 76511b41d2SMark Murray 77511b41d2SMark Murray /* 78511b41d2SMark Murray * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting 79511b41d2SMark Murray * to another cipher; otherwise use SSH_AUTHFILE_CIPHER. 80511b41d2SMark Murray */ 81af12a3e7SDag-Erling Smørgrav cipher_num = (strcmp(passphrase, "") == 0) ? 82af12a3e7SDag-Erling Smørgrav SSH_CIPHER_NONE : SSH_AUTHFILE_CIPHER; 83af12a3e7SDag-Erling Smørgrav if ((cipher = cipher_by_number(cipher_num)) == NULL) 8409958426SBrian Feldman fatal("save_private_key_rsa: bad cipher"); 85511b41d2SMark Murray 86511b41d2SMark Murray /* This buffer is used to built the secret part of the private key. */ 87511b41d2SMark Murray buffer_init(&buffer); 88511b41d2SMark Murray 89511b41d2SMark Murray /* Put checkbytes for checking passphrase validity. */ 90511b41d2SMark Murray rand = arc4random(); 91511b41d2SMark Murray buf[0] = rand & 0xff; 92511b41d2SMark Murray buf[1] = (rand >> 8) & 0xff; 93511b41d2SMark Murray buf[2] = buf[0]; 94511b41d2SMark Murray buf[3] = buf[1]; 95511b41d2SMark Murray buffer_append(&buffer, buf, 4); 96511b41d2SMark Murray 97511b41d2SMark Murray /* 98511b41d2SMark Murray * Store the private key (n and e will not be stored because they 99511b41d2SMark Murray * will be stored in plain text, and storing them also in encrypted 100511b41d2SMark Murray * format would just give known plaintext). 101511b41d2SMark Murray */ 102ca3176e7SBrian Feldman buffer_put_bignum(&buffer, key->rsa->d); 103ca3176e7SBrian Feldman buffer_put_bignum(&buffer, key->rsa->iqmp); 104ca3176e7SBrian Feldman buffer_put_bignum(&buffer, key->rsa->q); /* reverse from SSL p */ 105ca3176e7SBrian Feldman buffer_put_bignum(&buffer, key->rsa->p); /* reverse from SSL q */ 106511b41d2SMark Murray 107511b41d2SMark Murray /* Pad the part to be encrypted until its size is a multiple of 8. */ 108511b41d2SMark Murray while (buffer_len(&buffer) % 8 != 0) 109511b41d2SMark Murray buffer_put_char(&buffer, 0); 110511b41d2SMark Murray 111511b41d2SMark Murray /* This buffer will be used to contain the data in the file. */ 112511b41d2SMark Murray buffer_init(&encrypted); 113511b41d2SMark Murray 114511b41d2SMark Murray /* First store keyfile id string. */ 115ca3176e7SBrian Feldman for (i = 0; authfile_id_string[i]; i++) 116ca3176e7SBrian Feldman buffer_put_char(&encrypted, authfile_id_string[i]); 117511b41d2SMark Murray buffer_put_char(&encrypted, 0); 118511b41d2SMark Murray 119511b41d2SMark Murray /* Store cipher type. */ 120af12a3e7SDag-Erling Smørgrav buffer_put_char(&encrypted, cipher_num); 121511b41d2SMark Murray buffer_put_int(&encrypted, 0); /* For future extension */ 122511b41d2SMark Murray 123511b41d2SMark Murray /* Store public key. This will be in plain text. */ 124ca3176e7SBrian Feldman buffer_put_int(&encrypted, BN_num_bits(key->rsa->n)); 125ca3176e7SBrian Feldman buffer_put_bignum(&encrypted, key->rsa->n); 126ca3176e7SBrian Feldman buffer_put_bignum(&encrypted, key->rsa->e); 127af12a3e7SDag-Erling Smørgrav buffer_put_cstring(&encrypted, comment); 128511b41d2SMark Murray 129511b41d2SMark Murray /* Allocate space for the private part of the key in the buffer. */ 130af12a3e7SDag-Erling Smørgrav cp = buffer_append_space(&encrypted, buffer_len(&buffer)); 131511b41d2SMark Murray 132af12a3e7SDag-Erling Smørgrav cipher_set_key_string(&ciphercontext, cipher, passphrase, 133af12a3e7SDag-Erling Smørgrav CIPHER_ENCRYPT); 134af12a3e7SDag-Erling Smørgrav cipher_crypt(&ciphercontext, cp, 135af12a3e7SDag-Erling Smørgrav buffer_ptr(&buffer), buffer_len(&buffer)); 136af12a3e7SDag-Erling Smørgrav cipher_cleanup(&ciphercontext); 13709958426SBrian Feldman memset(&ciphercontext, 0, sizeof(ciphercontext)); 138511b41d2SMark Murray 139511b41d2SMark Murray /* Destroy temporary data. */ 140511b41d2SMark Murray memset(buf, 0, sizeof(buf)); 141511b41d2SMark Murray buffer_free(&buffer); 142511b41d2SMark Murray 143511b41d2SMark Murray fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); 144ca3176e7SBrian Feldman if (fd < 0) { 145ca3176e7SBrian Feldman error("open %s failed: %s.", filename, strerror(errno)); 146511b41d2SMark Murray return 0; 147ca3176e7SBrian Feldman } 148511b41d2SMark Murray if (write(fd, buffer_ptr(&encrypted), buffer_len(&encrypted)) != 149511b41d2SMark Murray buffer_len(&encrypted)) { 150ca3176e7SBrian Feldman error("write to key file %s failed: %s", filename, 151511b41d2SMark Murray strerror(errno)); 152511b41d2SMark Murray buffer_free(&encrypted); 153511b41d2SMark Murray close(fd); 154ca3176e7SBrian Feldman unlink(filename); 155511b41d2SMark Murray return 0; 156511b41d2SMark Murray } 157511b41d2SMark Murray close(fd); 158511b41d2SMark Murray buffer_free(&encrypted); 159511b41d2SMark Murray return 1; 160511b41d2SMark Murray } 161511b41d2SMark Murray 162ca3176e7SBrian Feldman /* save SSH v2 key in OpenSSL PEM format */ 163af12a3e7SDag-Erling Smørgrav static int 164ca3176e7SBrian Feldman key_save_private_pem(Key *key, const char *filename, const char *_passphrase, 165ca3176e7SBrian Feldman const char *comment) 166e8aafc91SKris Kennaway { 167e8aafc91SKris Kennaway FILE *fp; 168e8aafc91SKris Kennaway int fd; 169ca3176e7SBrian Feldman int success = 0; 170ca3176e7SBrian Feldman int len = strlen(_passphrase); 171af12a3e7SDag-Erling Smørgrav u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL; 172af12a3e7SDag-Erling Smørgrav const EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL; 173e8aafc91SKris Kennaway 174e8aafc91SKris Kennaway if (len > 0 && len <= 4) { 175ca3176e7SBrian Feldman error("passphrase too short: have %d bytes, need > 4", len); 176e8aafc91SKris Kennaway return 0; 177e8aafc91SKris Kennaway } 178e8aafc91SKris Kennaway fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); 179e8aafc91SKris Kennaway if (fd < 0) { 180ca3176e7SBrian Feldman error("open %s failed: %s.", filename, strerror(errno)); 181e8aafc91SKris Kennaway return 0; 182e8aafc91SKris Kennaway } 183e8aafc91SKris Kennaway fp = fdopen(fd, "w"); 184e8aafc91SKris Kennaway if (fp == NULL ) { 185ca3176e7SBrian Feldman error("fdopen %s failed: %s.", filename, strerror(errno)); 186e8aafc91SKris Kennaway close(fd); 187e8aafc91SKris Kennaway return 0; 188e8aafc91SKris Kennaway } 189ca3176e7SBrian Feldman switch (key->type) { 190ca3176e7SBrian Feldman case KEY_DSA: 191ca3176e7SBrian Feldman success = PEM_write_DSAPrivateKey(fp, key->dsa, 192ca3176e7SBrian Feldman cipher, passphrase, len, NULL, NULL); 193ca3176e7SBrian Feldman break; 194ca3176e7SBrian Feldman case KEY_RSA: 195ca3176e7SBrian Feldman success = PEM_write_RSAPrivateKey(fp, key->rsa, 196ca3176e7SBrian Feldman cipher, passphrase, len, NULL, NULL); 197ca3176e7SBrian Feldman break; 198e8aafc91SKris Kennaway } 199e8aafc91SKris Kennaway fclose(fp); 200e8aafc91SKris Kennaway return success; 201e8aafc91SKris Kennaway } 202e8aafc91SKris Kennaway 203e8aafc91SKris Kennaway int 204ca3176e7SBrian Feldman key_save_private(Key *key, const char *filename, const char *passphrase, 205e8aafc91SKris Kennaway const char *comment) 206e8aafc91SKris Kennaway { 207e8aafc91SKris Kennaway switch (key->type) { 208ca3176e7SBrian Feldman case KEY_RSA1: 209ca3176e7SBrian Feldman return key_save_private_rsa1(key, filename, passphrase, 210ca3176e7SBrian Feldman comment); 211e8aafc91SKris Kennaway break; 212e8aafc91SKris Kennaway case KEY_DSA: 213ca3176e7SBrian Feldman case KEY_RSA: 214ca3176e7SBrian Feldman return key_save_private_pem(key, filename, passphrase, 215ca3176e7SBrian Feldman comment); 216e8aafc91SKris Kennaway break; 217e8aafc91SKris Kennaway default: 218e8aafc91SKris Kennaway break; 219e8aafc91SKris Kennaway } 220ca3176e7SBrian Feldman error("key_save_private: cannot save key type %d", key->type); 221e8aafc91SKris Kennaway return 0; 222e8aafc91SKris Kennaway } 223e8aafc91SKris Kennaway 224511b41d2SMark Murray /* 225ca3176e7SBrian Feldman * Loads the public part of the ssh v1 key file. Returns NULL if an error was 226ca3176e7SBrian Feldman * encountered (the file does not exist or is not readable), and the key 227511b41d2SMark Murray * otherwise. 228511b41d2SMark Murray */ 229511b41d2SMark Murray 230af12a3e7SDag-Erling Smørgrav static Key * 231ca3176e7SBrian Feldman key_load_public_rsa1(int fd, const char *filename, char **commentp) 232511b41d2SMark Murray { 233511b41d2SMark Murray Buffer buffer; 234ca3176e7SBrian Feldman Key *pub; 235e73e9afaSDag-Erling Smørgrav struct stat st; 236511b41d2SMark Murray char *cp; 237ca3176e7SBrian Feldman int i; 238ca3176e7SBrian Feldman off_t len; 239511b41d2SMark Murray 240e73e9afaSDag-Erling Smørgrav if (fstat(fd, &st) < 0) { 241e73e9afaSDag-Erling Smørgrav error("fstat for key file %.200s failed: %.100s", 242e73e9afaSDag-Erling Smørgrav filename, strerror(errno)); 243e73e9afaSDag-Erling Smørgrav return NULL; 244e73e9afaSDag-Erling Smørgrav } 245e73e9afaSDag-Erling Smørgrav len = st.st_size; 246511b41d2SMark Murray 247511b41d2SMark Murray buffer_init(&buffer); 248af12a3e7SDag-Erling Smørgrav cp = buffer_append_space(&buffer, len); 249511b41d2SMark Murray 250511b41d2SMark Murray if (read(fd, cp, (size_t) len) != (size_t) len) { 251511b41d2SMark Murray debug("Read from key file %.200s failed: %.100s", filename, 252511b41d2SMark Murray strerror(errno)); 253511b41d2SMark Murray buffer_free(&buffer); 254ca3176e7SBrian Feldman return NULL; 255511b41d2SMark Murray } 256511b41d2SMark Murray 257ca3176e7SBrian Feldman /* Check that it is at least big enough to contain the ID string. */ 258ca3176e7SBrian Feldman if (len < sizeof(authfile_id_string)) { 259af12a3e7SDag-Erling Smørgrav debug3("Not a RSA1 key file %.200s.", filename); 260511b41d2SMark Murray buffer_free(&buffer); 261ca3176e7SBrian Feldman return NULL; 262511b41d2SMark Murray } 263511b41d2SMark Murray /* 264511b41d2SMark Murray * Make sure it begins with the id string. Consume the id string 265511b41d2SMark Murray * from the buffer. 266511b41d2SMark Murray */ 267ca3176e7SBrian Feldman for (i = 0; i < sizeof(authfile_id_string); i++) 268ca3176e7SBrian Feldman if (buffer_get_char(&buffer) != authfile_id_string[i]) { 269af12a3e7SDag-Erling Smørgrav debug3("Not a RSA1 key file %.200s.", filename); 270511b41d2SMark Murray buffer_free(&buffer); 271ca3176e7SBrian Feldman return NULL; 272511b41d2SMark Murray } 273511b41d2SMark Murray /* Skip cipher type and reserved data. */ 274511b41d2SMark Murray (void) buffer_get_char(&buffer); /* cipher type */ 275511b41d2SMark Murray (void) buffer_get_int(&buffer); /* reserved */ 276511b41d2SMark Murray 277511b41d2SMark Murray /* Read the public key from the buffer. */ 278a82e551fSDag-Erling Smørgrav (void) buffer_get_int(&buffer); 279ca3176e7SBrian Feldman pub = key_new(KEY_RSA1); 280ca3176e7SBrian Feldman buffer_get_bignum(&buffer, pub->rsa->n); 281ca3176e7SBrian Feldman buffer_get_bignum(&buffer, pub->rsa->e); 282ca3176e7SBrian Feldman if (commentp) 283ca3176e7SBrian Feldman *commentp = buffer_get_string(&buffer, NULL); 284511b41d2SMark Murray /* The encrypted private part is not parsed by this function. */ 285511b41d2SMark Murray 286511b41d2SMark Murray buffer_free(&buffer); 287ca3176e7SBrian Feldman return pub; 288511b41d2SMark Murray } 289511b41d2SMark Murray 290ca3176e7SBrian Feldman /* load public key from private-key file, works only for SSH v1 */ 291ca3176e7SBrian Feldman Key * 292ca3176e7SBrian Feldman key_load_public_type(int type, const char *filename, char **commentp) 293e8aafc91SKris Kennaway { 294ca3176e7SBrian Feldman Key *pub; 295ca3176e7SBrian Feldman int fd; 296ca3176e7SBrian Feldman 297ca3176e7SBrian Feldman if (type == KEY_RSA1) { 298ca3176e7SBrian Feldman fd = open(filename, O_RDONLY); 299ca3176e7SBrian Feldman if (fd < 0) 300ca3176e7SBrian Feldman return NULL; 301ca3176e7SBrian Feldman pub = key_load_public_rsa1(fd, filename, commentp); 302ca3176e7SBrian Feldman close(fd); 303ca3176e7SBrian Feldman return pub; 304e8aafc91SKris Kennaway } 305ca3176e7SBrian Feldman return NULL; 306e8aafc91SKris Kennaway } 307e8aafc91SKris Kennaway 308511b41d2SMark Murray /* 309511b41d2SMark Murray * Loads the private key from the file. Returns 0 if an error is encountered 310511b41d2SMark Murray * (file does not exist or is not readable, or passphrase is bad). This 311511b41d2SMark Murray * initializes the private key. 312511b41d2SMark Murray * Assumes we are called under uid of the owner of the file. 313511b41d2SMark Murray */ 314511b41d2SMark Murray 315af12a3e7SDag-Erling Smørgrav static Key * 316ca3176e7SBrian Feldman key_load_private_rsa1(int fd, const char *filename, const char *passphrase, 317ca3176e7SBrian Feldman char **commentp) 318511b41d2SMark Murray { 319e8aafc91SKris Kennaway int i, check1, check2, cipher_type; 320511b41d2SMark Murray off_t len; 321511b41d2SMark Murray Buffer buffer, decrypted; 322af12a3e7SDag-Erling Smørgrav u_char *cp; 32309958426SBrian Feldman CipherContext ciphercontext; 32409958426SBrian Feldman Cipher *cipher; 325ca3176e7SBrian Feldman Key *prv = NULL; 326e73e9afaSDag-Erling Smørgrav struct stat st; 327511b41d2SMark Murray 328e73e9afaSDag-Erling Smørgrav if (fstat(fd, &st) < 0) { 329e73e9afaSDag-Erling Smørgrav error("fstat for key file %.200s failed: %.100s", 330e73e9afaSDag-Erling Smørgrav filename, strerror(errno)); 331e73e9afaSDag-Erling Smørgrav close(fd); 332e73e9afaSDag-Erling Smørgrav return NULL; 333e73e9afaSDag-Erling Smørgrav } 334e73e9afaSDag-Erling Smørgrav len = st.st_size; 335511b41d2SMark Murray 336511b41d2SMark Murray buffer_init(&buffer); 337af12a3e7SDag-Erling Smørgrav cp = buffer_append_space(&buffer, len); 338511b41d2SMark Murray 339511b41d2SMark Murray if (read(fd, cp, (size_t) len) != (size_t) len) { 340511b41d2SMark Murray debug("Read from key file %.200s failed: %.100s", filename, 341511b41d2SMark Murray strerror(errno)); 342511b41d2SMark Murray buffer_free(&buffer); 343511b41d2SMark Murray close(fd); 344ca3176e7SBrian Feldman return NULL; 345511b41d2SMark Murray } 346511b41d2SMark Murray 347ca3176e7SBrian Feldman /* Check that it is at least big enough to contain the ID string. */ 348ca3176e7SBrian Feldman if (len < sizeof(authfile_id_string)) { 349af12a3e7SDag-Erling Smørgrav debug3("Not a RSA1 key file %.200s.", filename); 350511b41d2SMark Murray buffer_free(&buffer); 351ca3176e7SBrian Feldman close(fd); 352ca3176e7SBrian Feldman return NULL; 353511b41d2SMark Murray } 354511b41d2SMark Murray /* 355511b41d2SMark Murray * Make sure it begins with the id string. Consume the id string 356511b41d2SMark Murray * from the buffer. 357511b41d2SMark Murray */ 358ca3176e7SBrian Feldman for (i = 0; i < sizeof(authfile_id_string); i++) 359ca3176e7SBrian Feldman if (buffer_get_char(&buffer) != authfile_id_string[i]) { 360af12a3e7SDag-Erling Smørgrav debug3("Not a RSA1 key file %.200s.", filename); 361511b41d2SMark Murray buffer_free(&buffer); 362ca3176e7SBrian Feldman close(fd); 363ca3176e7SBrian Feldman return NULL; 364511b41d2SMark Murray } 365ca3176e7SBrian Feldman 366511b41d2SMark Murray /* Read cipher type. */ 367511b41d2SMark Murray cipher_type = buffer_get_char(&buffer); 368511b41d2SMark Murray (void) buffer_get_int(&buffer); /* Reserved data. */ 369511b41d2SMark Murray 370511b41d2SMark Murray /* Read the public key from the buffer. */ 371a82e551fSDag-Erling Smørgrav (void) buffer_get_int(&buffer); 372ca3176e7SBrian Feldman prv = key_new_private(KEY_RSA1); 373ca3176e7SBrian Feldman 374ca3176e7SBrian Feldman buffer_get_bignum(&buffer, prv->rsa->n); 375ca3176e7SBrian Feldman buffer_get_bignum(&buffer, prv->rsa->e); 376ca3176e7SBrian Feldman if (commentp) 377ca3176e7SBrian Feldman *commentp = buffer_get_string(&buffer, NULL); 378511b41d2SMark Murray else 379511b41d2SMark Murray xfree(buffer_get_string(&buffer, NULL)); 380511b41d2SMark Murray 381511b41d2SMark Murray /* Check that it is a supported cipher. */ 38209958426SBrian Feldman cipher = cipher_by_number(cipher_type); 38309958426SBrian Feldman if (cipher == NULL) { 38409958426SBrian Feldman debug("Unsupported cipher %d used in key file %.200s.", 38509958426SBrian Feldman cipher_type, filename); 386511b41d2SMark Murray buffer_free(&buffer); 387511b41d2SMark Murray goto fail; 388511b41d2SMark Murray } 389511b41d2SMark Murray /* Initialize space for decrypted data. */ 390511b41d2SMark Murray buffer_init(&decrypted); 391af12a3e7SDag-Erling Smørgrav cp = buffer_append_space(&decrypted, buffer_len(&buffer)); 392511b41d2SMark Murray 393511b41d2SMark Murray /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ 394af12a3e7SDag-Erling Smørgrav cipher_set_key_string(&ciphercontext, cipher, passphrase, 395af12a3e7SDag-Erling Smørgrav CIPHER_DECRYPT); 396af12a3e7SDag-Erling Smørgrav cipher_crypt(&ciphercontext, cp, 397af12a3e7SDag-Erling Smørgrav buffer_ptr(&buffer), buffer_len(&buffer)); 398af12a3e7SDag-Erling Smørgrav cipher_cleanup(&ciphercontext); 39909958426SBrian Feldman memset(&ciphercontext, 0, sizeof(ciphercontext)); 400511b41d2SMark Murray buffer_free(&buffer); 401511b41d2SMark Murray 402511b41d2SMark Murray check1 = buffer_get_char(&decrypted); 403511b41d2SMark Murray check2 = buffer_get_char(&decrypted); 404511b41d2SMark Murray if (check1 != buffer_get_char(&decrypted) || 405511b41d2SMark Murray check2 != buffer_get_char(&decrypted)) { 406511b41d2SMark Murray if (strcmp(passphrase, "") != 0) 407ca3176e7SBrian Feldman debug("Bad passphrase supplied for key file %.200s.", 408ca3176e7SBrian Feldman filename); 409511b41d2SMark Murray /* Bad passphrase. */ 410511b41d2SMark Murray buffer_free(&decrypted); 411ca3176e7SBrian Feldman goto fail; 412511b41d2SMark Murray } 413511b41d2SMark Murray /* Read the rest of the private key. */ 414ca3176e7SBrian Feldman buffer_get_bignum(&decrypted, prv->rsa->d); 415ca3176e7SBrian Feldman buffer_get_bignum(&decrypted, prv->rsa->iqmp); /* u */ 416ca3176e7SBrian Feldman /* in SSL and SSH v1 p and q are exchanged */ 417ca3176e7SBrian Feldman buffer_get_bignum(&decrypted, prv->rsa->q); /* p */ 418ca3176e7SBrian Feldman buffer_get_bignum(&decrypted, prv->rsa->p); /* q */ 419511b41d2SMark Murray 420ca3176e7SBrian Feldman /* calculate p-1 and q-1 */ 421af12a3e7SDag-Erling Smørgrav rsa_generate_additional_parameters(prv->rsa); 422511b41d2SMark Murray 423511b41d2SMark Murray buffer_free(&decrypted); 424e73e9afaSDag-Erling Smørgrav 425e73e9afaSDag-Erling Smørgrav /* enable blinding */ 426e73e9afaSDag-Erling Smørgrav if (RSA_blinding_on(prv->rsa, NULL) != 1) { 427e73e9afaSDag-Erling Smørgrav error("key_load_private_rsa1: RSA_blinding_on failed"); 428e73e9afaSDag-Erling Smørgrav goto fail; 429e73e9afaSDag-Erling Smørgrav } 430ca3176e7SBrian Feldman close(fd); 431ca3176e7SBrian Feldman return prv; 432511b41d2SMark Murray 433ca3176e7SBrian Feldman fail: 434ca3176e7SBrian Feldman if (commentp) 435ca3176e7SBrian Feldman xfree(*commentp); 436ca3176e7SBrian Feldman close(fd); 437ca3176e7SBrian Feldman key_free(prv); 438ca3176e7SBrian Feldman return NULL; 439511b41d2SMark Murray } 440e8aafc91SKris Kennaway 44180628bacSDag-Erling Smørgrav Key * 442ca3176e7SBrian Feldman key_load_private_pem(int fd, int type, const char *passphrase, 443ca3176e7SBrian Feldman char **commentp) 444e8aafc91SKris Kennaway { 445e8aafc91SKris Kennaway FILE *fp; 446ca3176e7SBrian Feldman EVP_PKEY *pk = NULL; 447ca3176e7SBrian Feldman Key *prv = NULL; 448ca3176e7SBrian Feldman char *name = "<no key>"; 449e8aafc91SKris Kennaway 450e8aafc91SKris Kennaway fp = fdopen(fd, "r"); 451e8aafc91SKris Kennaway if (fp == NULL) { 452ca3176e7SBrian Feldman error("fdopen failed: %s", strerror(errno)); 453ca3176e7SBrian Feldman close(fd); 454ca3176e7SBrian Feldman return NULL; 455e8aafc91SKris Kennaway } 456ca3176e7SBrian Feldman pk = PEM_read_PrivateKey(fp, NULL, NULL, (char *)passphrase); 457ca3176e7SBrian Feldman if (pk == NULL) { 458ca3176e7SBrian Feldman debug("PEM_read_PrivateKey failed"); 459ca3176e7SBrian Feldman (void)ERR_get_error(); 460ca3176e7SBrian Feldman } else if (pk->type == EVP_PKEY_RSA && 461ca3176e7SBrian Feldman (type == KEY_UNSPEC||type==KEY_RSA)) { 462ca3176e7SBrian Feldman prv = key_new(KEY_UNSPEC); 463ca3176e7SBrian Feldman prv->rsa = EVP_PKEY_get1_RSA(pk); 464ca3176e7SBrian Feldman prv->type = KEY_RSA; 465ca3176e7SBrian Feldman name = "rsa w/o comment"; 466ca3176e7SBrian Feldman #ifdef DEBUG_PK 467ca3176e7SBrian Feldman RSA_print_fp(stderr, prv->rsa, 8); 468e8aafc91SKris Kennaway #endif 469e73e9afaSDag-Erling Smørgrav if (RSA_blinding_on(prv->rsa, NULL) != 1) { 470e73e9afaSDag-Erling Smørgrav error("key_load_private_pem: RSA_blinding_on failed"); 471e73e9afaSDag-Erling Smørgrav key_free(prv); 472e73e9afaSDag-Erling Smørgrav prv = NULL; 473e73e9afaSDag-Erling Smørgrav } 474ca3176e7SBrian Feldman } else if (pk->type == EVP_PKEY_DSA && 475ca3176e7SBrian Feldman (type == KEY_UNSPEC||type==KEY_DSA)) { 476ca3176e7SBrian Feldman prv = key_new(KEY_UNSPEC); 477ca3176e7SBrian Feldman prv->dsa = EVP_PKEY_get1_DSA(pk); 478ca3176e7SBrian Feldman prv->type = KEY_DSA; 479ca3176e7SBrian Feldman name = "dsa w/o comment"; 480ca3176e7SBrian Feldman #ifdef DEBUG_PK 481ca3176e7SBrian Feldman DSA_print_fp(stderr, prv->dsa, 8); 482ca3176e7SBrian Feldman #endif 483ca3176e7SBrian Feldman } else { 484ca3176e7SBrian Feldman error("PEM_read_PrivateKey: mismatch or " 485ca3176e7SBrian Feldman "unknown EVP_PKEY save_type %d", pk->save_type); 486ca3176e7SBrian Feldman } 487ca3176e7SBrian Feldman fclose(fp); 488ca3176e7SBrian Feldman if (pk != NULL) 489ca3176e7SBrian Feldman EVP_PKEY_free(pk); 490ca3176e7SBrian Feldman if (prv != NULL && commentp) 491ca3176e7SBrian Feldman *commentp = xstrdup(name); 492ca3176e7SBrian Feldman debug("read PEM private key done: type %s", 493ca3176e7SBrian Feldman prv ? key_type(prv) : "<unknown>"); 494ca3176e7SBrian Feldman return prv; 495e8aafc91SKris Kennaway } 496e8aafc91SKris Kennaway 497af12a3e7SDag-Erling Smørgrav static int 498ca3176e7SBrian Feldman key_perm_ok(int fd, const char *filename) 499e8aafc91SKris Kennaway { 500e8aafc91SKris Kennaway struct stat st; 501e8aafc91SKris Kennaway 502af12a3e7SDag-Erling Smørgrav if (fstat(fd, &st) < 0) 503af12a3e7SDag-Erling Smørgrav return 0; 504af12a3e7SDag-Erling Smørgrav /* 505af12a3e7SDag-Erling Smørgrav * if a key owned by the user is accessed, then we check the 506af12a3e7SDag-Erling Smørgrav * permissions of the file. if the key owned by a different user, 507af12a3e7SDag-Erling Smørgrav * then we don't care. 508af12a3e7SDag-Erling Smørgrav */ 509989dd127SDag-Erling Smørgrav #ifdef HAVE_CYGWIN 510989dd127SDag-Erling Smørgrav if (check_ntsec(filename)) 511989dd127SDag-Erling Smørgrav #endif 512af12a3e7SDag-Erling Smørgrav if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) { 513e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 514e8aafc91SKris Kennaway error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); 515e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 516af12a3e7SDag-Erling Smørgrav error("Permissions 0%3.3o for '%s' are too open.", 517cf2b5f3bSDag-Erling Smørgrav (u_int)st.st_mode & 0777, filename); 518e8aafc91SKris Kennaway error("It is recommended that your private key files are NOT accessible by others."); 519ca3176e7SBrian Feldman error("This private key will be ignored."); 520e8aafc91SKris Kennaway return 0; 521e8aafc91SKris Kennaway } 522ca3176e7SBrian Feldman return 1; 523e8aafc91SKris Kennaway } 524ca3176e7SBrian Feldman 525ca3176e7SBrian Feldman Key * 526ca3176e7SBrian Feldman key_load_private_type(int type, const char *filename, const char *passphrase, 527ca3176e7SBrian Feldman char **commentp) 528ca3176e7SBrian Feldman { 529ca3176e7SBrian Feldman int fd; 530ca3176e7SBrian Feldman 531ca3176e7SBrian Feldman fd = open(filename, O_RDONLY); 532ca3176e7SBrian Feldman if (fd < 0) 533ca3176e7SBrian Feldman return NULL; 534ca3176e7SBrian Feldman if (!key_perm_ok(fd, filename)) { 535ca3176e7SBrian Feldman error("bad permissions: ignore key: %s", filename); 536ca3176e7SBrian Feldman close(fd); 537ca3176e7SBrian Feldman return NULL; 538e8aafc91SKris Kennaway } 539ca3176e7SBrian Feldman switch (type) { 540ca3176e7SBrian Feldman case KEY_RSA1: 541ca3176e7SBrian Feldman return key_load_private_rsa1(fd, filename, passphrase, 542ca3176e7SBrian Feldman commentp); 543ca3176e7SBrian Feldman /* closes fd */ 544e8aafc91SKris Kennaway break; 545e8aafc91SKris Kennaway case KEY_DSA: 546ca3176e7SBrian Feldman case KEY_RSA: 547ca3176e7SBrian Feldman case KEY_UNSPEC: 548ca3176e7SBrian Feldman return key_load_private_pem(fd, type, passphrase, commentp); 549ca3176e7SBrian Feldman /* closes fd */ 550ca3176e7SBrian Feldman break; 551e8aafc91SKris Kennaway default: 552ca3176e7SBrian Feldman close(fd); 553e8aafc91SKris Kennaway break; 554e8aafc91SKris Kennaway } 555ca3176e7SBrian Feldman return NULL; 556ca3176e7SBrian Feldman } 557ca3176e7SBrian Feldman 558ca3176e7SBrian Feldman Key * 559ca3176e7SBrian Feldman key_load_private(const char *filename, const char *passphrase, 560ca3176e7SBrian Feldman char **commentp) 561ca3176e7SBrian Feldman { 562af12a3e7SDag-Erling Smørgrav Key *pub, *prv; 563ca3176e7SBrian Feldman int fd; 564ca3176e7SBrian Feldman 565ca3176e7SBrian Feldman fd = open(filename, O_RDONLY); 566ca3176e7SBrian Feldman if (fd < 0) 567ca3176e7SBrian Feldman return NULL; 568ca3176e7SBrian Feldman if (!key_perm_ok(fd, filename)) { 569ca3176e7SBrian Feldman error("bad permissions: ignore key: %s", filename); 570e8aafc91SKris Kennaway close(fd); 571ca3176e7SBrian Feldman return NULL; 572ca3176e7SBrian Feldman } 573ca3176e7SBrian Feldman pub = key_load_public_rsa1(fd, filename, commentp); 574ca3176e7SBrian Feldman lseek(fd, (off_t) 0, SEEK_SET); /* rewind */ 575ca3176e7SBrian Feldman if (pub == NULL) { 576ca3176e7SBrian Feldman /* closes fd */ 577af12a3e7SDag-Erling Smørgrav prv = key_load_private_pem(fd, KEY_UNSPEC, passphrase, NULL); 578af12a3e7SDag-Erling Smørgrav /* use the filename as a comment for PEM */ 579af12a3e7SDag-Erling Smørgrav if (commentp && prv) 580af12a3e7SDag-Erling Smørgrav *commentp = xstrdup(filename); 581ca3176e7SBrian Feldman } else { 582ca3176e7SBrian Feldman /* it's a SSH v1 key if the public key part is readable */ 583ca3176e7SBrian Feldman key_free(pub); 584ca3176e7SBrian Feldman /* closes fd */ 585af12a3e7SDag-Erling Smørgrav prv = key_load_private_rsa1(fd, filename, passphrase, NULL); 586ca3176e7SBrian Feldman } 587af12a3e7SDag-Erling Smørgrav return prv; 588e8aafc91SKris Kennaway } 589c2d3a559SKris Kennaway 590af12a3e7SDag-Erling Smørgrav static int 591ca3176e7SBrian Feldman key_try_load_public(Key *k, const char *filename, char **commentp) 592c2d3a559SKris Kennaway { 593c2d3a559SKris Kennaway FILE *f; 594ca3176e7SBrian Feldman char line[4096]; 595c2d3a559SKris Kennaway char *cp; 596c2d3a559SKris Kennaway 597c2d3a559SKris Kennaway f = fopen(filename, "r"); 598c2d3a559SKris Kennaway if (f != NULL) { 599c2d3a559SKris Kennaway while (fgets(line, sizeof(line), f)) { 600c2d3a559SKris Kennaway line[sizeof(line)-1] = '\0'; 601c2d3a559SKris Kennaway cp = line; 602c2d3a559SKris Kennaway switch (*cp) { 603c2d3a559SKris Kennaway case '#': 604c2d3a559SKris Kennaway case '\n': 605c2d3a559SKris Kennaway case '\0': 606c2d3a559SKris Kennaway continue; 607c2d3a559SKris Kennaway } 608c2d3a559SKris Kennaway /* Skip leading whitespace. */ 609c2d3a559SKris Kennaway for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) 610c2d3a559SKris Kennaway ; 611c2d3a559SKris Kennaway if (*cp) { 612ca3176e7SBrian Feldman if (key_read(k, &cp) == 1) { 613c2d3a559SKris Kennaway if (commentp) 614c2d3a559SKris Kennaway *commentp=xstrdup(filename); 615c2d3a559SKris Kennaway fclose(f); 616c2d3a559SKris Kennaway return 1; 617c2d3a559SKris Kennaway } 618c2d3a559SKris Kennaway } 619c2d3a559SKris Kennaway } 620c2d3a559SKris Kennaway fclose(f); 621c2d3a559SKris Kennaway } 622c2d3a559SKris Kennaway return 0; 623c2d3a559SKris Kennaway } 624c2d3a559SKris Kennaway 625ca3176e7SBrian Feldman /* load public key from ssh v1 private or any pubkey file */ 626ca3176e7SBrian Feldman Key * 627ca3176e7SBrian Feldman key_load_public(const char *filename, char **commentp) 628c2d3a559SKris Kennaway { 629ca3176e7SBrian Feldman Key *pub; 630ca3176e7SBrian Feldman char file[MAXPATHLEN]; 631c2d3a559SKris Kennaway 632cf2b5f3bSDag-Erling Smørgrav /* try rsa1 private key */ 633ca3176e7SBrian Feldman pub = key_load_public_type(KEY_RSA1, filename, commentp); 634ca3176e7SBrian Feldman if (pub != NULL) 635ca3176e7SBrian Feldman return pub; 636cf2b5f3bSDag-Erling Smørgrav 637cf2b5f3bSDag-Erling Smørgrav /* try rsa1 public key */ 638cf2b5f3bSDag-Erling Smørgrav pub = key_new(KEY_RSA1); 639cf2b5f3bSDag-Erling Smørgrav if (key_try_load_public(pub, filename, commentp) == 1) 640cf2b5f3bSDag-Erling Smørgrav return pub; 641cf2b5f3bSDag-Erling Smørgrav key_free(pub); 642cf2b5f3bSDag-Erling Smørgrav 643cf2b5f3bSDag-Erling Smørgrav /* try ssh2 public key */ 644ca3176e7SBrian Feldman pub = key_new(KEY_UNSPEC); 645ca3176e7SBrian Feldman if (key_try_load_public(pub, filename, commentp) == 1) 646ca3176e7SBrian Feldman return pub; 647ca3176e7SBrian Feldman if ((strlcpy(file, filename, sizeof file) < sizeof(file)) && 648ca3176e7SBrian Feldman (strlcat(file, ".pub", sizeof file) < sizeof(file)) && 649ca3176e7SBrian Feldman (key_try_load_public(pub, file, commentp) == 1)) 650ca3176e7SBrian Feldman return pub; 651ca3176e7SBrian Feldman key_free(pub); 652ca3176e7SBrian Feldman return NULL; 653c2d3a559SKris Kennaway } 654