diff options
Diffstat (limited to 'crypto/openssh/authfile.c')
-rw-r--r-- | crypto/openssh/authfile.c | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/crypto/openssh/authfile.c b/crypto/openssh/authfile.c new file mode 100644 index 0000000..a2dc524 --- /dev/null +++ b/crypto/openssh/authfile.c @@ -0,0 +1,570 @@ +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * This file contains functions for reading and writing identity files, and + * for reading the passphrase from the user. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$OpenBSD: authfile.c,v 1.20 2000/10/11 20:27:23 markus Exp $"); +RCSID("$FreeBSD$"); + +#include <openssl/bn.h> +#include <openssl/dsa.h> +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/evp.h> + +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "ssh.h" +#include "key.h" + +/* Version identification string for identity files. */ +#define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n" + +/* + * Saves the authentication (private) key in a file, encrypting it with + * passphrase. The identification of the file (lowest 64 bits of n) will + * precede the key to provide identification of the key without needing a + * passphrase. + */ + +int +save_private_key_rsa(const char *filename, const char *passphrase, + RSA *key, const char *comment) +{ + Buffer buffer, encrypted; + char buf[100], *cp; + int fd, i; + CipherContext ciphercontext; + Cipher *cipher; + u_int32_t rand; + + /* + * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting + * to another cipher; otherwise use SSH_AUTHFILE_CIPHER. + */ + if (strcmp(passphrase, "") == 0) + cipher = cipher_by_number(SSH_CIPHER_NONE); + else + cipher = cipher_by_number(SSH_AUTHFILE_CIPHER); + if (cipher == NULL) + fatal("save_private_key_rsa: bad cipher"); + + /* This buffer is used to built the secret part of the private key. */ + buffer_init(&buffer); + + /* Put checkbytes for checking passphrase validity. */ + rand = arc4random(); + buf[0] = rand & 0xff; + buf[1] = (rand >> 8) & 0xff; + buf[2] = buf[0]; + buf[3] = buf[1]; + buffer_append(&buffer, buf, 4); + + /* + * Store the private key (n and e will not be stored because they + * will be stored in plain text, and storing them also in encrypted + * format would just give known plaintext). + */ + buffer_put_bignum(&buffer, key->d); + buffer_put_bignum(&buffer, key->iqmp); + buffer_put_bignum(&buffer, key->q); /* reverse from SSL p */ + buffer_put_bignum(&buffer, key->p); /* reverse from SSL q */ + + /* Pad the part to be encrypted until its size is a multiple of 8. */ + while (buffer_len(&buffer) % 8 != 0) + buffer_put_char(&buffer, 0); + + /* This buffer will be used to contain the data in the file. */ + buffer_init(&encrypted); + + /* First store keyfile id string. */ + cp = AUTHFILE_ID_STRING; + for (i = 0; cp[i]; i++) + buffer_put_char(&encrypted, cp[i]); + buffer_put_char(&encrypted, 0); + + /* Store cipher type. */ + buffer_put_char(&encrypted, cipher->number); + buffer_put_int(&encrypted, 0); /* For future extension */ + + /* Store public key. This will be in plain text. */ + buffer_put_int(&encrypted, BN_num_bits(key->n)); + buffer_put_bignum(&encrypted, key->n); + buffer_put_bignum(&encrypted, key->e); + buffer_put_string(&encrypted, comment, strlen(comment)); + + /* Allocate space for the private part of the key in the buffer. */ + buffer_append_space(&encrypted, &cp, buffer_len(&buffer)); + + cipher_set_key_string(&ciphercontext, cipher, passphrase); + cipher_encrypt(&ciphercontext, (unsigned char *) cp, + (unsigned char *) buffer_ptr(&buffer), buffer_len(&buffer)); + memset(&ciphercontext, 0, sizeof(ciphercontext)); + + /* Destroy temporary data. */ + memset(buf, 0, sizeof(buf)); + buffer_free(&buffer); + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) + return 0; + if (write(fd, buffer_ptr(&encrypted), buffer_len(&encrypted)) != + buffer_len(&encrypted)) { + debug("Write to key file %.200s failed: %.100s", filename, + strerror(errno)); + buffer_free(&encrypted); + close(fd); + remove(filename); + return 0; + } + close(fd); + buffer_free(&encrypted); + return 1; +} + +/* save DSA key in OpenSSL PEM format */ + +int +save_private_key_dsa(const char *filename, const char *passphrase, + DSA *dsa, const char *comment) +{ + FILE *fp; + int fd; + int success = 1; + int len = strlen(passphrase); + + if (len > 0 && len <= 4) { + error("passphrase too short: %d bytes", len); + errno = 0; + return 0; + } + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { + debug("open %s failed", filename); + return 0; + } + fp = fdopen(fd, "w"); + if (fp == NULL ) { + debug("fdopen %s failed", filename); + close(fd); + return 0; + } + if (len > 0) { + if (!PEM_write_DSAPrivateKey(fp, dsa, EVP_des_ede3_cbc(), + (char *)passphrase, strlen(passphrase), NULL, NULL)) + success = 0; + } else { + if (!PEM_write_DSAPrivateKey(fp, dsa, NULL, + NULL, 0, NULL, NULL)) + success = 0; + } + fclose(fp); + return success; +} + +int +save_private_key(const char *filename, const char *passphrase, Key *key, + const char *comment) +{ + switch (key->type) { + case KEY_RSA: + return save_private_key_rsa(filename, passphrase, key->rsa, comment); + break; + case KEY_DSA: + return save_private_key_dsa(filename, passphrase, key->dsa, comment); + break; + default: + break; + } + return 0; +} + +/* + * Loads the public part of the key file. Returns 0 if an error was + * encountered (the file does not exist or is not readable), and non-zero + * otherwise. + */ + +int +load_public_key_rsa(const char *filename, RSA * pub, char **comment_return) +{ + int fd, i; + off_t len; + Buffer buffer; + char *cp; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return 0; + len = lseek(fd, (off_t) 0, SEEK_END); + lseek(fd, (off_t) 0, SEEK_SET); + + buffer_init(&buffer); + buffer_append_space(&buffer, &cp, len); + + if (read(fd, cp, (size_t) len) != (size_t) len) { + debug("Read from key file %.200s failed: %.100s", filename, + strerror(errno)); + buffer_free(&buffer); + close(fd); + return 0; + } + close(fd); + + /* Check that it is at least big enought to contain the ID string. */ + if (len < strlen(AUTHFILE_ID_STRING) + 1) { + debug("Bad key file %.200s.", filename); + buffer_free(&buffer); + return 0; + } + /* + * Make sure it begins with the id string. Consume the id string + * from the buffer. + */ + for (i = 0; i < (unsigned int) strlen(AUTHFILE_ID_STRING) + 1; i++) + if (buffer_get_char(&buffer) != (u_char) AUTHFILE_ID_STRING[i]) { + debug("Bad key file %.200s.", filename); + buffer_free(&buffer); + return 0; + } + /* Skip cipher type and reserved data. */ + (void) buffer_get_char(&buffer); /* cipher type */ + (void) buffer_get_int(&buffer); /* reserved */ + + /* Read the public key from the buffer. */ + buffer_get_int(&buffer); + /* XXX alloc */ + if (pub->n == NULL) + pub->n = BN_new(); + buffer_get_bignum(&buffer, pub->n); + /* XXX alloc */ + if (pub->e == NULL) + pub->e = BN_new(); + buffer_get_bignum(&buffer, pub->e); + if (comment_return) + *comment_return = buffer_get_string(&buffer, NULL); + /* The encrypted private part is not parsed by this function. */ + + buffer_free(&buffer); + + return 1; +} + +/* load public key from private-key file */ +int +load_public_key(const char *filename, Key * key, char **comment_return) +{ + switch (key->type) { + case KEY_RSA: + return load_public_key_rsa(filename, key->rsa, comment_return); + break; + case KEY_DSA: + default: + break; + } + return 0; +} + +/* + * Loads the private key from the file. Returns 0 if an error is encountered + * (file does not exist or is not readable, or passphrase is bad). This + * initializes the private key. + * Assumes we are called under uid of the owner of the file. + */ + +int +load_private_key_rsa(int fd, const char *filename, + const char *passphrase, RSA * prv, char **comment_return) +{ + int i, check1, check2, cipher_type; + off_t len; + Buffer buffer, decrypted; + char *cp; + CipherContext ciphercontext; + Cipher *cipher; + BN_CTX *ctx; + BIGNUM *aux; + + len = lseek(fd, (off_t) 0, SEEK_END); + lseek(fd, (off_t) 0, SEEK_SET); + + buffer_init(&buffer); + buffer_append_space(&buffer, &cp, len); + + if (read(fd, cp, (size_t) len) != (size_t) len) { + debug("Read from key file %.200s failed: %.100s", filename, + strerror(errno)); + buffer_free(&buffer); + close(fd); + return 0; + } + close(fd); + + /* Check that it is at least big enought to contain the ID string. */ + if (len < strlen(AUTHFILE_ID_STRING) + 1) { + debug("Bad key file %.200s.", filename); + buffer_free(&buffer); + return 0; + } + /* + * Make sure it begins with the id string. Consume the id string + * from the buffer. + */ + for (i = 0; i < (unsigned int) strlen(AUTHFILE_ID_STRING) + 1; i++) + if (buffer_get_char(&buffer) != (unsigned char) AUTHFILE_ID_STRING[i]) { + debug("Bad key file %.200s.", filename); + buffer_free(&buffer); + return 0; + } + /* Read cipher type. */ + cipher_type = buffer_get_char(&buffer); + (void) buffer_get_int(&buffer); /* Reserved data. */ + + /* Read the public key from the buffer. */ + buffer_get_int(&buffer); + prv->n = BN_new(); + buffer_get_bignum(&buffer, prv->n); + prv->e = BN_new(); + buffer_get_bignum(&buffer, prv->e); + if (comment_return) + *comment_return = buffer_get_string(&buffer, NULL); + else + xfree(buffer_get_string(&buffer, NULL)); + + /* Check that it is a supported cipher. */ + cipher = cipher_by_number(cipher_type); + if (cipher == NULL) { + debug("Unsupported cipher %d used in key file %.200s.", + cipher_type, filename); + buffer_free(&buffer); + goto fail; + } + /* Initialize space for decrypted data. */ + buffer_init(&decrypted); + buffer_append_space(&decrypted, &cp, buffer_len(&buffer)); + + /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ + cipher_set_key_string(&ciphercontext, cipher, passphrase); + cipher_decrypt(&ciphercontext, (unsigned char *) cp, + (unsigned char *) buffer_ptr(&buffer), buffer_len(&buffer)); + memset(&ciphercontext, 0, sizeof(ciphercontext)); + buffer_free(&buffer); + + check1 = buffer_get_char(&decrypted); + check2 = buffer_get_char(&decrypted); + if (check1 != buffer_get_char(&decrypted) || + check2 != buffer_get_char(&decrypted)) { + if (strcmp(passphrase, "") != 0) + debug("Bad passphrase supplied for key file %.200s.", filename); + /* Bad passphrase. */ + buffer_free(&decrypted); +fail: + BN_clear_free(prv->n); + prv->n = NULL; + BN_clear_free(prv->e); + prv->e = NULL; + if (comment_return) + xfree(*comment_return); + return 0; + } + /* Read the rest of the private key. */ + prv->d = BN_new(); + buffer_get_bignum(&decrypted, prv->d); + prv->iqmp = BN_new(); + buffer_get_bignum(&decrypted, prv->iqmp); /* u */ + /* in SSL and SSH p and q are exchanged */ + prv->q = BN_new(); + buffer_get_bignum(&decrypted, prv->q); /* p */ + prv->p = BN_new(); + buffer_get_bignum(&decrypted, prv->p); /* q */ + + ctx = BN_CTX_new(); + aux = BN_new(); + + BN_sub(aux, prv->q, BN_value_one()); + prv->dmq1 = BN_new(); + BN_mod(prv->dmq1, prv->d, aux, ctx); + + BN_sub(aux, prv->p, BN_value_one()); + prv->dmp1 = BN_new(); + BN_mod(prv->dmp1, prv->d, aux, ctx); + + BN_clear_free(aux); + BN_CTX_free(ctx); + + buffer_free(&decrypted); + + return 1; +} + +int +load_private_key_dsa(int fd, const char *passphrase, Key *k, char **comment_return) +{ + DSA *dsa; + BIO *in; + FILE *fp; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + error("BIO_new failed"); + return 0; + } + fp = fdopen(fd, "r"); + if (fp == NULL) { + error("fdopen failed"); + return 0; + } + BIO_set_fp(in, fp, BIO_NOCLOSE); + dsa = PEM_read_bio_DSAPrivateKey(in, NULL, NULL, (char *)passphrase); + if (dsa == NULL) { + debug("PEM_read_bio_DSAPrivateKey failed"); + } else { + /* replace k->dsa with loaded key */ + DSA_free(k->dsa); + k->dsa = dsa; + } + BIO_free(in); + fclose(fp); + if (comment_return) + *comment_return = xstrdup("dsa w/o comment"); + debug("read DSA private key done"); +#ifdef DEBUG_DSS + DSA_print_fp(stderr, dsa, 8); +#endif + return dsa != NULL ? 1 : 0; +} + +int +load_private_key(const char *filename, const char *passphrase, Key *key, + char **comment_return) +{ + int fd; + int ret = 0; + struct stat st; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return 0; + + /* check owner and modes */ + if (fstat(fd, &st) < 0 || + (st.st_uid != 0 && st.st_uid != getuid()) || + (st.st_mode & 077) != 0) { + close(fd); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("Bad ownership or mode(0%3.3o) for '%s'.", + st.st_mode & 0777, filename); + error("It is recommended that your private key files are NOT accessible by others."); + return 0; + } + switch (key->type) { + case KEY_RSA: + if (key->rsa->e != NULL) { + BN_clear_free(key->rsa->e); + key->rsa->e = NULL; + } + if (key->rsa->n != NULL) { + BN_clear_free(key->rsa->n); + key->rsa->n = NULL; + } + ret = load_private_key_rsa(fd, filename, passphrase, + key->rsa, comment_return); + break; + case KEY_DSA: + ret = load_private_key_dsa(fd, passphrase, key, comment_return); + default: + break; + } + close(fd); + return ret; +} + +int +do_load_public_key(const char *filename, Key *k, char **commentp) +{ + FILE *f; + unsigned int bits; + char line[1024]; + char *cp; + + f = fopen(filename, "r"); + if (f != NULL) { + while (fgets(line, sizeof(line), f)) { + line[sizeof(line)-1] = '\0'; + cp = line; + switch(*cp){ + case '#': + case '\n': + case '\0': + continue; + } + /* Skip leading whitespace. */ + for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) + ; + if (*cp) { + bits = key_read(k, &cp); + if (bits != 0) { + if (commentp) + *commentp=xstrdup(filename); + fclose(f); + return 1; + } + } + } + fclose(f); + } + return 0; +} + +/* load public key from pubkey file */ +int +try_load_public_key(const char *filename, Key *k, char **commentp) +{ + char pub[MAXPATHLEN]; + + if (do_load_public_key(filename, k, commentp) == 1) + return 1; + if (strlcpy(pub, filename, sizeof pub) >= MAXPATHLEN) + return 0; + if (strlcat(pub, ".pub", sizeof pub) >= MAXPATHLEN) + return 0; + if (do_load_public_key(pub, k, commentp) == 1) + return 1; + return 0; +} |