From 814ccd3fa756865f9d24e6aadc91a1c7a2cef263 Mon Sep 17 00:00:00 2001 From: markus Date: Wed, 26 Apr 2000 20:56:29 +0000 Subject: [PATCH] add DSA pubkey auth and other SSH2 fixes. use ssh-keygen -[xX] for trading keys with the real and the original SSH, directly from the people who invented the SSH protocol. --- usr.bin/ssh/auth.c | 298 ++++++++++++++++++++++++++++------ usr.bin/ssh/authfile.c | 206 +++++++++++++++++++---- usr.bin/ssh/authfile.h | 36 +++++ usr.bin/ssh/compat.c | 3 +- usr.bin/ssh/dsa.c | 78 +++++---- usr.bin/ssh/dsa.h | 12 +- usr.bin/ssh/hostfile.c | 23 +-- usr.bin/ssh/key.c | 107 ++++++------ usr.bin/ssh/key.h | 3 +- usr.bin/ssh/lib/Makefile | 2 +- usr.bin/ssh/radix.c | 96 +---------- usr.bin/ssh/readconf.c | 43 ++++- usr.bin/ssh/readconf.h | 6 +- usr.bin/ssh/ssh-add.c | 37 +++-- usr.bin/ssh/ssh-keygen.c | 341 +++++++++++++++++++++++++++++---------- usr.bin/ssh/ssh.c | 67 +++++--- usr.bin/ssh/ssh.h | 35 +--- usr.bin/ssh/sshconnect.c | 267 ++++++++++++++++++++---------- usr.bin/ssh/sshd.c | 214 +++++++++++++++--------- usr.bin/ssh/uuencode.c | 117 ++++++++++++++ usr.bin/ssh/uuencode.h | 6 + 21 files changed, 1391 insertions(+), 606 deletions(-) create mode 100644 usr.bin/ssh/authfile.h create mode 100644 usr.bin/ssh/uuencode.c create mode 100644 usr.bin/ssh/uuencode.h diff --git a/usr.bin/ssh/auth.c b/usr.bin/ssh/auth.c index d20a4e3fe3e..7c88017e8d0 100644 --- a/usr.bin/ssh/auth.c +++ b/usr.bin/ssh/auth.c @@ -5,7 +5,11 @@ */ #include "includes.h" -RCSID("$OpenBSD: auth.c,v 1.4 2000/04/14 10:30:29 markus Exp $"); +RCSID("$OpenBSD: auth.c,v 1.5 2000/04/26 20:56:29 markus Exp $"); + +#include +#include +#include #include "xmalloc.h" #include "rsa.h" @@ -19,13 +23,17 @@ RCSID("$OpenBSD: auth.c,v 1.4 2000/04/14 10:30:29 markus Exp $"); #include "compat.h" #include "channels.h" #include "match.h" - #include "bufaux.h" #include "ssh2.h" #include "auth.h" #include "session.h" #include "dispatch.h" +#include "key.h" +#include "kex.h" +#include "dsa.h" +#include "uidswap.h" +#include "channels.h" /* import */ extern ServerOptions options; @@ -40,7 +48,7 @@ extern char *forced_command; * If the user's shell is not executable, false will be returned. * Otherwise true is returned. */ -static int +int allowed_user(struct passwd * pw) { struct stat st; @@ -110,6 +118,10 @@ allowed_user(struct passwd * pw) return 1; } +/* import */ +extern ServerOptions options; +extern char *forced_command; + /* * convert ssh auth msg type into description */ @@ -559,10 +571,25 @@ do_authentication() do_authenticated(pw); } +/* import */ +extern ServerOptions options; +extern unsigned char *session_id2; +extern int session_id2_len; + +/* protocol */ -void input_service_request(int type, int plen); -void input_userauth_request(int type, int plen); -void ssh2_pty_cleanup(void); +void input_service_request(int type, int plen); +void input_userauth_request(int type, int plen); +void protocol_error(int type, int plen); + +/* auth */ +int ssh2_auth_none(struct passwd *pw); +int ssh2_auth_password(struct passwd *pw); +int ssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen); + +/* helper */ +struct passwd* auth_set_user(char *u, char *s); +int user_dsa_key_allowed(struct passwd *pw, Key *key); typedef struct Authctxt Authctxt; struct Authctxt { @@ -574,11 +601,14 @@ struct Authctxt { static Authctxt *authctxt = NULL; static int userauth_success = 0; +/* set and get current user */ + struct passwd* auth_get_user(void) { return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL; } + struct passwd* auth_set_user(char *u, char *s) { @@ -615,7 +645,20 @@ auth_set_user(char *u, char *s) return auth_get_user(); } -static void +/* + * loop until userauth_success == TRUE + */ + +void +do_authentication2() +{ + dispatch_init(&protocol_error); + dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); + dispatch_run(DISPATCH_BLOCK, &userauth_success); + do_authenticated2(); +} + +void protocol_error(int type, int plen) { log("auth: protocol error: type %d plen %d", type, plen); @@ -624,6 +667,7 @@ protocol_error(int type, int plen) packet_send(); packet_write_wait(); } + void input_service_request(int type, int plen) { @@ -652,18 +696,22 @@ input_service_request(int type, int plen) } xfree(service); } + void input_userauth_request(int type, int plen) { static int try = 0; - unsigned int len; - int c, authenticated = 0; - char *user, *service, *method; + unsigned int len, rlen; + int authenticated = 0; + char *raw, *user, *service, *method; struct passwd *pw; if (++try == AUTH_FAIL_MAX) packet_disconnect("too many failed userauth_requests"); + raw = packet_get_raw(&rlen); + if (plen != rlen) + fatal("plen != rlen"); user = packet_get_string(&len); service = packet_get_string(&len); method = packet_get_string(&len); @@ -672,64 +720,216 @@ input_userauth_request(int type, int plen) /* XXX we only allow the ssh-connection service */ pw = auth_set_user(user, service); if (pw && strcmp(service, "ssh-connection")==0) { - if (strcmp(method, "none") == 0 && try == 1) { - packet_done(); - authenticated = auth_password(pw, ""); + if (strcmp(method, "none") == 0) { + authenticated = ssh2_auth_none(pw); } else if (strcmp(method, "password") == 0) { - char *password; - c = packet_get_char(); - if (c) - debug("password change not supported"); - password = packet_get_string(&len); - packet_done(); - authenticated = auth_password(pw, password); - memset(password, 0, len); - xfree(password); + authenticated = ssh2_auth_password(pw); } else if (strcmp(method, "publickey") == 0) { - /* XXX TODO */ - char *pkalg, *pkblob, *sig; - int have_sig = packet_get_char(); - pkalg = packet_get_string(&len); - pkblob = packet_get_string(&len); - if (have_sig) { - sig = packet_get_string(&len); - /* test for correct signature */ - packet_done(); - xfree(sig); - } else { - packet_done(); - /* test whether pkalg/pkblob are acceptable */ - } - xfree(pkalg); - xfree(pkblob); + authenticated = ssh2_auth_pubkey(pw, raw, rlen); } } /* XXX check if other auth methods are needed */ - if (authenticated) { + if (authenticated == 1) { + log("userauth success for %s method %s", user, method); /* turn off userauth */ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error); packet_start(SSH2_MSG_USERAUTH_SUCCESS); packet_send(); packet_write_wait(); - log("userauth success for %s", user); /* now we can break out */ userauth_success = 1; - } else { + } else if (authenticated == 0) { + log("userauth failure for %s method %s", user, method); packet_start(SSH2_MSG_USERAUTH_FAILURE); - packet_put_cstring("password"); - packet_put_char(0); /* partial success */ + packet_put_cstring("publickey,password"); /* XXX dynamic */ + packet_put_char(0); /* XXX partial success, unused */ packet_send(); packet_write_wait(); + } else { + log("userauth postponed for %s method %s", user, method); } xfree(service); xfree(user); xfree(method); } -void -do_authentication2() + +int +ssh2_auth_none(struct passwd *pw) { - dispatch_init(&protocol_error); - dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); - dispatch_run(DISPATCH_BLOCK, &userauth_success); - do_authenticated2(); + packet_done(); + return auth_password(pw, ""); +} +int +ssh2_auth_password(struct passwd *pw) +{ + char *password; + int authenticated = 0; + int change; + unsigned int len; + change = packet_get_char(); + if (change) + log("password change not supported"); + password = packet_get_string(&len); + packet_done(); + if (auth_password(pw, password)) + authenticated = 1; + memset(password, 0, len); + xfree(password); + return authenticated; +} + +int +ssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen) +{ + Buffer b; + Key *key; + char *pkalg, *pkblob, *sig; + unsigned int alen, blen, slen; + int have_sig; + int authenticated = 0; + + have_sig = packet_get_char(); + pkalg = packet_get_string(&alen); + if (strcmp(pkalg, KEX_DSS) != 0) { + xfree(pkalg); + log("bad pkalg %s", pkalg); /*XXX*/ + return 0; + } + pkblob = packet_get_string(&blen); + key = dsa_key_from_blob(pkblob, blen); + + if (have_sig && key != NULL) { + sig = packet_get_string(&slen); + packet_done(); + buffer_init(&b); + buffer_append(&b, session_id2, session_id2_len); + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + if (slen + 4 > rlen) + fatal("bad rlen/slen"); + buffer_append(&b, raw, rlen - slen - 4); +#ifdef DEBUG_DSS + buffer_dump(&b); +#endif + /* test for correct signature */ + if (user_dsa_key_allowed(pw, key) && + dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) + authenticated = 1; + buffer_clear(&b); + xfree(sig); + } else if (!have_sig && key != NULL) { + packet_done(); + debug("test key..."); + /* test whether pkalg/pkblob are acceptable */ + /* XXX fake reply and always send PK_OK ? */ + if (user_dsa_key_allowed(pw, key)) { + packet_start(SSH2_MSG_USERAUTH_PK_OK); + packet_put_string(pkalg, alen); + packet_put_string(pkblob, blen); + packet_send(); + packet_write_wait(); + authenticated = -1; + } + } + xfree(pkalg); + xfree(pkblob); + return authenticated; +} + +/* return 1 if user allows given key */ +int +user_dsa_key_allowed(struct passwd *pw, Key *key) +{ + char line[8192], file[1024]; + int found_key = 0; + unsigned int bits = -1; + FILE *f; + unsigned long linenum = 0; + struct stat st; + Key *found; + + /* Temporarily use the user's uid. */ + temporarily_use_uid(pw->pw_uid); + + /* The authorized keys. */ + snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, + SSH_USER_PERMITTED_KEYS2); + + /* Fail quietly if file does not exist */ + if (stat(file, &st) < 0) { + /* Restore the privileged uid. */ + restore_uid(); + return 0; + } + /* Open the file containing the authorized keys. */ + f = fopen(file, "r"); + if (!f) { + /* Restore the privileged uid. */ + restore_uid(); + packet_send_debug("Could not open %.900s for reading.", file); + packet_send_debug("If your home is on an NFS volume, it may need to be world-readable."); + return 0; + } + if (options.strict_modes) { + int fail = 0; + char buf[1024]; + /* Check open file in order to avoid open/stat races */ + if (fstat(fileno(f), &st) < 0 || + (st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0) { + snprintf(buf, sizeof buf, "DSA authentication refused for %.100s: " + "bad ownership or modes for '%s'.", pw->pw_name, file); + fail = 1; + } else { + /* Check path to SSH_USER_PERMITTED_KEYS */ + int i; + static const char *check[] = { + "", SSH_USER_DIR, NULL + }; + for (i = 0; check[i]; i++) { + snprintf(line, sizeof line, "%.500s/%.100s", + pw->pw_dir, check[i]); + if (stat(line, &st) < 0 || + (st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0) { + snprintf(buf, sizeof buf, + "DSA authentication refused for %.100s: " + "bad ownership or modes for '%s'.", + pw->pw_name, line); + fail = 1; + break; + } + } + } + if (fail) { + log(buf); + fclose(f); + restore_uid(); + return 0; + } + } + found_key = 0; + found = key_new(KEY_DSA); + + while (fgets(line, sizeof(line), f)) { + char *cp; + linenum++; + /* Skip leading whitespace, empty and comment lines. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '\n' || *cp == '#') + continue; + bits = key_read(found, &cp); + if (bits == 0) + continue; + if (key_equal(found, key)) { + found_key = 1; + debug("matching key found: file %s, line %ld", + file, linenum); + break; + } + } + restore_uid(); + fclose(f); + key_free(found); + return found_key; } diff --git a/usr.bin/ssh/authfile.c b/usr.bin/ssh/authfile.c index 82a0990917d..b6c4de38b4f 100644 --- a/usr.bin/ssh/authfile.c +++ b/usr.bin/ssh/authfile.c @@ -15,14 +15,20 @@ */ #include "includes.h" -RCSID("$Id: authfile.c,v 1.14 2000/04/14 10:30:30 markus Exp $"); +RCSID("$Id: authfile.c,v 1.15 2000/04/26 20:56:29 markus Exp $"); #include +#include +#include +#include +#include + #include "xmalloc.h" #include "buffer.h" #include "bufaux.h" #include "cipher.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" @@ -35,8 +41,8 @@ RCSID("$Id: authfile.c,v 1.14 2000/04/14 10:30:30 markus Exp $"); */ int -save_private_key(const char *filename, const char *passphrase, - RSA *key, const char *comment) +save_private_key_rsa(const char *filename, const char *passphrase, + RSA *key, const char *comment) { Buffer buffer, encrypted; char buf[100], *cp; @@ -128,6 +134,63 @@ save_private_key(const char *filename, const char *passphrase, 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 @@ -135,8 +198,7 @@ save_private_key(const char *filename, const char *passphrase, */ int -load_public_key(const char *filename, RSA * pub, - char **comment_return) +load_public_key_rsa(const char *filename, RSA * pub, char **comment_return) { int fd, i; off_t len; @@ -154,7 +216,7 @@ load_public_key(const char *filename, RSA * pub, 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; @@ -183,9 +245,13 @@ load_public_key(const char *filename, RSA * pub, /* Read the public key from the buffer. */ buffer_get_int(&buffer); - pub->n = BN_new(); + /* XXX alloc */ + if (pub->n == NULL) + pub->n = BN_new(); buffer_get_bignum(&buffer, pub->n); - pub->e = BN_new(); + /* 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); @@ -196,6 +262,20 @@ load_public_key(const char *filename, RSA * pub, return 1; } +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 @@ -204,35 +284,17 @@ load_public_key(const char *filename, RSA * pub, */ int -load_private_key(const char *filename, const char *passphrase, - RSA * prv, char **comment_return) +load_private_key_rsa(int fd, const char *filename, + const char *passphrase, RSA * prv, char **comment_return) { - int fd, i, check1, check2, cipher_type; + int i, check1, check2, cipher_type; off_t len; Buffer buffer, decrypted; char *cp; CipherContext cipher; BN_CTX *ctx; BIGNUM *aux; - 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; - } len = lseek(fd, (off_t) 0, SEEK_END); lseek(fd, (off_t) 0, SEEK_SET); @@ -309,7 +371,9 @@ load_private_key(const char *filename, const char *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; @@ -343,3 +407,87 @@ fail: 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; +} diff --git a/usr.bin/ssh/authfile.h b/usr.bin/ssh/authfile.h new file mode 100644 index 00000000000..afec27d5433 --- /dev/null +++ b/usr.bin/ssh/authfile.h @@ -0,0 +1,36 @@ +#ifndef AUTHFILE_H +#define AUTHFILE_H + +/* + * Saves the authentication (private) key in a file, encrypting it with + * passphrase. + * For RSA keys: 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(const char *filename, const char *passphrase, + Key * private_key, const char *comment); + +/* + * Loads the public part of the key file (public key and comment). Returns 0 + * if an error occurred; zero if the public key was successfully read. The + * comment of the key is returned in comment_return if it is non-NULL; the + * caller must free the value with xfree. + */ +int +load_public_key(const char *filename, Key * pub, + char **comment_return); + +/* + * 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. The comment of the key is returned in + * comment_return if it is non-NULL; the caller must free the value with + * xfree. + */ +int +load_private_key(const char *filename, const char *passphrase, + Key * private_key, char **comment_return); + +#endif diff --git a/usr.bin/ssh/compat.c b/usr.bin/ssh/compat.c index bcd1ff2ae56..d7bb1186695 100644 --- a/usr.bin/ssh/compat.c +++ b/usr.bin/ssh/compat.c @@ -28,7 +28,7 @@ */ #include "includes.h" -RCSID("$Id: compat.c,v 1.11 2000/04/14 10:30:31 markus Exp $"); +RCSID("$Id: compat.c,v 1.12 2000/04/26 20:56:29 markus Exp $"); #include "ssh.h" #include "packet.h" @@ -44,7 +44,6 @@ enable_compat20(void) { verbose("Enabling compatibility mode for protocol 2.0"); compat20 = 1; - packet_set_ssh2_format(); } void enable_compat13(void) diff --git a/usr.bin/ssh/dsa.c b/usr.bin/ssh/dsa.c index 1594c14f53a..a4f6d3e7857 100644 --- a/usr.bin/ssh/dsa.c +++ b/usr.bin/ssh/dsa.c @@ -28,7 +28,7 @@ */ #include "includes.h" -RCSID("$Id: dsa.c,v 1.4 2000/04/14 10:30:31 markus Exp $"); +RCSID("$Id: dsa.c,v 1.5 2000/04/26 20:56:29 markus Exp $"); #include "ssh.h" #include "xmalloc.h" @@ -47,13 +47,14 @@ RCSID("$Id: dsa.c,v 1.4 2000/04/14 10:30:31 markus Exp $"); #include #include "kex.h" #include "key.h" +#include "uuencode.h" #define INTBLOB_LEN 20 #define SIGBLOB_LEN (2*INTBLOB_LEN) Key * -dsa_serverkey_from_blob( - char *serverhostkey, int serverhostkeylen) +dsa_key_from_blob( + char *blob, int blen) { Buffer b; char *ktype; @@ -61,14 +62,17 @@ dsa_serverkey_from_blob( DSA *dsa; Key *key; +#ifdef DEBUG_DSS + dump_base64(blob, blen); +#endif /* fetch & parse DSA/DSS pubkey */ key = key_new(KEY_DSA); dsa = key->dsa; buffer_init(&b); - buffer_append(&b, serverhostkey, serverhostkeylen); + buffer_append(&b, blob, blen); ktype = buffer_get_string(&b, NULL); if (strcmp(KEX_DSS, ktype) != 0) { - error("dsa_serverkey_from_blob: cannot handle type %s", ktype); + error("dsa_key_from_blob: cannot handle type %s", ktype); key_free(key); return NULL; } @@ -78,7 +82,7 @@ dsa_serverkey_from_blob( buffer_get_bignum2(&b, dsa->pub_key); rlen = buffer_len(&b); if(rlen != 0) - error("dsa_serverkey_from_blob: remaining bytes in serverhostkey %d", rlen); + error("dsa_key_from_blob: remaining bytes in key blob %d", rlen); buffer_free(&b); debug("keytype %s", ktype); @@ -87,37 +91,8 @@ dsa_serverkey_from_blob( #endif return key; } -DSA * -dsa_load_private(char *filename) -{ - DSA *dsa; - BIO *in; - - in = BIO_new(BIO_s_file()); - if (in == NULL) - fatal("BIO_new failed"); - if (BIO_read_filename(in, filename) <= 0) - fatal("BIO_read failed %s: %s", filename, strerror(errno)); - fprintf(stderr, "read DSA private key\n"); - dsa = PEM_read_bio_DSAPrivateKey(in,NULL,NULL,NULL); - if (dsa == NULL) - fatal("PEM_read_bio_DSAPrivateKey failed %s", filename); - BIO_free(in); - return dsa; -} -Key * -dsa_get_serverkey(char *filename) -{ - Key *k = key_new(KEY_EMPTY); - k->type = KEY_DSA; - k->dsa = dsa_load_private(filename); -#ifdef DEBUG_DSS - DSA_print_fp(stderr, dsa, 8); -#endif - return k; -} int -dsa_make_serverkey_blob(Key *key, unsigned char **blobp, unsigned int *lenp) +dsa_make_key_blob(Key *key, unsigned char **blobp, unsigned int *lenp) { Buffer b; int len; @@ -146,7 +121,7 @@ int dsa_sign( Key *key, unsigned char **sigp, int *lenp, - unsigned char *hash, int hlen) + unsigned char *data, int datalen) { unsigned char *digest; unsigned char *ret; @@ -165,10 +140,13 @@ dsa_sign( } digest = xmalloc(evp_md->md_size); EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, hash, hlen); + EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, NULL); sig = DSA_do_sign(digest, evp_md->md_size, key->dsa); + if (sig == NULL) { + fatal("dsa_sign: cannot sign"); + } rlen = BN_num_bytes(sig->r); slen = BN_num_bytes(sig->s); @@ -212,7 +190,7 @@ int dsa_verify( Key *key, unsigned char *signature, int signaturelen, - unsigned char *hash, int hlen) + unsigned char *data, int datalen) { Buffer b; unsigned char *digest; @@ -269,10 +247,10 @@ dsa_verify( xfree(sigblob); } - /* sha1 the signed data (== session_id == hash) */ + /* sha1 the data */ digest = xmalloc(evp_md->md_size); EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, hash, hlen); + EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, NULL); ret = DSA_do_verify(digest, evp_md->md_size, sig, key->dsa); @@ -296,3 +274,21 @@ dsa_verify( debug("dsa_verify: signature %s", txt); return ret; } + +Key * +dsa_generate_key(unsigned int bits) +{ + DSA *dsa = DSA_generate_parameters(bits, NULL, 0, NULL, NULL, NULL, NULL); + Key *k; + if (dsa == NULL) { + fatal("DSA_generate_parameters failed"); + } + if (!DSA_generate_key(dsa)) { + fatal("DSA_generate_keys failed"); + } + + k = key_new(KEY_EMPTY); + k->type = KEY_DSA; + k->dsa = dsa; + return k; +} diff --git a/usr.bin/ssh/dsa.h b/usr.bin/ssh/dsa.h index 65e651d9b91..3cece7c1f22 100644 --- a/usr.bin/ssh/dsa.h +++ b/usr.bin/ssh/dsa.h @@ -1,20 +1,22 @@ #ifndef DSA_H #define DSA_H -Key *dsa_serverkey_from_blob(char *serverhostkey, int serverhostkeylen); -Key *dsa_get_serverkey(char *filename); -int dsa_make_serverkey_blob(Key *key, unsigned char **blobp, unsigned int *lenp); +Key *dsa_key_from_blob(char *blob, int blen); +int dsa_make_key_blob(Key *key, unsigned char **blobp, unsigned int *lenp); int dsa_sign( Key *key, unsigned char **sigp, int *lenp, - unsigned char *hash, int hlen); + unsigned char *data, int datalen); int dsa_verify( Key *key, unsigned char *signature, int signaturelen, - unsigned char *hash, int hlen); + unsigned char *data, int datalen); + +Key * +dsa_generate_key(unsigned int bits); #endif diff --git a/usr.bin/ssh/hostfile.c b/usr.bin/ssh/hostfile.c index 29efe5656a1..e1c2429bd64 100644 --- a/usr.bin/ssh/hostfile.c +++ b/usr.bin/ssh/hostfile.c @@ -14,7 +14,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: hostfile.c,v 1.16 2000/04/14 10:30:31 markus Exp $"); +RCSID("$OpenBSD: hostfile.c,v 1.17 2000/04/26 20:56:29 markus Exp $"); #include "packet.h" #include "match.h" @@ -39,13 +39,8 @@ hostfile_read_key(char **cpp, unsigned int *bitsp, Key *ret) for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) ; - /* Get number of bits. */ - if (*cp < '0' || *cp > '9') - return 0; /* Bad bit count... */ - for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) - bits = 10 * bits + *cp - '0'; - - if (!key_read(ret, bits, &cp)) + bits = key_read(ret, &cp); + if (bits == 0) return 0; /* Skip trailing whitespace. */ @@ -182,24 +177,18 @@ add_host_to_hostfile(const char *filename, const char *host, Key *key) { FILE *f; int success = 0; - if (key == NULL) - return 1; - - /* Open the file for appending. */ + return 1; /* XXX ? */ f = fopen(filename, "a"); if (!f) return 0; - fprintf(f, "%s ", host); if (key_write(key, f)) { - fprintf(f, "\n"); success = 1; } else { - error("add_host_to_hostfile: saving key failed"); + error("add_host_to_hostfile: saving key in %s failed", filename); } - - /* Close the file. */ + fprintf(f, "\n"); fclose(f); return success; } diff --git a/usr.bin/ssh/key.c b/usr.bin/ssh/key.c index 872313abc29..583c529010d 100644 --- a/usr.bin/ssh/key.c +++ b/usr.bin/ssh/key.c @@ -38,6 +38,10 @@ #include #include "xmalloc.h" #include "key.h" +#include "dsa.h" +#include "uuencode.h" + +#define SSH_DSS "ssh-dss" Key * key_new(int type) @@ -47,6 +51,8 @@ key_new(int type) DSA *dsa; k = xmalloc(sizeof(*k)); k->type = type; + k->dsa = NULL; + k->rsa = NULL; switch (k->type) { case KEY_RSA: rsa = RSA_new(); @@ -63,8 +69,6 @@ key_new(int type) k->dsa = dsa; break; case KEY_EMPTY: - k->dsa = NULL; - k->rsa = NULL; break; default: fatal("key_new: bad key type %d", k->type); @@ -111,7 +115,7 @@ key_equal(Key *a, Key *b) BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; break; default: - fatal("key_free: bad key type %d", a->type); + fatal("key_equal: bad key type %d", a->type); break; } return 0; @@ -127,46 +131,37 @@ char * key_fingerprint(Key *k) { static char retval[80]; - unsigned char *buf = NULL; + unsigned char *blob = NULL; int len = 0; - int nlen, elen, plen, qlen, glen, publen; + int nlen, elen; switch (k->type) { case KEY_RSA: nlen = BN_num_bytes(k->rsa->n); elen = BN_num_bytes(k->rsa->e); len = nlen + elen; - buf = xmalloc(len); - BN_bn2bin(k->rsa->n, buf); - BN_bn2bin(k->rsa->e, buf + nlen); + blob = xmalloc(len); + BN_bn2bin(k->rsa->n, blob); + BN_bn2bin(k->rsa->e, blob + nlen); break; case KEY_DSA: - plen = BN_num_bytes(k->dsa->p); - qlen = BN_num_bytes(k->dsa->q); - glen = BN_num_bytes(k->dsa->g); - publen = BN_num_bytes(k->dsa->pub_key); - len = qlen + qlen + glen + publen; - buf = xmalloc(len); - BN_bn2bin(k->dsa->p, buf); - BN_bn2bin(k->dsa->q, buf + plen); - BN_bn2bin(k->dsa->g, buf + plen + qlen); - BN_bn2bin(k->dsa->pub_key , buf + plen + qlen + glen); + dsa_make_key_blob(k, &blob, &len); break; default: fatal("key_fingerprint: bad key type %d", k->type); break; } - if (buf != NULL) { + if (blob != NULL) { unsigned char d[16]; EVP_MD_CTX md; EVP_DigestInit(&md, EVP_md5()); - EVP_DigestUpdate(&md, buf, len); + EVP_DigestUpdate(&md, blob, len); EVP_DigestFinal(&md, d, NULL); snprintf(retval, sizeof(retval), FPRINT, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); - memset(buf, 0, len); - xfree(buf); + memset(blob, 0, len); + xfree(blob); } return retval; } @@ -226,13 +221,27 @@ write_bignum(FILE *f, BIGNUM *num) free(buf); return 1; } -int -key_read(Key *ret, unsigned int bits, char **cpp) +unsigned int +key_read(Key *ret, char **cpp) { + Key *k; + unsigned int bits = 0; + char *cp; + int len, n; + unsigned char *blob; + + cp = *cpp; + switch(ret->type) { case KEY_RSA: + /* Get number of bits. */ + if (*cp < '0' || *cp > '9') + return 0; /* Bad bit count... */ + for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) + bits = 10 * bits + *cp - '0'; if (bits == 0) return 0; + *cpp = cp; /* Get public exponent, public modulus. */ if (!read_bignum(cpp, ret->rsa->e)) return 0; @@ -240,22 +249,32 @@ key_read(Key *ret, unsigned int bits, char **cpp) return 0; break; case KEY_DSA: - if (bits != 0) - return 0; - if (!read_bignum(cpp, ret->dsa->p)) + if (strncmp(cp, SSH_DSS " ", 7) != 0) return 0; - if (!read_bignum(cpp, ret->dsa->q)) - return 0; - if (!read_bignum(cpp, ret->dsa->g)) - return 0; - if (!read_bignum(cpp, ret->dsa->pub_key)) + cp += 7; + len = 2*strlen(cp); + blob = xmalloc(len); + n = uudecode(cp, blob, len); + k = dsa_key_from_blob(blob, n); + if (k == NULL) + return 0; + xfree(blob); + if (ret->dsa != NULL) + DSA_free(ret->dsa); + ret->dsa = k->dsa; + k->dsa = NULL; + key_free(k); + bits = BN_num_bits(ret->dsa->p); + cp = strchr(cp, '='); + if (cp == NULL) return 0; + *cpp = cp + 1; break; default: - fatal("bad key type: %d", ret->type); + fatal("key_read: bad key type: %d", ret->type); break; } - return 1; + return bits; } int key_write(Key *key, FILE *f) @@ -274,17 +293,15 @@ key_write(Key *key, FILE *f) error("key_write: failed for RSA key"); } } else if (key->type == KEY_DSA && key->dsa != NULL) { - /* bits == 0 means DSA key */ - bits = 0; - fprintf(f, "%u", bits); - if (write_bignum(f, key->dsa->p) && - write_bignum(f, key->dsa->q) && - write_bignum(f, key->dsa->g) && - write_bignum(f, key->dsa->pub_key)) { - success = 1; - } else { - error("key_write: failed for DSA key"); - } + int len, n; + unsigned char *blob, *uu; + dsa_make_key_blob(key, &blob, &len); + uu = xmalloc(2*len); + n = uuencode(blob, len, uu); + fprintf(f, "%s %s", SSH_DSS, uu); + xfree(blob); + xfree(uu); + success = 1; } return success; } diff --git a/usr.bin/ssh/key.h b/usr.bin/ssh/key.h index 70f0c518b8a..d1bcf3b1bed 100644 --- a/usr.bin/ssh/key.h +++ b/usr.bin/ssh/key.h @@ -18,6 +18,7 @@ void key_free(Key *k); int key_equal(Key *a, Key *b); char *key_fingerprint(Key *k); int key_write(Key *key, FILE *f); -int key_read(Key *key, unsigned int bits, char **cpp); +unsigned int +key_read(Key *key, char **cpp); #endif diff --git a/usr.bin/ssh/lib/Makefile b/usr.bin/ssh/lib/Makefile index 4c695afa741..35de105b496 100644 --- a/usr.bin/ssh/lib/Makefile +++ b/usr.bin/ssh/lib/Makefile @@ -5,7 +5,7 @@ SRCS= authfd.c authfile.c bufaux.c buffer.c canohost.c channels.c \ cipher.c compat.c compress.c crc32.c deattack.c fingerprint.c \ hostfile.c log.c match.c mpaux.c nchan.c packet.c readpass.c \ rsa.c tildexpand.c ttymodes.c uidswap.c xmalloc.c atomicio.c \ - key.c dispatch.c dsa.c kex.c hmac.c + key.c dispatch.c dsa.c kex.c hmac.c uuencode.c NOPROFILE= yes NOPIC= yes diff --git a/usr.bin/ssh/radix.c b/usr.bin/ssh/radix.c index 84e390fd1d6..9d1c999a12b 100644 --- a/usr.bin/ssh/radix.c +++ b/usr.bin/ssh/radix.c @@ -1,109 +1,15 @@ /* * radix.c * - * base-64 encoding pinched from lynx2-7-2, who pinched it from rpem. - * Originally written by Mark Riordan 12 August 1990 and 17 Feb 1991 - * and placed in the public domain. - * * Dug Song */ #include "includes.h" +#include "uuencode.h" #ifdef AFS #include -char six2pr[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' -}; - -unsigned char pr2six[256]; - -int -uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded) -{ - /* ENC is the basic 1 character encoding function to make a char printing */ -#define ENC(c) six2pr[c] - - register char *outptr = bufcoded; - unsigned int i; - - for (i = 0; i < nbytes; i += 3) { - *(outptr++) = ENC(*bufin >> 2); /* c1 */ - *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /* c2 */ - *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)); /* c3 */ - *(outptr++) = ENC(bufin[2] & 077); /* c4 */ - bufin += 3; - } - if (i == nbytes + 1) { - outptr[-1] = '='; - } else if (i == nbytes + 2) { - outptr[-1] = '='; - outptr[-2] = '='; - } - *outptr = '\0'; - return (outptr - bufcoded); -} - -int -uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize) -{ - /* single character decode */ -#define DEC(c) pr2six[(unsigned char)c] -#define MAXVAL 63 - - static int first = 1; - int nbytesdecoded, j; - const char *bufin = bufcoded; - register unsigned char *bufout = bufplain; - register int nprbytes; - - /* If this is the first call, initialize the mapping table. */ - if (first) { - first = 0; - for (j = 0; j < 256; j++) - pr2six[j] = MAXVAL + 1; - for (j = 0; j < 64; j++) - pr2six[(unsigned char) six2pr[j]] = (unsigned char) j; - } - /* Strip leading whitespace. */ - while (*bufcoded == ' ' || *bufcoded == '\t') - bufcoded++; - - /* - * Figure out how many characters are in the input buffer. If this - * would decode into more bytes than would fit into the output - * buffer, adjust the number of input bytes downwards. - */ - bufin = bufcoded; - while (DEC(*(bufin++)) <= MAXVAL); - nprbytes = bufin - bufcoded - 1; - nbytesdecoded = ((nprbytes + 3) / 4) * 3; - if (nbytesdecoded > outbufsize) - nprbytes = (outbufsize * 4) / 3; - - bufin = bufcoded; - - while (nprbytes > 0) { - *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4); - *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2); - *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3])); - bufin += 4; - nprbytes -= 4; - } - if (nprbytes & 03) { - if (DEC(bufin[-2]) > MAXVAL) - nbytesdecoded -= 2; - else - nbytesdecoded -= 1; - } - return (nbytesdecoded); -} - typedef unsigned char my_u_char; typedef unsigned int my_u_int32_t; typedef unsigned short my_u_short; diff --git a/usr.bin/ssh/readconf.c b/usr.bin/ssh/readconf.c index f7f00dd9c2b..eac514ea530 100644 --- a/usr.bin/ssh/readconf.c +++ b/usr.bin/ssh/readconf.c @@ -14,7 +14,7 @@ */ #include "includes.h" -RCSID("$Id: readconf.c,v 1.26 2000/04/14 10:30:32 markus Exp $"); +RCSID("$Id: readconf.c,v 1.27 2000/04/26 20:56:29 markus Exp $"); #include "ssh.h" #include "cipher.h" @@ -104,7 +104,8 @@ typedef enum { oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication, - oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol + oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oIdentityFile2, + oGlobalKnownHostsFile2, oUserKnownHostsFile2 } OpCodes; /* Textual representations of the tokens. */ @@ -131,6 +132,7 @@ static struct { { "fallbacktorsh", oFallBackToRsh }, { "usersh", oUseRsh }, { "identityfile", oIdentityFile }, + { "identityfile2", oIdentityFile2 }, { "hostname", oHostName }, { "proxycommand", oProxyCommand }, { "port", oPort }, @@ -145,6 +147,8 @@ static struct { { "rhostsrsaauthentication", oRhostsRSAAuthentication }, { "globalknownhostsfile", oGlobalKnownHostsFile }, { "userknownhostsfile", oUserKnownHostsFile }, + { "globalknownhostsfile2", oGlobalKnownHostsFile2 }, + { "userknownhostsfile2", oUserKnownHostsFile2 }, { "connectionattempts", oConnectionAttempts }, { "batchmode", oBatchMode }, { "checkhostip", oCheckHostIP }, @@ -368,14 +372,22 @@ parse_flag: goto parse_int; case oIdentityFile: + case oIdentityFile2: cp = strtok(NULL, WHITESPACE); if (!cp) fatal("%.200s line %d: Missing argument.", filename, linenum); if (*activep) { - if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) + intptr = (opcode == oIdentityFile) ? + &options->num_identity_files : + &options->num_identity_files2; + if (*intptr >= SSH_MAX_IDENTITY_FILES) fatal("%.200s line %d: Too many identity files specified (max %d).", filename, linenum, SSH_MAX_IDENTITY_FILES); - options->identity_files[options->num_identity_files++] = xstrdup(cp); + charptr = (opcode == oIdentityFile) ? + &options->identity_files[*intptr] : + &options->identity_files2[*intptr]; + *charptr = xstrdup(cp); + *intptr = *intptr + 1; } break; @@ -397,6 +409,14 @@ parse_string: charptr = &options->user_hostfile; goto parse_string; + case oGlobalKnownHostsFile2: + charptr = &options->system_hostfile2; + goto parse_string; + + case oUserKnownHostsFile2: + charptr = &options->user_hostfile2; + goto parse_string; + case oHostName: charptr = &options->hostname; goto parse_string; @@ -642,12 +662,15 @@ initialize_options(Options * options) options->ciphers = NULL; options->protocol = SSH_PROTO_UNKNOWN; options->num_identity_files = 0; + options->num_identity_files2 = 0; options->hostname = NULL; options->proxy_command = NULL; options->user = NULL; options->escape_char = -1; options->system_hostfile = NULL; options->user_hostfile = NULL; + options->system_hostfile2 = NULL; + options->user_hostfile2 = NULL; options->num_local_forwards = 0; options->num_remote_forwards = 0; options->log_level = (LogLevel) - 1; @@ -722,12 +745,24 @@ fill_default_options(Options * options) sprintf(options->identity_files[0], "~/%.100s", SSH_CLIENT_IDENTITY); options->num_identity_files = 1; } +#if 0 + if (options->num_identity_files2 == 0) { + options->identity_files2[0] = + xmalloc(2 + strlen(SSH2_CLIENT_IDENTITY) + 1); + sprintf(options->identity_files2[0], "~/%.100s", SSH2_CLIENT_IDENTITY); + options->num_identity_files2 = 1; + } +#endif if (options->escape_char == -1) options->escape_char = '~'; if (options->system_hostfile == NULL) options->system_hostfile = SSH_SYSTEM_HOSTFILE; if (options->user_hostfile == NULL) options->user_hostfile = SSH_USER_HOSTFILE; + if (options->system_hostfile2 == NULL) + options->system_hostfile2 = SSH_SYSTEM_HOSTFILE2; + if (options->user_hostfile2 == NULL) + options->user_hostfile2 = SSH_USER_HOSTFILE2; if (options->log_level == (LogLevel) - 1) options->log_level = SYSLOG_LEVEL_INFO; /* options->proxy_command should not be set by default */ diff --git a/usr.bin/ssh/readconf.h b/usr.bin/ssh/readconf.h index 3391e0ebaad..ca685f30957 100644 --- a/usr.bin/ssh/readconf.h +++ b/usr.bin/ssh/readconf.h @@ -13,7 +13,7 @@ * */ -/* RCSID("$Id: readconf.h,v 1.15 2000/04/14 10:30:32 markus Exp $"); */ +/* RCSID("$Id: readconf.h,v 1.16 2000/04/26 20:56:29 markus Exp $"); */ #ifndef READCONF_H #define READCONF_H @@ -73,9 +73,13 @@ typedef struct { char *system_hostfile;/* Path for /etc/ssh_known_hosts. */ char *user_hostfile; /* Path for $HOME/.ssh/known_hosts. */ + char *system_hostfile2; + char *user_hostfile2; int num_identity_files; /* Number of files for RSA identities. */ + int num_identity_files2; /* DSA identities. */ char *identity_files[SSH_MAX_IDENTITY_FILES]; + char *identity_files2[SSH_MAX_IDENTITY_FILES]; /* Local TCP/IP forward requests. */ int num_local_forwards; diff --git a/usr.bin/ssh/ssh-add.c b/usr.bin/ssh/ssh-add.c index 78e15204304..b7a385c171d 100644 --- a/usr.bin/ssh/ssh-add.c +++ b/usr.bin/ssh/ssh-add.c @@ -7,30 +7,35 @@ */ #include "includes.h" -RCSID("$Id: ssh-add.c,v 1.15 1999/12/02 20:05:40 markus Exp $"); +RCSID("$Id: ssh-add.c,v 1.16 2000/04/26 20:56:29 markus Exp $"); + +#include +#include #include "rsa.h" #include "ssh.h" #include "xmalloc.h" #include "authfd.h" #include "fingerprint.h" +#include "key.h" +#include "authfile.h" void delete_file(AuthenticationConnection *ac, const char *filename) { - RSA *key; + Key *public; char *comment; - key = RSA_new(); - if (!load_public_key(filename, key, &comment)) { + public = key_new(KEY_RSA); + if (!load_public_key(filename, public, &comment)) { printf("Bad key file %s: %s\n", filename, strerror(errno)); return; } - if (ssh_remove_identity(ac, key)) + if (ssh_remove_identity(ac, public->rsa)) fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); else fprintf(stderr, "Could not remove identity: %s\n", filename); - RSA_free(key); + key_free(public); xfree(comment); } @@ -85,20 +90,19 @@ ssh_askpass(char *askpass, char *msg) void add_file(AuthenticationConnection *ac, const char *filename) { - RSA *key; - RSA *public_key; + Key *public; + Key *private; char *saved_comment, *comment, *askpass = NULL; char buf[1024], msg[1024]; int success; int interactive = isatty(STDIN_FILENO); - key = RSA_new(); - public_key = RSA_new(); - if (!load_public_key(filename, public_key, &saved_comment)) { + public = key_new(KEY_RSA); + if (!load_public_key(filename, public, &saved_comment)) { printf("Bad key file %s: %s\n", filename, strerror(errno)); return; } - RSA_free(public_key); + key_free(public); if (!interactive && getenv("DISPLAY")) { if (getenv(SSH_ASKPASS_ENV)) @@ -108,7 +112,8 @@ add_file(AuthenticationConnection *ac, const char *filename) } /* At first, try empty passphrase */ - success = load_private_key(filename, "", key, &comment); + private = key_new(KEY_RSA); + success = load_private_key(filename, "", private, &comment); if (!success) { printf("Need passphrase for %.200s\n", filename); if (!interactive && askpass == NULL) { @@ -129,7 +134,7 @@ add_file(AuthenticationConnection *ac, const char *filename) xfree(saved_comment); return; } - success = load_private_key(filename, pass, key, &comment); + success = load_private_key(filename, pass, private, &comment); memset(pass, 0, strlen(pass)); xfree(pass); if (success) @@ -139,11 +144,11 @@ add_file(AuthenticationConnection *ac, const char *filename) } xfree(saved_comment); - if (ssh_add_identity(ac, key, comment)) + if (ssh_add_identity(ac, private->rsa, comment)) fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); else fprintf(stderr, "Could not add identity: %s\n", filename); - RSA_free(key); + key_free(private); xfree(comment); } diff --git a/usr.bin/ssh/ssh-keygen.c b/usr.bin/ssh/ssh-keygen.c index a8516c9b1ff..5482839578d 100644 --- a/usr.bin/ssh/ssh-keygen.c +++ b/usr.bin/ssh/ssh-keygen.c @@ -7,20 +7,23 @@ */ #include "includes.h" -RCSID("$Id: ssh-keygen.c,v 1.18 2000/04/14 10:30:33 markus Exp $"); +RCSID("$Id: ssh-keygen.c,v 1.19 2000/04/26 20:56:29 markus Exp $"); + +#include +#include +#include +#include -#include "rsa.h" #include "ssh.h" #include "xmalloc.h" #include "fingerprint.h" +#include "key.h" +#include "rsa.h" +#include "dsa.h" +#include "authfile.h" +#include "uuencode.h" -/* Generated private key. */ -RSA *private_key; - -/* Generated public key. */ -RSA *public_key; - -/* Number of bits in the RSA key. This value can be changed on the command line. */ +/* Number of bits in the RSA/DSA key. This value can be changed on the command line. */ int bits = 1024; /* @@ -53,9 +56,17 @@ char *identity_new_passphrase = NULL; /* This is set to the new comment if given on the command line. */ char *identity_comment = NULL; +/* Dump public key file in format used by real and the original SSH 2 */ +int convert_to_ssh2 = 0; +int convert_from_ssh2 = 0; +int print_public = 0; +int dsa_mode = 0; + /* argv0 */ extern char *__progname; +char hostname[MAXHOSTNAMELEN]; + void ask_filename(struct passwd *pw, const char *prompt) { @@ -73,12 +84,138 @@ ask_filename(struct passwd *pw, const char *prompt) have_identity = 1; } +int +try_load_key(char *filename, Key *k) +{ + int success = 1; + if (!load_private_key(filename, "", k, NULL)) { + char *pass = read_passphrase("Enter passphrase: ", 1); + if (!load_private_key(filename, pass, k, NULL)) { + success = 0; + } + memset(pass, 0, strlen(pass)); + xfree(pass); + } + return success; +} + +#define SSH_COM_MAGIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" +#define SSH_COM_MAGIC_END "---- END SSH2 PUBLIC KEY ----" + +void +do_convert_to_ssh2(struct passwd *pw) +{ + Key *k; + int len; + unsigned char *blob; + struct stat st; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + k = key_new(KEY_DSA); + if (!try_load_key(identity_file, k)) { + fprintf(stderr, "load failed\n"); + exit(1); + } + dsa_make_key_blob(k, &blob, &len); + fprintf(stdout, SSH_COM_MAGIC_BEGIN "\n"); + fprintf(stdout, + "Comment: \"%d-bit DSA, converted from openssh by %s@%s\"\n", + BN_num_bits(k->dsa->p), + pw->pw_name, hostname); + dump_base64(stdout, blob, len); + fprintf(stdout, SSH_COM_MAGIC_END "\n"); + key_free(k); + exit(0); +} + +void +do_convert_from_ssh2(struct passwd *pw) +{ + Key *k; + int blen; + char line[1024], *p; + char blob[8096]; + char encoded[8096]; + struct stat st; + FILE *fp; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + fp = fopen(identity_file, "r"); + if (fp == NULL) { + perror(identity_file); + exit(1); + } + encoded[0] = '\0'; + while (fgets(line, sizeof(line), fp)) { + if (strncmp(line, "----", 4) == 0 || + strstr(line, ": ") != NULL) { + fprintf(stderr, "ignore: %s", line); + continue; + } + if (!(p = strchr(line, '\n'))) { + fprintf(stderr, "input line too long.\n"); + exit(1); + } + *p = '\0'; + strlcat(encoded, line, sizeof(encoded)); + } + blen = uudecode(encoded, (unsigned char *)blob, sizeof(blob)); + if (blen < 0) { + fprintf(stderr, "uudecode failed.\n"); + exit(1); + } + k = dsa_key_from_blob(blob, blen); + if (!key_write(k, stdout)) + fprintf(stderr, "key_write failed"); + key_free(k); + fprintf(stdout, "\n"); + fclose(fp); + exit(0); +} + +void +do_print_public(struct passwd *pw) +{ + Key *k; + int len; + unsigned char *blob; + struct stat st; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + k = key_new(KEY_DSA); + if (!try_load_key(identity_file, k)) { + fprintf(stderr, "load failed\n"); + exit(1); + } + dsa_make_key_blob(k, &blob, &len); + if (!key_write(k, stdout)) + fprintf(stderr, "key_write failed"); + key_free(k); + fprintf(stdout, "\n"); + exit(0); +} + void do_fingerprint(struct passwd *pw) { FILE *f; BIGNUM *e, *n; - RSA *public_key; + Key *public; char *comment = NULL, *cp, *ep, line[16*1024]; int i, skip = 0, num = 1, invalid = 1; unsigned int ignore; @@ -90,17 +227,16 @@ do_fingerprint(struct passwd *pw) perror(identity_file); exit(1); } - - public_key = RSA_new(); - if (load_public_key(identity_file, public_key, &comment)) { - printf("%d %s %s\n", BN_num_bits(public_key->n), - fingerprint(public_key->e, public_key->n), - comment); - RSA_free(public_key); + public = key_new(KEY_RSA); + if (load_public_key(identity_file, public, &comment)) { + printf("%d %s %s\n", BN_num_bits(public->rsa->n), + key_fingerprint(public), comment); + key_free(public); exit(0); } - RSA_free(public_key); + key_free(public); + /* XXX */ f = fopen(identity_file, "r"); if (f != NULL) { n = BN_new(); @@ -168,7 +304,9 @@ do_change_passphrase(struct passwd *pw) char *comment; char *old_passphrase, *passphrase1, *passphrase2; struct stat st; - RSA *private_key; + Key *private; + Key *public; + int type = dsa_mode ? KEY_DSA : KEY_RSA; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -176,22 +314,26 @@ do_change_passphrase(struct passwd *pw) perror(identity_file); exit(1); } - public_key = RSA_new(); - if (!load_public_key(identity_file, public_key, NULL)) { - printf("%s is not a valid key file.\n", identity_file); - exit(1); + + if (type == KEY_RSA) { + /* XXX this works currently only for RSA */ + public = key_new(type); + if (!load_public_key(identity_file, public, NULL)) { + printf("%s is not a valid key file.\n", identity_file); + exit(1); + } + /* Clear the public key since we are just about to load the whole file. */ + key_free(public); } - /* Clear the public key since we are just about to load the whole file. */ - RSA_free(public_key); /* Try to load the file with empty passphrase. */ - private_key = RSA_new(); - if (!load_private_key(identity_file, "", private_key, &comment)) { + private = key_new(type); + if (!load_private_key(identity_file, "", private, &comment)) { if (identity_passphrase) old_passphrase = xstrdup(identity_passphrase); else old_passphrase = read_passphrase("Enter old passphrase: ", 1); - if (!load_private_key(identity_file, old_passphrase, private_key, &comment)) { + if (!load_private_key(identity_file, old_passphrase, private, &comment)) { memset(old_passphrase, 0, strlen(old_passphrase)); xfree(old_passphrase); printf("Bad passphrase.\n"); @@ -226,19 +368,19 @@ do_change_passphrase(struct passwd *pw) } /* Save the file using the new passphrase. */ - if (!save_private_key(identity_file, passphrase1, private_key, comment)) { + if (!save_private_key(identity_file, passphrase1, private, comment)) { printf("Saving the key failed: %s: %s.\n", identity_file, strerror(errno)); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); - RSA_free(private_key); + key_free(private); xfree(comment); exit(1); } /* Destroy the passphrase and the copy of the key in memory. */ memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); - RSA_free(private_key); /* Destroys contents */ + key_free(private); /* Destroys contents */ xfree(comment); printf("Your identification has been saved with the new passphrase.\n"); @@ -252,11 +394,11 @@ void do_change_comment(struct passwd *pw) { char new_comment[1024], *comment; - RSA *private_key; + Key *private; + Key *public; char *passphrase; struct stat st; FILE *f; - char *tmpbuf; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -268,14 +410,14 @@ do_change_comment(struct passwd *pw) * Try to load the public key from the file the verify that it is * readable and of the proper format. */ - public_key = RSA_new(); - if (!load_public_key(identity_file, public_key, NULL)) { + public = key_new(KEY_RSA); + if (!load_public_key(identity_file, public, NULL)) { printf("%s is not a valid key file.\n", identity_file); exit(1); } - private_key = RSA_new(); - if (load_private_key(identity_file, "", private_key, &comment)) + private = key_new(KEY_RSA); + if (load_private_key(identity_file, "", private, &comment)) passphrase = xstrdup(""); else { if (identity_passphrase) @@ -285,7 +427,7 @@ do_change_comment(struct passwd *pw) else passphrase = read_passphrase("Enter passphrase: ", 1); /* Try to load using the passphrase. */ - if (!load_private_key(identity_file, passphrase, private_key, &comment)) { + if (!load_private_key(identity_file, passphrase, private, &comment)) { memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); printf("Bad passphrase.\n"); @@ -301,7 +443,7 @@ do_change_comment(struct passwd *pw) fflush(stdout); if (!fgets(new_comment, sizeof(new_comment), stdin)) { memset(passphrase, 0, strlen(passphrase)); - RSA_free(private_key); + key_free(private); exit(1); } if (strchr(new_comment, '\n')) @@ -309,18 +451,18 @@ do_change_comment(struct passwd *pw) } /* Save the file using the new passphrase. */ - if (!save_private_key(identity_file, passphrase, private_key, new_comment)) { + if (!save_private_key(identity_file, passphrase, private, new_comment)) { printf("Saving the key failed: %s: %s.\n", identity_file, strerror(errno)); memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); - RSA_free(private_key); + key_free(private); xfree(comment); exit(1); } memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); - RSA_free(private_key); + key_free(private); strlcat(identity_file, ".pub", sizeof(identity_file)); f = fopen(identity_file, "w"); @@ -328,13 +470,10 @@ do_change_comment(struct passwd *pw) printf("Could not save your public key in %s\n", identity_file); exit(1); } - fprintf(f, "%d ", BN_num_bits(public_key->n)); - tmpbuf = BN_bn2dec(public_key->e); - fprintf(f, "%s ", tmpbuf); - free(tmpbuf); - tmpbuf = BN_bn2dec(public_key->n); - fprintf(f, "%s %s\n", tmpbuf, new_comment); - free(tmpbuf); + if (!key_write(public, f)) + fprintf(stderr, "write key failed"); + key_free(public); + fprintf(f, " %s\n", new_comment); fclose(f); xfree(comment); @@ -347,7 +486,7 @@ void usage(void) { printf("ssh-keygen version %s\n", SSH_VERSION); - printf("Usage: %s [-b bits] [-p] [-c] [-l] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname); + printf("Usage: %s [-b bits] [-p] [-c] [-l] [-x] [-X] [-y] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname); exit(1); } @@ -359,29 +498,28 @@ main(int ac, char **av) { char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2; struct passwd *pw; - char *tmpbuf; int opt; struct stat st; FILE *f; - char hostname[MAXHOSTNAMELEN]; + Key *private; + Key *public; extern int optind; extern char *optarg; - /* check if RSA support exists */ - if (rsa_alive() == 0) { - fprintf(stderr, - "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", - __progname); - exit(1); - } + OpenSSL_add_all_algorithms(); + /* we need this for the home * directory. */ pw = getpwuid(getuid()); if (!pw) { printf("You don't exist, go away!\n"); exit(1); } + if (gethostname(hostname, sizeof(hostname)) < 0) { + perror("gethostname"); + exit(1); + } - while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF) { + while ((opt = getopt(ac, av, "dqpclxXyb:f:P:N:C:")) != EOF) { switch (opt) { case 'b': bits = atoi(optarg); @@ -424,6 +562,22 @@ main(int ac, char **av) quiet = 1; break; + case 'x': + convert_to_ssh2 = 1; + break; + + case 'X': + convert_from_ssh2 = 1; + break; + + case 'y': + print_public = 1; + break; + + case 'd': + dsa_mode = 1; + break; + case '?': default: usage(); @@ -437,22 +591,44 @@ main(int ac, char **av) printf("Can only have one of -p and -c.\n"); usage(); } + /* check if RSA support is needed and exists */ + if (dsa_mode == 0 && rsa_alive() == 0) { + fprintf(stderr, + "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", + __progname); + exit(1); + } if (print_fingerprint) do_fingerprint(pw); if (change_passphrase) do_change_passphrase(pw); if (change_comment) do_change_comment(pw); + if (convert_to_ssh2) + do_convert_to_ssh2(pw); + if (convert_from_ssh2) + do_convert_from_ssh2(pw); + if (print_public) + do_print_public(pw); arc4random_stir(); - if (quiet) - rsa_set_verbose(0); - - /* Generate the rsa key pair. */ - private_key = RSA_new(); - public_key = RSA_new(); - rsa_generate_key(private_key, public_key, bits); + if (dsa_mode != 0) { + if (!quiet) + printf("Generating DSA parameter and key.\n"); + public = private = dsa_generate_key(bits); + if (private == NULL) { + fprintf(stderr, "dsa_generate_keys failed"); + exit(1); + } + } else { + if (quiet) + rsa_set_verbose(0); + /* Generate the rsa key pair. */ + public = key_new(KEY_RSA); + private = key_new(KEY_RSA); + rsa_generate_key(private->rsa, public->rsa, bits); + } if (!have_identity) ask_filename(pw, "Enter file in which to save the key"); @@ -505,17 +681,13 @@ passphrase_again: strlcpy(comment, identity_comment, sizeof(comment)); } else { /* Create default commend field for the passphrase. */ - if (gethostname(hostname, sizeof(hostname)) < 0) { - perror("gethostname"); - exit(1); - } snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); } /* Save the key with the given passphrase and comment. */ - if (!save_private_key(identity_file, passphrase1, private_key, comment)) { + if (!save_private_key(identity_file, passphrase1, private, comment)) { printf("Saving the key failed: %s: %s.\n", - identity_file, strerror(errno)); + identity_file, strerror(errno)); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); exit(1); @@ -525,7 +697,9 @@ passphrase_again: xfree(passphrase1); /* Clear the private key and the random number generator. */ - RSA_free(private_key); + if (private != public) { + key_free(private); + } arc4random_stir(); if (!quiet) @@ -537,21 +711,18 @@ passphrase_again: printf("Could not save your public key in %s\n", identity_file); exit(1); } - fprintf(f, "%d ", BN_num_bits(public_key->n)); - tmpbuf = BN_bn2dec(public_key->e); - fprintf(f, "%s ", tmpbuf); - free(tmpbuf); - tmpbuf = BN_bn2dec(public_key->n); - fprintf(f, "%s %s\n", tmpbuf, comment); - free(tmpbuf); + if (!key_write(public, f)) + fprintf(stderr, "write key failed"); + fprintf(f, " %s\n", comment); fclose(f); if (!quiet) { - printf("Your public key has been saved in %s.\n", identity_file); + printf("Your public key has been saved in %s.\n", + identity_file); printf("The key fingerprint is:\n"); - printf("%d %s %s\n", BN_num_bits(public_key->n), - fingerprint(public_key->e, public_key->n), - comment); + printf("%s %s\n", key_fingerprint(public), comment); } + + key_free(public); exit(0); } diff --git a/usr.bin/ssh/ssh.c b/usr.bin/ssh/ssh.c index 850e1e528f4..5d6079e9b6e 100644 --- a/usr.bin/ssh/ssh.c +++ b/usr.bin/ssh/ssh.c @@ -11,7 +11,11 @@ */ #include "includes.h" -RCSID("$Id: ssh.c,v 1.48 2000/04/14 10:30:33 markus Exp $"); +RCSID("$Id: ssh.c,v 1.49 2000/04/26 20:56:30 markus Exp $"); + +#include +#include +#include #include "xmalloc.h" #include "ssh.h" @@ -24,6 +28,10 @@ RCSID("$Id: ssh.c,v 1.48 2000/04/14 10:30:33 markus Exp $"); #include "ssh2.h" #include "compat.h" #include "channels.h" +#include "key.h" +#include "authfile.h" + +extern char *__progname; /* Flag indicating whether IPv4 or IPv6. This can be set on the command line. Default value is AF_UNSPEC means both IPv4 and IPv6. */ @@ -348,10 +356,16 @@ main(int ac, char **av) } break; case 'c': - options.cipher = cipher_number(optarg); - if (options.cipher == -1) { - fprintf(stderr, "Unknown cipher type '%s'\n", optarg); - exit(1); + if (ciphers_valid(optarg)) { + /* SSH2 only */ + options.ciphers = xstrdup(optarg); + } else { + /* SSH1 only */ + options.cipher = cipher_number(optarg); + if (options.cipher == -1) { + fprintf(stderr, "Unknown cipher type '%s'\n", optarg); + exit(1); + } } break; case 'p': @@ -407,15 +421,8 @@ main(int ac, char **av) if (!host) usage(); - /* check if RSA support exists */ - if (rsa_alive() == 0) { - extern char *__progname; + OpenSSL_add_all_algorithms(); - fprintf(stderr, - "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", - __progname); - exit(1); - } /* Initialize the command to execute on remote host. */ buffer_init(&command); @@ -488,6 +495,20 @@ main(int ac, char **av) /* reinit */ log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0); + /* check if RSA support exists */ + if ((options.protocol & SSH_PROTO_1) && + rsa_alive() == 0) { + log("%s: no RSA support in libssl and libcrypto. See ssl(8).", + __progname); + log("Disabling protocol version 1"); + options.protocol &= ~ (SSH_PROTO_1|SSH_PROTO_1_PREFERRED); + } + if (! options.protocol & (SSH_PROTO_1|SSH_PROTO_2)) { + fprintf(stderr, "%s: No protocol version available.\n", + __progname); + exit(1); + } + if (options.user == NULL) options.user = xstrdup(pw->pw_name); @@ -554,9 +575,12 @@ main(int ac, char **av) * authentication. This must be done before releasing extra * privileges, because the file is only readable by root. */ - if (ok) { + if (ok && (options.protocol & SSH_PROTO_1)) { + Key k; host_private_key = RSA_new(); - if (load_private_key(HOST_KEY_FILE, "", host_private_key, NULL)) + k.type = KEY_RSA; + k.rsa = host_private_key; + if (load_private_key(HOST_KEY_FILE, "", &k, NULL)) host_private_key_loaded = 1; } /* @@ -602,15 +626,22 @@ main(int ac, char **av) exit(1); } /* Expand ~ in options.identity_files. */ + /* XXX mem-leaks */ for (i = 0; i < options.num_identity_files; i++) options.identity_files[i] = tilde_expand_filename(options.identity_files[i], original_real_uid); - + for (i = 0; i < options.num_identity_files2; i++) + options.identity_files2[i] = + tilde_expand_filename(options.identity_files2[i], original_real_uid); /* Expand ~ in known host file names. */ options.system_hostfile = tilde_expand_filename(options.system_hostfile, - original_real_uid); + original_real_uid); options.user_hostfile = tilde_expand_filename(options.user_hostfile, - original_real_uid); + original_real_uid); + options.system_hostfile2 = tilde_expand_filename(options.system_hostfile2, + original_real_uid); + options.user_hostfile2 = tilde_expand_filename(options.user_hostfile2, + original_real_uid); /* Log into the remote system. This never returns if the login fails. */ ssh_login(host_private_key_loaded, host_private_key, diff --git a/usr.bin/ssh/ssh.h b/usr.bin/ssh/ssh.h index 5e53b34b1e0..425b0b3e55a 100644 --- a/usr.bin/ssh/ssh.h +++ b/usr.bin/ssh/ssh.h @@ -13,7 +13,7 @@ * */ -/* RCSID("$Id: ssh.h,v 1.39 2000/04/19 07:05:49 deraadt Exp $"); */ +/* RCSID("$Id: ssh.h,v 1.40 2000/04/26 20:56:30 markus Exp $"); */ #ifndef SSH_H #define SSH_H @@ -71,6 +71,7 @@ * world-readable. */ #define SSH_SYSTEM_HOSTFILE ETCDIR "/ssh_known_hosts" +#define SSH_SYSTEM_HOSTFILE2 ETCDIR "/ssh_known_hosts2" /* * Of these, ssh_host_key must be readable only by root, whereas ssh_config @@ -101,6 +102,7 @@ * contain anything particularly secret. */ #define SSH_USER_HOSTFILE "~/.ssh/known_hosts" +#define SSH_USER_HOSTFILE2 "~/.ssh/known_hosts2" /* * Name of the default file containing client-side authentication key. This @@ -125,6 +127,7 @@ * running as root.) */ #define SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" +#define SSH_USER_PERMITTED_KEYS2 ".ssh/authorized_keys2" /* * Per-user and system-wide ssh "rc" files. These files are executed with @@ -378,36 +381,6 @@ int auth_rsa_challenge_dialog(RSA *pk); */ char *read_passphrase(const char *prompt, int from_stdin); -/* - * 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(const char *filename, const char *passphrase, - RSA * private_key, const char *comment); - -/* - * Loads the public part of the key file (public key and comment). Returns 0 - * if an error occurred; zero if the public key was successfully read. The - * comment of the key is returned in comment_return if it is non-NULL; the - * caller must free the value with xfree. - */ -int -load_public_key(const char *filename, RSA * pub, - char **comment_return); - -/* - * 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. The comment of the key is returned in - * comment_return if it is non-NULL; the caller must free the value with - * xfree. - */ -int -load_private_key(const char *filename, const char *passphrase, - RSA * private_key, char **comment_return); /*------------ Definitions for logging. -----------------------*/ diff --git a/usr.bin/ssh/sshconnect.c b/usr.bin/ssh/sshconnect.c index 82c8b5c2179..9c551d46a76 100644 --- a/usr.bin/ssh/sshconnect.c +++ b/usr.bin/ssh/sshconnect.c @@ -5,12 +5,10 @@ * Created: Sat Mar 18 22:15:47 1995 ylo * Code to connect to a remote host, and to perform the client side of the * login (authentication) dialog. - * - * SSH2 support added by Markus Friedl. */ #include "includes.h" -RCSID("$OpenBSD: sshconnect.c,v 1.69 2000/04/19 07:05:50 deraadt Exp $"); +RCSID("$OpenBSD: sshconnect.c,v 1.70 2000/04/26 20:56:30 markus Exp $"); #include #include "xmalloc.h" @@ -38,15 +36,17 @@ RCSID("$OpenBSD: sshconnect.c,v 1.69 2000/04/19 07:05:50 deraadt Exp $"); #include "key.h" #include "dsa.h" #include "hostfile.h" +#include "authfile.h" /* Session id for the current session. */ unsigned char session_id[16]; +unsigned int supported_authentications = 0; -/* authentications supported by server */ -unsigned int supported_authentications; +unsigned char *session_id2 = NULL; +int session_id2_len = 0; -static char *client_version_string = NULL; -static char *server_version_string = NULL; +char *client_version_string = NULL; +char *server_version_string = NULL; extern Options options; extern char *__progname; @@ -316,6 +316,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, return 1; } + /* * Checks if the user has an authentication agent, and if so, tries to * authenticate using the agent. @@ -467,16 +468,16 @@ int try_rsa_authentication(const char *authfile) { BIGNUM *challenge; - RSA *private_key; - RSA *public_key; + Key *public; + Key *private; char *passphrase, *comment; int type, i; int plen, clen; /* Try to load identification for the authentication key. */ - public_key = RSA_new(); - if (!load_public_key(authfile, public_key, &comment)) { - RSA_free(public_key); + public = key_new(KEY_RSA); + if (!load_public_key(authfile, public, &comment)) { + key_free(public); /* Could not load it. Fail. */ return 0; } @@ -484,12 +485,12 @@ try_rsa_authentication(const char *authfile) /* Tell the server that we are willing to authenticate using this key. */ packet_start(SSH_CMSG_AUTH_RSA); - packet_put_bignum(public_key->n); + packet_put_bignum(public->rsa->n); packet_send(); packet_write_wait(); /* We no longer need the public key. */ - RSA_free(public_key); + key_free(public); /* Wait for server's response. */ type = packet_read(&plen); @@ -515,12 +516,12 @@ try_rsa_authentication(const char *authfile) debug("Received RSA challenge from server."); - private_key = RSA_new(); + private = key_new(KEY_RSA); /* * Load the private key. Try first with empty passphrase; if it * fails, ask for a passphrase. */ - if (!load_private_key(authfile, "", private_key, NULL)) { + if (!load_private_key(authfile, "", private, NULL)) { char buf[300]; snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ", comment); @@ -533,7 +534,7 @@ try_rsa_authentication(const char *authfile) } /* Load the authentication file using the pasphrase. */ - if (!load_private_key(authfile, passphrase, private_key, NULL)) { + if (!load_private_key(authfile, passphrase, private, NULL)) { memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); error("Bad passphrase."); @@ -558,10 +559,10 @@ try_rsa_authentication(const char *authfile) xfree(comment); /* Compute and send a response to the challenge. */ - respond_to_rsa_challenge(challenge, private_key); + respond_to_rsa_challenge(challenge, private->rsa); /* Destroy the private key. */ - RSA_free(private_key); + key_free(private); /* We no longer need the challenge. */ BN_clear_free(challenge); @@ -963,6 +964,7 @@ try_password_authentication(char *prompt) return 0; } + char * chop(char *s) { @@ -1060,7 +1062,8 @@ ssh_exchange_identification() fatal("Protocol major versions differ: %d vs. %d", (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, remote_major); - + if (compat20) + packet_set_ssh2_format(); /* Send our own protocol version identification. */ snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, @@ -1122,7 +1125,8 @@ read_yes_or_no(const char *prompt, int defval) */ void -check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) +check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, + const char *user_hostfile, const char *system_hostfile) { Key *file_key; char *ip = NULL; @@ -1140,6 +1144,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) * essentially disables host authentication for localhost; however, * this is probably not a real problem. */ + /** hostaddr == 0! */ switch (hostaddr->sa_family) { case AF_INET: local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; @@ -1180,19 +1185,19 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) * Check if the host key is present in the user\'s list of known * hosts or in the systemwide list. */ - host_status = check_host_in_hostfile(options.user_hostfile, host, host_key, file_key); + host_status = check_host_in_hostfile(user_hostfile, host, host_key, file_key); if (host_status == HOST_NEW) - host_status = check_host_in_hostfile(options.system_hostfile, host, host_key, file_key); + host_status = check_host_in_hostfile(system_hostfile, host, host_key, file_key); /* * Also perform check for the ip address, skip the check if we are * localhost or the hostname was an ip address to begin with */ if (options.check_host_ip && !local && strcmp(host, ip)) { Key *ip_key = key_new(host_key->type); - ip_status = check_host_in_hostfile(options.user_hostfile, ip, host_key, ip_key); + ip_status = check_host_in_hostfile(user_hostfile, ip, host_key, ip_key); if (ip_status == HOST_NEW) - ip_status = check_host_in_hostfile(options.system_hostfile, ip, host_key, ip_key); + ip_status = check_host_in_hostfile(system_hostfile, ip, host_key, ip_key); if (host_status == HOST_CHANGED && (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) host_ip_differ = 1; @@ -1209,9 +1214,9 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) debug("Host '%.200s' is known and matches the host key.", host); if (options.check_host_ip) { if (ip_status == HOST_NEW) { - if (!add_host_to_hostfile(options.user_hostfile, ip, host_key)) + if (!add_host_to_hostfile(user_hostfile, ip, host_key)) log("Failed to add the host key for IP address '%.30s' to the list of known hosts (%.30s).", - ip, options.user_hostfile); + ip, user_hostfile); else log("Warning: Permanently added host key for IP address '%.30s' to the list of known hosts.", ip); @@ -1245,9 +1250,9 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) hostp = host; /* If not in strict mode, add the key automatically to the local known_hosts file. */ - if (!add_host_to_hostfile(options.user_hostfile, hostp, host_key)) + if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) log("Failed to add the host to the list of known hosts (%.500s).", - options.user_hostfile); + user_hostfile); else log("Warning: Permanently added '%.200s' to the list of known hosts.", hostp); @@ -1279,7 +1284,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) error("It is also possible that the host key has just been changed."); error("Please contact your system administrator."); error("Add correct host key in %.100s to get rid of this message.", - options.user_hostfile); + user_hostfile); /* * If strict host key checking is in use, the user will have @@ -1313,18 +1318,11 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) if (options.check_host_ip) xfree(ip); } -void -check_rsa_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key) -{ - Key k; - k.type = KEY_RSA; - k.rsa = host_key; - check_host_key(host, hostaddr, &k); -} /* * SSH2 key exchange */ + void ssh_kex2(char *host, struct sockaddr *hostaddr) { @@ -1435,11 +1433,12 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) /* key, cert */ server_host_key_blob = packet_get_string(&sbloblen); - server_host_key = dsa_serverkey_from_blob(server_host_key_blob, sbloblen); + server_host_key = dsa_key_from_blob(server_host_key_blob, sbloblen); if (server_host_key == NULL) fatal("cannot decode server_host_key_blob"); - check_host_key(host, hostaddr, server_host_key); + check_host_key(host, hostaddr, server_host_key, + options.user_hostfile2, options.system_hostfile2); /* DH paramter f, server public DH key */ dh_server_pub = BN_new(); @@ -1498,7 +1497,8 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) fprintf(stderr, "%02x", (hash[i])&0xff); fprintf(stderr, "\n"); #endif - dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20); + if (dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1) + fatal("dsa_verify failed for server_host_key"); key_free(server_host_key); kex_derive_keys(kex, hash, shared_secret); @@ -1507,6 +1507,11 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) /* have keys, free DH */ DH_free(dh); + /* save session id */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); + debug("Wait SSH2_MSG_NEWKEYS."); packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS); packet_done(); @@ -1530,19 +1535,103 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) /* * Authenticate user */ +int +ssh2_try_passwd(const char *server_user, const char *host, const char *service) +{ + char prompt[80]; + char *password; + + snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", + server_user, host); + password = read_passphrase(prompt, 0); + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(server_user); + packet_put_cstring(service); + packet_put_cstring("password"); + packet_put_char(0); + packet_put_cstring(password); + memset(password, 0, strlen(password)); + xfree(password); + packet_send(); + packet_write_wait(); + return 1; +} + +int +ssh2_try_pubkey(char *filename, + const char *server_user, const char *host, const char *service) +{ + Buffer b; + Key *k; + unsigned char *blob, *signature; + int bloblen, slen; + + debug("try pubkey: %s", filename); + + k = key_new(KEY_DSA); + if (!load_private_key(filename, "", k, NULL)) { + int success = 0; + char *passphrase; + char prompt[300]; + snprintf(prompt, sizeof prompt, + "Enter passphrase for DSA key '%.100s': ", + filename); + passphrase = read_passphrase(prompt, 0); + success = load_private_key(filename, passphrase, k, NULL); + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + if (!success) + return 0; + } + dsa_make_key_blob(k, &blob, &bloblen); + + /* data to be signed */ + buffer_init(&b); + buffer_append(&b, session_id2, session_id2_len); + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(&b, server_user); + buffer_put_cstring(&b, service); + buffer_put_cstring(&b, "publickey"); + buffer_put_char(&b, 1); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_string(&b, blob, bloblen); + + /* generate signature */ + dsa_sign(k, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); + key_free(k); +#ifdef DEBUG_DSS + buffer_dump(&b); +#endif + /* append signature */ + buffer_put_string(&b, signature, slen); + xfree(signature); + + /* skip session id and packet type */ + if (buffer_len(&b) < session_id2_len + 1) + fatal("ssh2_try_pubkey: internal error"); + buffer_consume(&b, session_id2_len + 1); + + /* put remaining data from buffer into packet */ + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_raw(buffer_ptr(&b), buffer_len(&b)); + buffer_free(&b); + + /* send */ + packet_send(); + packet_write_wait(); + return 1; +} + void -ssh_userauth2(int host_key_valid, RSA *own_host_key, - uid_t original_real_uid, char *host) +ssh_userauth2(const char *server_user, char *host) { int type; int plen; + int sent; unsigned int dlen; int partial; - struct passwd *pw; - char prompt[80]; - char *server_user, *local_user; + int i = 0; char *auths; - char *password; char *service = "ssh-connection"; /* service name */ debug("send SSH2_MSG_SERVICE_REQUEST"); @@ -1566,14 +1655,6 @@ ssh_userauth2(int host_key_valid, RSA *own_host_key, packet_done(); debug("got SSH2_MSG_SERVICE_ACCEPT"); - /*XX COMMONCODE: */ - /* Get local user name. Use it as server user if no user name was given. */ - pw = getpwuid(original_real_uid); - if (!pw) - fatal("User id %d not found from user database.", original_real_uid); - local_user = xstrdup(pw->pw_name); - server_user = options.user ? options.user : local_user; - /* INITIAL request for auth */ packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(server_user); @@ -1583,6 +1664,7 @@ ssh_userauth2(int host_key_valid, RSA *own_host_key, packet_write_wait(); for (;;) { + sent = 0; type = packet_read(&plen); if (type == SSH2_MSG_USERAUTH_SUCCESS) break; @@ -1595,23 +1677,25 @@ ssh_userauth2(int host_key_valid, RSA *own_host_key, packet_done(); if (partial) debug("partial success"); - if (strstr(auths, "password") == NULL) - fatal("passwd auth not supported: %s", auths); + if (strstr(auths, "publickey") != NULL) { + while (i < options.num_identity_files2) { + sent = ssh2_try_pubkey( + options.identity_files2[i++], + server_user, host, service); + if (sent) + break; + } + } + if (!sent) { + if (strstr(auths, "password") != NULL) { + sent = ssh2_try_passwd(server_user, host, service); + } else { + fatal("passwd auth not supported: %s", auths); + } + if (!sent) + fatal("no more auths: %s", auths); + } xfree(auths); - /* try passwd */ - snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", - server_user, host); - password = read_passphrase(prompt, 0); - packet_start(SSH2_MSG_USERAUTH_REQUEST); - packet_put_cstring(server_user); - packet_put_cstring(service); - packet_put_cstring("password"); - packet_put_char(0); - packet_put_cstring(password); - memset(password, 0, strlen(password)); - xfree(password); - packet_send(); - packet_write_wait(); } packet_done(); debug("ssh-userauth2 successfull"); @@ -1627,6 +1711,7 @@ ssh_kex(char *host, struct sockaddr *hostaddr) BIGNUM *key; RSA *host_key; RSA *public_key; + Key k; int bits, rbits; int ssh_cipher_default = SSH_CIPHER_3DES; unsigned char session_key[SSH_SESSION_KEY_LENGTH]; @@ -1691,8 +1776,10 @@ ssh_kex(char *host, struct sockaddr *hostaddr) packet_integrity_check(payload_len, 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, SSH_SMSG_PUBLIC_KEY); - - check_rsa_host_key(host, hostaddr, host_key); + k.type = KEY_RSA; + k.rsa = host_key; + check_host_key(host, hostaddr, &k, + options.user_hostfile, options.system_hostfile); client_flags = SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN; @@ -1819,20 +1906,17 @@ ssh_kex(char *host, struct sockaddr *hostaddr) * Authenticate user */ void -ssh_userauth(int host_key_valid, RSA *own_host_key, - uid_t original_real_uid, char *host) +ssh_userauth( + const char* local_user, + const char* server_user, + char *host, + int host_key_valid, RSA *own_host_key) { int i, type; int payload_len; - struct passwd *pw; - const char *server_user, *local_user; - /* Get local user name. Use it as server user if no user name was given. */ - pw = getpwuid(original_real_uid); - if (!pw) - fatal("User id %d not found from user database.", original_real_uid); - local_user = xstrdup(pw->pw_name); - server_user = options.user ? options.user : local_user; + if (supported_authentications == 0) + fatal("ssh_userauth: server supports no auth methods"); /* Send the name of the user to log in as on the server. */ packet_start(SSH_CMSG_USER); @@ -1951,6 +2035,7 @@ ssh_userauth(int host_key_valid, RSA *own_host_key, fatal("Permission denied."); /* NOTREACHED */ } + /* * Starts a dialog with the server, and authenticates the current user on the * server. This does not need any extra privileges. The basic connection @@ -1962,7 +2047,16 @@ void ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, struct sockaddr *hostaddr, uid_t original_real_uid) { + struct passwd *pw; char *host, *cp; + char *server_user, *local_user; + + /* Get local user name. Use it as server user if no user name was given. */ + pw = getpwuid(original_real_uid); + if (!pw) + fatal("User id %d not found from user database.", original_real_uid); + local_user = xstrdup(pw->pw_name); + server_user = options.user ? options.user : local_user; /* Convert the user-supplied hostname into all lowercase. */ host = xstrdup(orighost); @@ -1980,12 +2074,9 @@ ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, /* authenticate user */ if (compat20) { ssh_kex2(host, hostaddr); - ssh_userauth2(host_key_valid, own_host_key, original_real_uid, host); + ssh_userauth2(server_user, host); } else { - supported_authentications = 0; ssh_kex(host, hostaddr); - if (supported_authentications == 0) - fatal("supported_authentications == 0."); - ssh_userauth(host_key_valid, own_host_key, original_real_uid, host); + ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key); } } diff --git a/usr.bin/ssh/sshd.c b/usr.bin/ssh/sshd.c index b13347d83c4..3bd1b3237ae 100644 --- a/usr.bin/ssh/sshd.c +++ b/usr.bin/ssh/sshd.c @@ -14,7 +14,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.107 2000/04/19 07:05:50 deraadt Exp $"); +RCSID("$OpenBSD: sshd.c,v 1.108 2000/04/26 20:56:30 markus Exp $"); #include "xmalloc.h" #include "rsa.h" @@ -40,6 +40,7 @@ RCSID("$OpenBSD: sshd.c,v 1.107 2000/04/19 07:05:50 deraadt Exp $"); #include "auth.h" #include "myproposal.h" +#include "authfile.h" #ifdef LIBWRAP #include @@ -108,8 +109,9 @@ char *server_version_string = NULL; * not very useful. Currently, memory locking is not implemented. */ struct { - RSA *private_key; /* Private part of server key. */ + RSA *private_key; /* Private part of empheral server key. */ RSA *host_key; /* Private part of host key. */ + Key *dsa_host_key; /* Private DSA host key. */ } sensitive_data; /* @@ -128,6 +130,10 @@ RSA *public_key; /* session identifier, used by RSA-auth */ unsigned char session_id[16]; +/* same for ssh2 */ +unsigned char *session_id2 = NULL; +int session_id2_len = 0; + /* Prototypes for various functions defined later in this file. */ void do_ssh1_kex(); void do_ssh2_kex(); @@ -220,6 +226,7 @@ grace_alarm_handler(int sig) * Thus there should be no concurrency control/asynchronous execution * problems. */ +/* XXX do we really want this work to be done in a signal handler ? -m */ void key_regeneration_alarm(int sig) { @@ -340,6 +347,13 @@ sshd_exchange_identification(int sock_in, int sock_out) mismatch = 0; switch(remote_major) { case 1: + if (remote_minor == 99) { + if (options.protocol & SSH_PROTO_2) + enable_compat20(); + else + mismatch = 1; + break; + } if (!(options.protocol & SSH_PROTO_1)) { mismatch = 1; break; @@ -351,12 +365,6 @@ sshd_exchange_identification(int sock_in, int sock_out) /* note that this disables agent-forwarding */ enable_compat13(); } - if (remote_minor == 99) { - if (options.protocol & SSH_PROTO_2) - enable_compat20(); - else - mismatch = 1; - } break; case 2: if (options.protocol & SSH_PROTO_2) { @@ -382,6 +390,20 @@ sshd_exchange_identification(int sock_in, int sock_out) server_version_string, client_version_string); fatal_cleanup(); } + if (compat20) + packet_set_ssh2_format(); +} + + +void +destroy_sensitive_data(void) +{ + /* Destroy the private and public keys. They will no longer be needed. */ + RSA_free(public_key); + RSA_free(sensitive_data.private_key); + RSA_free(sensitive_data.host_key); + if (sensitive_data.dsa_host_key != NULL) + key_free(sensitive_data.dsa_host_key); } /* @@ -495,25 +517,12 @@ main(int ac, char **av) options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, !inetd_flag); - /* check if RSA support exists */ - if (rsa_alive() == 0) { - if (silentrsa == 0) - printf("sshd: no RSA support in libssl and libcrypto -- exiting. See ssl(8)\n"); - log("no RSA support in libssl and libcrypto -- exiting. See ssl(8)"); - exit(1); - } /* Read server configuration options from the configuration file. */ read_server_config(&options, config_file_name); /* Fill in default values for those options not explicitly set. */ fill_default_server_options(&options); - /* Check certain values for sanity. */ - if (options.server_key_bits < 512 || - options.server_key_bits > 32768) { - fprintf(stderr, "Bad server key size.\n"); - exit(1); - } /* Check that there are no remaining arguments. */ if (optind < ac) { fprintf(stderr, "Extra argument %s.\n", av[optind]); @@ -522,26 +531,81 @@ main(int ac, char **av) debug("sshd version %.100s", SSH_VERSION); - sensitive_data.host_key = RSA_new(); - errno = 0; - /* Load the host key. It must have empty passphrase. */ - if (!load_private_key(options.host_key_file, "", - sensitive_data.host_key, &comment)) { - error("Could not load host key: %.200s: %.100s", - options.host_key_file, strerror(errno)); + sensitive_data.dsa_host_key = NULL; + sensitive_data.host_key = NULL; + + /* check if RSA support exists */ + if ((options.protocol & SSH_PROTO_1) && + rsa_alive() == 0) { + if (silentrsa == 0) + fprintf(stderr, "sshd: no RSA support in libssl and libcrypto. See ssl(8)\n"); + log("no RSA support in libssl and libcrypto. See ssl(8)"); + log("Disabling protocol version 1"); + options.protocol &= ~SSH_PROTO_1; + } + /* Load the RSA/DSA host key. It must have empty passphrase. */ + if (options.protocol & SSH_PROTO_1) { + Key k; + sensitive_data.host_key = RSA_new(); + k.type = KEY_RSA; + k.rsa = sensitive_data.host_key; + errno = 0; + if (!load_private_key(options.host_key_file, "", &k, &comment)) { + error("Could not load host key: %.200s: %.100s", + options.host_key_file, strerror(errno)); + log("Disabling protocol version 1"); + options.protocol &= ~SSH_PROTO_1; + } + k.rsa = NULL; + xfree(comment); + } + if (options.protocol & SSH_PROTO_2) { + sensitive_data.dsa_host_key = key_new(KEY_DSA); + if (!load_private_key(options.dsa_key_file, "", sensitive_data.dsa_host_key, NULL)) { + error("Could not load DSA host key: %.200s", options.dsa_key_file); + log("Disabling protocol version 2"); + options.protocol &= ~SSH_PROTO_2; + } + } + if (! options.protocol & (SSH_PROTO_1|SSH_PROTO_2)) { + fprintf(stderr, "sshd: no hostkeys available -- exiting.\n"); + log("sshd: no hostkeys available -- exiting.\n"); exit(1); } - xfree(comment); - /* Initialize the log (it is reinitialized below in case we - forked). */ + /* Check certain values for sanity. */ + if (options.protocol & SSH_PROTO_1) { + if (options.server_key_bits < 512 || + options.server_key_bits > 32768) { + fprintf(stderr, "Bad server key size.\n"); + exit(1); + } + /* + * Check that server and host key lengths differ sufficiently. This + * is necessary to make double encryption work with rsaref. Oh, I + * hate software patents. I dont know if this can go? Niels + */ + if (options.server_key_bits > + BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && + options.server_key_bits < + BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { + options.server_key_bits = + BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; + debug("Forcing server key to %d bits to make it differ from host key.", + options.server_key_bits); + } + } + + /* Initialize the log (it is reinitialized below in case we forked). */ if (debug_flag && !inetd_flag) log_stderr = 1; log_init(av0, options.log_level, options.log_facility, log_stderr); - /* If not in debugging mode, and not started from inetd, - disconnect from the controlling terminal, and fork. The - original process exits. */ + /* + * If not in debugging mode, and not started from inetd, disconnect + * from the controlling terminal, and fork. The original process + * exits. + */ if (!debug_flag && !inetd_flag) { #ifdef TIOCNOTTY int fd; @@ -561,18 +625,6 @@ main(int ac, char **av) /* Reinitialize the log (because of the fork above). */ log_init(av0, options.log_level, options.log_facility, log_stderr); - /* Check that server and host key lengths differ sufficiently. - This is necessary to make double encryption work with rsaref. - Oh, I hate software patents. I dont know if this can go? Niels */ - if (options.server_key_bits > - BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && - options.server_key_bits < - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { - options.server_key_bits = - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; - debug("Forcing server key to %d bits to make it differ from host key.", - options.server_key_bits); - } /* Do not display messages to stdout in RSA code. */ rsa_set_verbose(0); @@ -590,20 +642,22 @@ main(int ac, char **av) s2 = dup(s1); sock_in = dup(0); sock_out = dup(1); - /* We intentionally do not close the descriptors 0, 1, and 2 - as our code for setting the descriptors won\'t work - if ttyfd happens to be one of those. */ + /* + * We intentionally do not close the descriptors 0, 1, and 2 + * as our code for setting the descriptors won\'t work if + * ttyfd happens to be one of those. + */ debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); - public_key = RSA_new(); - sensitive_data.private_key = RSA_new(); - - /* XXX check options.protocol */ - log("Generating %d bit RSA key.", options.server_key_bits); - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - log("RSA key generation complete."); + if (options.protocol & SSH_PROTO_1) { + public_key = RSA_new(); + sensitive_data.private_key = RSA_new(); + log("Generating %d bit RSA key.", options.server_key_bits); + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + arc4random_stir(); + log("RSA key generation complete."); + } } else { for (ai = options.listen_addrs; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) @@ -679,19 +733,20 @@ main(int ac, char **av) fclose(f); } } + if (options.protocol & SSH_PROTO_1) { + public_key = RSA_new(); + sensitive_data.private_key = RSA_new(); - public_key = RSA_new(); - sensitive_data.private_key = RSA_new(); - - log("Generating %d bit RSA key.", options.server_key_bits); - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - log("RSA key generation complete."); + log("Generating %d bit RSA key.", options.server_key_bits); + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + arc4random_stir(); + log("RSA key generation complete."); - /* Schedule server key regeneration alarm. */ - signal(SIGALRM, key_regeneration_alarm); - alarm(options.key_regeneration_time); + /* Schedule server key regeneration alarm. */ + signal(SIGALRM, key_regeneration_alarm); + alarm(options.key_regeneration_time); + } /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ signal(SIGHUP, sighup_handler); @@ -1059,9 +1114,7 @@ do_ssh1_kex() sensitive_data.private_key->n); /* Destroy the private and public keys. They will no longer be needed. */ - RSA_free(public_key); - RSA_free(sensitive_data.private_key); - RSA_free(sensitive_data.host_key); + destroy_sensitive_data(); /* * Extract session key from the decrypted integer. The key is in the @@ -1120,7 +1173,6 @@ do_ssh2_kex() unsigned char *kbuf; unsigned char *hash; Kex *kex; - Key *server_host_key; char *cprop[PROPOSAL_MAX]; char *sprop[PROPOSAL_MAX]; @@ -1221,8 +1273,7 @@ do_ssh2_kex() memset(kbuf, 0, klen); xfree(kbuf); - server_host_key = dsa_get_serverkey(options.dsa_key_file); - dsa_make_serverkey_blob(server_host_key, &server_host_key_blob, &sbloblen); + dsa_make_key_blob(sensitive_data.dsa_host_key, &server_host_key_blob, &sbloblen); /* calc H */ /* XXX depends on 'kex' */ hash = kex_hash( @@ -1245,10 +1296,17 @@ do_ssh2_kex() fprintf(stderr, "%02x", (hash[i])&0xff); fprintf(stderr, "\n"); #endif + /* save session id := H */ + /* XXX hashlen depends on KEX */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); + /* sign H */ - dsa_sign(server_host_key, &signature, &slen, hash, 20); - /* hashlen depends on KEX */ - key_free(server_host_key); + /* XXX hashlen depends on KEX */ + dsa_sign(sensitive_data.dsa_host_key, &signature, &slen, hash, 20); + + destroy_sensitive_data(); /* send server hostkey, DH pubkey 'f' and singed H */ packet_start(SSH2_MSG_KEXDH_REPLY); diff --git a/usr.bin/ssh/uuencode.c b/usr.bin/ssh/uuencode.c new file mode 100644 index 00000000000..22cad305884 --- /dev/null +++ b/usr.bin/ssh/uuencode.c @@ -0,0 +1,117 @@ +/* + * base-64 encoding pinched from lynx2-7-2, who pinched it from rpem. + * Originally written by Mark Riordan 12 August 1990 and 17 Feb 1991 + * and placed in the public domain. + * + * Dug Song + */ + +#include "includes.h" +#include "xmalloc.h" + +char six2pr[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +unsigned char pr2six[256]; + +int +uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded) +{ + /* ENC is the basic 1 character encoding function to make a char printing */ +#define ENC(c) six2pr[c] + + register char *outptr = bufcoded; + unsigned int i; + + for (i = 0; i < nbytes; i += 3) { + *(outptr++) = ENC(*bufin >> 2); /* c1 */ + *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /* c2 */ + *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)); /* c3 */ + *(outptr++) = ENC(bufin[2] & 077); /* c4 */ + bufin += 3; + } + if (i == nbytes + 1) { + outptr[-1] = '='; + } else if (i == nbytes + 2) { + outptr[-1] = '='; + outptr[-2] = '='; + } + *outptr = '\0'; + return (outptr - bufcoded); +} + +int +uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize) +{ + /* single character decode */ +#define DEC(c) pr2six[(unsigned char)c] +#define MAXVAL 63 + + static int first = 1; + int nbytesdecoded, j; + const char *bufin = bufcoded; + register unsigned char *bufout = bufplain; + register int nprbytes; + + /* If this is the first call, initialize the mapping table. */ + if (first) { + first = 0; + for (j = 0; j < 256; j++) + pr2six[j] = MAXVAL + 1; + for (j = 0; j < 64; j++) + pr2six[(unsigned char) six2pr[j]] = (unsigned char) j; + } + /* Strip leading whitespace. */ + while (*bufcoded == ' ' || *bufcoded == '\t') + bufcoded++; + + /* + * Figure out how many characters are in the input buffer. If this + * would decode into more bytes than would fit into the output + * buffer, adjust the number of input bytes downwards. + */ + bufin = bufcoded; + while (DEC(*(bufin++)) <= MAXVAL); + nprbytes = bufin - bufcoded - 1; + nbytesdecoded = ((nprbytes + 3) / 4) * 3; + if (nbytesdecoded > outbufsize) + nprbytes = (outbufsize * 4) / 3; + + bufin = bufcoded; + + while (nprbytes > 0) { + *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4); + *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2); + *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3])); + bufin += 4; + nprbytes -= 4; + } + if (nprbytes & 03) { + if (DEC(bufin[-2]) > MAXVAL) + nbytesdecoded -= 2; + else + nbytesdecoded -= 1; + } + return (nbytesdecoded); +} + +void +dump_base64(FILE *fp, unsigned char *data, int len) +{ + unsigned char *buf = xmalloc(2*len); + int i, n; + n = uuencode(data, len, buf); + for (i = 0; i < n; i++) { + fprintf(fp, "%c", buf[i]); + if (i % 70 == 69) + fprintf(fp, "\n"); + } + if (i % 70 != 69) + fprintf(fp, "\n"); + xfree(buf); +} diff --git a/usr.bin/ssh/uuencode.h b/usr.bin/ssh/uuencode.h new file mode 100644 index 00000000000..d3f4462845b --- /dev/null +++ b/usr.bin/ssh/uuencode.h @@ -0,0 +1,6 @@ +#ifndef UUENCODE_H +#define UUENCODE_H +int uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded); +int uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize); +void dump_base64(FILE *fp, unsigned char *data, int len); +#endif -- 2.20.1