=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/authfile.c,v retrieving revision 1.16 retrieving revision 1.16.2.3 diff -u -r1.16 -r1.16.2.3 --- src/usr.bin/ssh/authfile.c 2000/04/26 21:28:32 1.16 +++ src/usr.bin/ssh/authfile.c 2001/03/12 15:44:08 1.16.2.3 @@ -1,37 +1,59 @@ /* - * - * authfile.c - * * Author: Tatu Ylonen - * * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved - * - * Created: Mon Mar 27 03:52:05 1995 ylo - * * 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("$Id: authfile.c,v 1.16 2000/04/26 21:28:32 markus Exp $"); +RCSID("$OpenBSD: authfile.c,v 1.16.2.3 2001/03/12 15:44:08 jason Exp $"); -#include -#include -#include -#include +#include #include +#include +#include "cipher.h" #include "xmalloc.h" #include "buffer.h" #include "bufaux.h" -#include "cipher.h" -#include "ssh.h" #include "key.h" +#include "ssh.h" +#include "log.h" +#include "authfile.h" /* Version identification string for identity files. */ -#define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n" +static const char authfile_id_string[] = + "SSH PRIVATE KEY FILE FORMAT 1.1\n"; /* * Saves the authentication (private) key in a file, encrypting it with @@ -41,14 +63,14 @@ */ int -save_private_key_rsa(const char *filename, const char *passphrase, +save_private_key_rsa1(const char *filename, const char *passphrase, RSA *key, const char *comment) { Buffer buffer, encrypted; char buf[100], *cp; int fd, i; - CipherContext cipher; - int cipher_type; + CipherContext ciphercontext; + Cipher *cipher; u_int32_t rand; /* @@ -56,9 +78,11 @@ * to another cipher; otherwise use SSH_AUTHFILE_CIPHER. */ if (strcmp(passphrase, "") == 0) - cipher_type = SSH_CIPHER_NONE; + cipher = cipher_by_number(SSH_CIPHER_NONE); else - cipher_type = SSH_AUTHFILE_CIPHER; + 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); @@ -89,13 +113,12 @@ buffer_init(&encrypted); /* First store keyfile id string. */ - cp = AUTHFILE_ID_STRING; - for (i = 0; cp[i]; i++) - buffer_put_char(&encrypted, cp[i]); + for (i = 0; authfile_id_string[i]; i++) + buffer_put_char(&encrypted, authfile_id_string[i]); buffer_put_char(&encrypted, 0); /* Store cipher type. */ - buffer_put_char(&encrypted, 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. */ @@ -107,11 +130,10 @@ /* Allocate space for the private part of the key in the buffer. */ buffer_append_space(&encrypted, &cp, buffer_len(&buffer)); - cipher_set_key_string(&cipher, cipher_type, passphrase); - cipher_encrypt(&cipher, (unsigned char *) cp, - (unsigned char *) buffer_ptr(&buffer), - buffer_len(&buffer)); - memset(&cipher, 0, sizeof(cipher)); + cipher_set_key_string(&ciphercontext, cipher, passphrase); + cipher_encrypt(&ciphercontext, (u_char *) cp, + (u_char *) buffer_ptr(&buffer), buffer_len(&buffer)); + memset(&ciphercontext, 0, sizeof(ciphercontext)); /* Destroy temporary data. */ memset(buf, 0, sizeof(buf)); @@ -126,7 +148,7 @@ strerror(errno)); buffer_free(&encrypted); close(fd); - remove(filename); + unlink(filename); return 0; } close(fd); @@ -134,16 +156,17 @@ return 1; } -/* save DSA key in OpenSSL PEM format */ - +/* save SSH2 key in OpenSSL PEM format */ int -save_private_key_dsa(const char *filename, const char *passphrase, - DSA *dsa, const char *comment) +save_private_key_ssh2(const char *filename, const char *_passphrase, + Key *key, const char *comment) { FILE *fp; int fd; - int success = 1; - int len = strlen(passphrase); + int success = 0; + int len = strlen(_passphrase); + char *passphrase = (len > 0) ? (char *)_passphrase : NULL; + EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL; if (len > 0 && len <= 4) { error("passphrase too short: %d bytes", len); @@ -161,14 +184,15 @@ 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; + switch (key->type) { + case KEY_DSA: + success = PEM_write_DSAPrivateKey(fp, key->dsa, + cipher, passphrase, len, NULL, NULL); + break; + case KEY_RSA: + success = PEM_write_RSAPrivateKey(fp, key->rsa, + cipher, passphrase, len, NULL, NULL); + break; } fclose(fp); return success; @@ -179,11 +203,12 @@ const char *comment) { switch (key->type) { - case KEY_RSA: - return save_private_key_rsa(filename, passphrase, key->rsa, comment); + case KEY_RSA1: + return save_private_key_rsa1(filename, passphrase, key->rsa, comment); break; case KEY_DSA: - return save_private_key_dsa(filename, passphrase, key->dsa, comment); + case KEY_RSA: + return save_private_key_ssh2(filename, passphrase, key, comment); break; default: break; @@ -223,9 +248,9 @@ } 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); + /* Check that it is at least big enough to contain the ID string. */ + if (len < sizeof(authfile_id_string)) { + debug3("Bad RSA1 key file %.200s.", filename); buffer_free(&buffer); return 0; } @@ -233,9 +258,9 @@ * 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); + for (i = 0; i < sizeof(authfile_id_string); i++) + if (buffer_get_char(&buffer) != authfile_id_string[i]) { + debug3("Bad RSA1 key file %.200s.", filename); buffer_free(&buffer); return 0; } @@ -262,14 +287,16 @@ 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: + case KEY_RSA1: return load_public_key_rsa(filename, key->rsa, comment_return); break; case KEY_DSA: + case KEY_RSA: default: break; } @@ -284,14 +311,15 @@ */ int -load_private_key_rsa(int fd, const char *filename, +load_private_key_rsa1(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 cipher; + CipherContext ciphercontext; + Cipher *cipher; BN_CTX *ctx; BIGNUM *aux; @@ -303,16 +331,16 @@ if (read(fd, cp, (size_t) len) != (size_t) len) { debug("Read from key file %.200s failed: %.100s", filename, - strerror(errno)); + 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); + /* Check that it is at least big enough to contain the ID string. */ + if (len < sizeof(authfile_id_string)) { + debug3("Bad RSA1 key file %.200s.", filename); buffer_free(&buffer); return 0; } @@ -320,9 +348,9 @@ * 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); + for (i = 0; i < sizeof(authfile_id_string); i++) + if (buffer_get_char(&buffer) != authfile_id_string[i]) { + debug3("Bad RSA1 key file %.200s.", filename); buffer_free(&buffer); return 0; } @@ -342,10 +370,10 @@ xfree(buffer_get_string(&buffer, NULL)); /* Check that it is a supported cipher. */ - if (((cipher_mask1() | SSH_CIPHER_NONE | SSH_AUTHFILE_CIPHER) & - (1 << cipher_type)) == 0) { - debug("Unsupported cipher %.100s used in key file %.200s.", - cipher_name(cipher_type), filename); + 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; } @@ -354,11 +382,10 @@ buffer_append_space(&decrypted, &cp, buffer_len(&buffer)); /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ - cipher_set_key_string(&cipher, cipher_type, passphrase); - cipher_decrypt(&cipher, (unsigned char *) cp, - (unsigned char *) buffer_ptr(&buffer), - buffer_len(&buffer)); - + cipher_set_key_string(&ciphercontext, cipher, passphrase); + cipher_decrypt(&ciphercontext, (u_char *) cp, + (u_char *) buffer_ptr(&buffer), buffer_len(&buffer)); + memset(&ciphercontext, 0, sizeof(ciphercontext)); buffer_free(&buffer); check1 = buffer_get_char(&decrypted); @@ -409,40 +436,59 @@ } int -load_private_key_dsa(int fd, const char *passphrase, Key *k, char **comment_return) +load_private_key_ssh2(int fd, const char *passphrase, Key *k, char **comment_return) { - DSA *dsa; - BIO *in; FILE *fp; + int success = 0; + EVP_PKEY *pk = NULL; + char *name = ""; - 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 { + pk = PEM_read_PrivateKey(fp, NULL, NULL, (char *)passphrase); + if (pk == NULL) { + debug("PEM_read_PrivateKey failed"); + (void)ERR_get_error(); + } else if (pk->type == EVP_PKEY_RSA) { + /* replace k->rsa with loaded key */ + if (k->type == KEY_RSA || k->type == KEY_UNSPEC) { + if (k->rsa != NULL) + RSA_free(k->rsa); + k->rsa = EVP_PKEY_get1_RSA(pk); + k->type = KEY_RSA; + name = "rsa w/o comment"; + success = 1; +#ifdef DEBUG_PK + RSA_print_fp(stderr, k->rsa, 8); +#endif + } + } else if (pk->type == EVP_PKEY_DSA) { /* replace k->dsa with loaded key */ - DSA_free(k->dsa); - k->dsa = dsa; + if (k->type == KEY_DSA || k->type == KEY_UNSPEC) { + if (k->dsa != NULL) + DSA_free(k->dsa); + k->dsa = EVP_PKEY_get1_DSA(pk); + k->type = KEY_DSA; + name = "dsa w/o comment"; +#ifdef DEBUG_PK + DSA_print_fp(stderr, k->dsa, 8); +#endif + success = 1; + } + } else { + error("PEM_read_PrivateKey: mismatch or " + "unknown EVP_PKEY save_type %d", pk->save_type); } - 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; + if (pk != NULL) + EVP_PKEY_free(pk); + if (success && comment_return) + *comment_return = xstrdup(name); + debug("read SSH2 private key done: name %s success %d", name, success); + return success; } int @@ -459,7 +505,7 @@ /* check owner and modes */ if (fstat(fd, &st) < 0 || - (st.st_uid != 0 && st.st_uid != getuid()) || + (st.st_uid != 0 && getuid() != 0 && st.st_uid != getuid()) || (st.st_mode & 077) != 0) { close(fd); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); @@ -471,7 +517,7 @@ return 0; } switch (key->type) { - case KEY_RSA: + case KEY_RSA1: if (key->rsa->e != NULL) { BN_clear_free(key->rsa->e); key->rsa->e = NULL; @@ -480,14 +526,68 @@ BN_clear_free(key->rsa->n); key->rsa->n = NULL; } - ret = load_private_key_rsa(fd, filename, passphrase, + ret = load_private_key_rsa1(fd, filename, passphrase, key->rsa, comment_return); break; case KEY_DSA: - ret = load_private_key_dsa(fd, passphrase, key, comment_return); + case KEY_RSA: + case KEY_UNSPEC: + ret = load_private_key_ssh2(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; + 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) { + if (key_read(k, &cp) == 1) { + 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; }