From a8be9f800cf90e7d296951556739de21d2f93382 Mon Sep 17 00:00:00 2001 From: damien Date: Wed, 12 Apr 2000 10:17:38 +0000 Subject: [PATCH] - OpenBSD CVS updates: - [channels.c] repair x11-fwd - [sshconnect.c] fix passwd prompt for ssh2, less debugging output. - [clientloop.c compat.c dsa.c kex.c sshd.c] less debugging output - [kex.c kex.h sshconnect.c sshd.c] check for reasonable public DH values - [README.openssh2 cipher.c cipher.h compat.c compat.h readconf.c] [readconf.h servconf.c servconf.h ssh.c ssh.h sshconnect.c sshd.c] add Cipher and Protocol options to ssh/sshd, e.g.: ssh -o 'Protocol 1,2' if you prefer proto 1, ssh -o 'Ciphers arcfour,3des-cbc' - [sshd.c] print 1.99 only if server supports both --- ChangeLog | 18 ++++++++++ README.openssh2 | 15 +++++--- channels.c | 2 ++ cipher.c | 35 ++++++++++++++++--- cipher.h | 3 ++ clientloop.c | 2 +- compat.c | 31 ++++++++++++++++- compat.h | 7 ++++ dsa.c | 22 ++++++------ kex.c | 54 +++++++++++++++++++++-------- kex.h | 3 +- readconf.c | 29 +++++++++++++++- readconf.h | 2 ++ servconf.c | 65 ++++++++++++++++++++++------------- servconf.h | 2 ++ ssh.c | 7 ++-- ssh.h | 12 ++++--- sshconnect.c | 91 +++++++++++++++++++++++++++++++------------------ sshd.c | 76 +++++++++++++++++++++++++++-------------- 19 files changed, 347 insertions(+), 129 deletions(-) diff --git a/ChangeLog b/ChangeLog index 976c3834..af54ab6d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +20000412 + - OpenBSD CVS updates: + - [channels.c] + repair x11-fwd + - [sshconnect.c] + fix passwd prompt for ssh2, less debugging output. + - [clientloop.c compat.c dsa.c kex.c sshd.c] + less debugging output + - [kex.c kex.h sshconnect.c sshd.c] + check for reasonable public DH values + - [README.openssh2 cipher.c cipher.h compat.c compat.h readconf.c] + [readconf.h servconf.c servconf.h ssh.c ssh.h sshconnect.c sshd.c] + add Cipher and Protocol options to ssh/sshd, e.g.: + ssh -o 'Protocol 1,2' if you prefer proto 1, ssh -o 'Ciphers + arcfour,3des-cbc' + - [sshd.c] + print 1.99 only if server supports both + 20000408 - Avoid some compiler warnings in fake-get*.c - Add IPTOS macros for systems which lack them diff --git a/README.openssh2 b/README.openssh2 index 8bdf8bf0..18722b3c 100644 --- a/README.openssh2 +++ b/README.openssh2 @@ -1,5 +1,14 @@ $Id$ +howto: + 1) generate server key: + $ umask 077 + $ openssl dsaparam 1024 -out dsa1024.pem + $ openssl gendsa -out /etc/ssh_dsa_key dsa1024.pem -rand /dev/arandom + 2) enable ssh2: + server: add 'Protocol 2,1' to /etc/sshd_config + client: ssh -o 'Protocol 2,1', or add to .ssh/config + works: secsh-transport: works w/o rekey proposal exchange, i.e. different enc/mac/comp per direction @@ -11,11 +20,7 @@ works: tcp-forwarding: -L works dss: verification works, key database in ~/.ssh/known_hosts with bits == 0 hack - dss: signature works, keygen w/ openssl: - $ umask 077 - $ openssl dsaparam 1024 -out dsa1024.pem - $ openssl gendsa -out /etc/ssh_dsa_key dsa1024.pem -rand /dev/arandom - start sshd with '-2' flag + dss: signature works, keygen w/ openssl client interops w/ sshd2, lshd server interops w/ ssh2, lsh, ssh.com's Windows client, SecureCRT server supports multiple concurrent sessions (e.g. with SSH.com Windows client) diff --git a/channels.c b/channels.c index ccc7e5c0..60c05607 100644 --- a/channels.c +++ b/channels.c @@ -437,6 +437,7 @@ channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) if (ret == 1) { /* Start normal processing for the channel. */ c->type = SSH_CHANNEL_OPEN; + channel_pre_open_13(c, readset, writeset); } else if (ret == -1) { /* * We have received an X11 connection that has bad @@ -460,6 +461,7 @@ channel_pre_x11_open_15(Channel *c, fd_set * readset, fd_set * writeset) int ret = x11_open_helper(c); if (ret == 1) { c->type = SSH_CHANNEL_OPEN; + channel_pre_open_15(c, readset, writeset); } else if (ret == -1) { debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); chan_read_failed(c); diff --git a/cipher.c b/cipher.c index d38543b1..c7925766 100644 --- a/cipher.c +++ b/cipher.c @@ -16,7 +16,7 @@ RCSID("$Id$"); #include "ssh.h" #include "cipher.h" -#include "config.h" +#include "xmalloc.h" #ifdef HAVE_OPENSSL #include @@ -26,7 +26,9 @@ RCSID("$Id$"); #endif /* - * What kind of tripple DES are these 2 routines? + * This is used by SSH1: + * + * What kind of triple DES are these 2 routines? * * Why is there a redundant initialization vector? * @@ -81,7 +83,7 @@ SSH_3CBC_DECRYPT(des_key_schedule ks1, } /* - * SSH uses a variation on Blowfish, all bytes must be swapped before + * SSH1 uses a variation on Blowfish, all bytes must be swapped before * and after encryption/decryption. Thus the swap_bytes stuff (yuk). */ static void @@ -167,10 +169,34 @@ cipher_name(int cipher) { if (cipher < 0 || cipher >= sizeof(cipher_names) / sizeof(cipher_names[0]) || cipher_names[cipher] == NULL) - fatal("cipher_name: bad cipher number: %d", cipher); + fatal("cipher_name: bad cipher name: %d", cipher); return cipher_names[cipher]; } +/* Returns 1 if the name of the ciphers are valid. */ + +#define CIPHER_SEP "," +int +ciphers_valid(const char *names) +{ + char *ciphers; + char *p; + int i; + + if (strcmp(names, "") == 0) + return 0; + ciphers = xstrdup(names); + for ((p = strtok(ciphers, CIPHER_SEP)); p; (p = strtok(NULL, CIPHER_SEP))) { + i = cipher_number(p); + if (i == -1 || !(cipher_mask2() & (1 << i))) { + xfree(ciphers); + return 0; + } + } + xfree(ciphers); + return 1; +} + /* * Parses the name of the cipher. Returns the number of the corresponding * cipher, or -1 on error. @@ -271,7 +297,6 @@ cipher_set_key(CipherContext *context, int cipher, const unsigned char *key, memset(padded, 0, sizeof(padded)); } - void cipher_set_key_iv(CipherContext * context, int cipher, const unsigned char *key, int keylen, diff --git a/cipher.h b/cipher.h index aa6a6058..3d390938 100644 --- a/cipher.h +++ b/cipher.h @@ -88,6 +88,9 @@ const char *cipher_name(int cipher); */ int cipher_number(const char *name); +/* returns 1 if all ciphers are supported (ssh2 only) */ +int ciphers_valid(const char *names); + /* * Selects the cipher to use and sets the key. If for_encryption is true, * the key is setup for encryption; otherwise it is setup for decryption. diff --git a/clientloop.c b/clientloop.c index d03fa3c4..5d599a28 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1013,7 +1013,7 @@ client_input_channel_req(int id, void *arg) rtype = packet_get_string(&len); reply = packet_get_char(); - log("session_input_channel_req: rtype %s reply %d", rtype, reply); + debug("session_input_channel_req: rtype %s reply %d", rtype, reply); c = channel_lookup(id); if (c == NULL) diff --git a/compat.c b/compat.c index 5a633ff2..e3b6db2d 100644 --- a/compat.c +++ b/compat.c @@ -32,6 +32,8 @@ RCSID("$Id$"); #include "ssh.h" #include "packet.h" +#include "xmalloc.h" +#include "compat.h" int compat13 = 0; int compat20 = 0; @@ -65,9 +67,36 @@ compat_datafellows(const char *version) len = strlen(check[i]); if (strlen(version) >= len && (strncmp(version, check[i], len) == 0)) { - log("datafellows: %.200s", version); + verbose("datafellows: %.200s", version); datafellows = 1; return; } } } + +#define SEP "," +int +proto_spec(const char *spec) +{ + char *s = xstrdup(spec); + char *p; + int ret = SSH_PROTO_UNKNOWN; + + for ((p = strtok(s, SEP)); p; (p = strtok(NULL, SEP))) { + switch(atoi(p)) { + case 1: + if (ret == SSH_PROTO_UNKNOWN) + ret |= SSH_PROTO_1_PREFERRED; + ret |= SSH_PROTO_1; + break; + case 2: + ret |= SSH_PROTO_2; + break; + default: + log("ignoring bad proto spec: '%s'.", p); + break; + } + } + xfree(s); + return ret; +} diff --git a/compat.h b/compat.h index 3da8d784..4e3a6cfc 100644 --- a/compat.h +++ b/compat.h @@ -30,9 +30,16 @@ #ifndef COMPAT_H #define COMPAT_H + +#define SSH_PROTO_UNKNOWN 0x00 +#define SSH_PROTO_1 0x01 +#define SSH_PROTO_1_PREFERRED 0x02 +#define SSH_PROTO_2 0x04 + void enable_compat13(void); void enable_compat20(void); void compat_datafellows(const char *s); +int proto_spec(const char *spec); extern int compat13; extern int compat20; extern int datafellows; diff --git a/dsa.c b/dsa.c index a60840fa..3c1a5f46 100644 --- a/dsa.c +++ b/dsa.c @@ -80,7 +80,7 @@ dsa_serverkey_from_blob( buffer_append(&b, serverhostkey, serverhostkeylen); ktype = buffer_get_string(&b, NULL); if (strcmp(KEX_DSS, ktype) != 0) { - log("dsa_serverkey_from_blob: cannot handle type %s", ktype); + error("dsa_serverkey_from_blob: cannot handle type %s", ktype); key_free(key); return NULL; } @@ -90,10 +90,10 @@ dsa_serverkey_from_blob( buffer_get_bignum2(&b, dsa->pub_key); rlen = buffer_len(&b); if(rlen != 0) - log("dsa_serverkey_from_blob: remaining bytes in serverhostkey %d", rlen); + error("dsa_serverkey_from_blob: remaining bytes in serverhostkey %d", rlen); buffer_free(&b); - log("keytype %s", ktype); + debug("keytype %s", ktype); #ifdef DEBUG_DSS DSA_print_fp(stderr, dsa, 8); #endif @@ -172,7 +172,7 @@ dsa_sign( Buffer b; if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { - log("dsa_sign: no DSA key"); + error("dsa_sign: no DSA key"); return -1; } digest = xmalloc(evp_md->md_size); @@ -185,11 +185,11 @@ dsa_sign( rlen = BN_num_bytes(sig->r); slen = BN_num_bytes(sig->s); if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { - log("bad sig size %d %d", rlen, slen); + error("bad sig size %d %d", rlen, slen); DSA_SIG_free(sig); return -1; } - log("sig size %d %d", rlen, slen); + debug("sig size %d %d", rlen, slen); memset(sigblob, 0, SIGBLOB_LEN); BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen); @@ -197,7 +197,7 @@ dsa_sign( DSA_SIG_free(sig); if (datafellows) { - log("datafellows"); + debug("datafellows"); ret = xmalloc(SIGBLOB_LEN); memcpy(ret, sigblob, SIGBLOB_LEN); if (lenp != NULL) @@ -239,7 +239,7 @@ dsa_verify( int ret; if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { - log("dsa_verify: no DSA key"); + error("dsa_verify: no DSA key"); return -1; } @@ -248,7 +248,7 @@ dsa_verify( datafellows = 0; } - log("len %d datafellows %d", signaturelen, datafellows); + debug("len %d datafellows %d", signaturelen, datafellows); /* fetch signature */ if (datafellows) { @@ -262,7 +262,7 @@ dsa_verify( sigblob = (unsigned char *)buffer_get_string(&b, &len); rlen = buffer_len(&b); if(rlen != 0) - log("remaining bytes in signature %d", rlen); + error("remaining bytes in signature %d", rlen); buffer_free(&b); } @@ -305,6 +305,6 @@ dsa_verify( txt = "error"; break; } - log("dsa_verify: signature %s", txt); + debug("dsa_verify: signature %s", txt); return ret; } diff --git a/kex.c b/kex.c index 0e5f2576..3082b73f 100644 --- a/kex.c +++ b/kex.c @@ -43,8 +43,6 @@ RCSID("$Id$"); # include # include # include -# include -# include # include #endif /* HAVE_OPENSSL */ #if HAVE_SSL @@ -52,12 +50,9 @@ RCSID("$Id$"); # include # include # include -# include -# include # include #endif /* HAVE_SSL */ -#include "entropy.h" #include "kex.h" Buffer * @@ -85,8 +80,36 @@ kex_init(char *myproposal[PROPOSAL_MAX]) /* diffie-hellman-group1-sha1 */ +int +dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) +{ + int i; + int n = BN_num_bits(dh_pub); + int bits_set = 0; + + /* we only accept g==2 */ + if (!BN_is_word(dh->g, 2)) { + log("invalid DH base != 2"); + return 0; + } + if (dh_pub->neg) { + log("invalid public DH value: negativ"); + return 0; + } + for (i = 0; i <= n; i++) + if (BN_is_bit_set(dh_pub, i)) + bits_set++; + debug("bits set: %d/%d", bits_set, BN_num_bits(dh->p)); + + /* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */ + if (bits_set > 1 && (BN_cmp(dh_pub, dh->p) == -1)) + return 1; + log("invalid public DH value (%d/%d)", bits_set, BN_num_bits(dh->p)); + return 0; +} + DH * -new_dh_group1() +dh_new_group1() { static char *group1 = "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" @@ -96,22 +119,23 @@ new_dh_group1() "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381" "FFFFFFFF" "FFFFFFFF"; DH *dh; - int ret; + int ret, tries = 0; dh = DH_new(); if(dh == NULL) fatal("DH_new"); - ret = BN_hex2bn(&dh->p,group1); + ret = BN_hex2bn(&dh->p, group1); if(ret<0) fatal("BN_hex2bn"); dh->g = BN_new(); if(dh->g == NULL) fatal("DH_new g"); - BN_set_word(dh->g,2); - - seed_rng(); - if (DH_generate_key(dh) == 0) - fatal("DH_generate_key"); - + BN_set_word(dh->g, 2); + do { + if (DH_generate_key(dh) == 0) + fatal("DH_generate_key"); + if (tries++ > 10) + fatal("dh_new_group1: too many bad keys: giving up"); + } while (!dh_pub_is_valid(dh, dh->pub_key)); return dh; } @@ -356,7 +380,7 @@ kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server choose_enc (&k->enc [mode], cprop[nenc], sprop[nenc]); choose_mac (&k->mac [mode], cprop[nmac], sprop[nmac]); choose_comp(&k->comp[mode], cprop[ncomp], sprop[ncomp]); - log("kex: %s %s %s %s", + debug("kex: %s %s %s %s", ctos ? "client->server" : "server->client", k->enc[mode].name, k->mac[mode].name, diff --git a/kex.h b/kex.h index 81c41342..29e1e887 100644 --- a/kex.h +++ b/kex.h @@ -102,7 +102,8 @@ struct Kex { }; Buffer *kex_init(char *myproposal[PROPOSAL_MAX]); -DH *new_dh_group1(); +int dh_pub_is_valid(DH *dh, BIGNUM *dh_pub); +DH *dh_new_group1(); Kex *kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server); int kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret); void bignum_print(BIGNUM *b); diff --git a/readconf.c b/readconf.c index c1aa3ceb..b23e9269 100644 --- a/readconf.c +++ b/readconf.c @@ -21,6 +21,7 @@ RCSID("$Id$"); #include "readconf.h" #include "match.h" #include "xmalloc.h" +#include "compat.h" /* Format of the configuration file: @@ -103,7 +104,7 @@ typedef enum { oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication, - oUsePrivilegedPort, oLogLevel + oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol } OpCodes; /* Textual representations of the tokens. */ @@ -134,6 +135,8 @@ static struct { { "proxycommand", oProxyCommand }, { "port", oPort }, { "cipher", oCipher }, + { "ciphers", oCiphers }, + { "protocol", oProtocol }, { "remoteforward", oRemoteForward }, { "localforward", oLocalForward }, { "user", oUser }, @@ -444,6 +447,26 @@ parse_int: *intptr = value; break; + case oCiphers: + cp = strtok(NULL, WHITESPACE); + if (!ciphers_valid(cp)) + fatal("%.200s line %d: Bad cipher spec '%s'.", + filename, linenum, cp ? cp : ""); + if (*activep && options->ciphers == NULL) + options->ciphers = xstrdup(cp); + break; + + case oProtocol: + intptr = &options->protocol; + cp = strtok(NULL, WHITESPACE); + value = proto_spec(cp); + if (value == SSH_PROTO_UNKNOWN) + fatal("%.200s line %d: Bad protocol spec '%s'.", + filename, linenum, cp ? cp : ""); + if (*activep && *intptr == SSH_PROTO_UNKNOWN) + *intptr = value; + break; + case oLogLevel: intptr = (int *) &options->log_level; cp = strtok(NULL, WHITESPACE); @@ -616,6 +639,8 @@ initialize_options(Options * options) options->connection_attempts = -1; options->number_of_password_prompts = -1; options->cipher = -1; + options->ciphers = NULL; + options->protocol = SSH_PROTO_UNKNOWN; options->num_identity_files = 0; options->hostname = NULL; options->proxy_command = NULL; @@ -689,6 +714,8 @@ fill_default_options(Options * options) /* Selected in ssh_login(). */ if (options->cipher == -1) options->cipher = SSH_CIPHER_NOT_SET; + if (options->protocol == SSH_PROTO_UNKNOWN) + options->protocol = SSH_PROTO_1; if (options->num_identity_files == 0) { options->identity_files[0] = xmalloc(2 + strlen(SSH_CLIENT_IDENTITY) + 1); diff --git a/readconf.h b/readconf.h index 29dd4f0f..fc774197 100644 --- a/readconf.h +++ b/readconf.h @@ -64,6 +64,8 @@ typedef struct { int number_of_password_prompts; /* Max number of password * prompts. */ int cipher; /* Cipher to use. */ + char *ciphers; /* Ciphers in order of preference. */ + int protocol; /* Protocol in order of preference. */ char *hostname; /* Real host to connect. */ char *proxy_command; /* Proxy command for connecting the host. */ char *user; /* User to log in as. */ diff --git a/servconf.c b/servconf.c index b3bf54cf..6c5b4e74 100644 --- a/servconf.c +++ b/servconf.c @@ -17,6 +17,7 @@ RCSID("$Id$"); #include "ssh.h" #include "servconf.h" #include "xmalloc.h" +#include "compat.h" /* add listen address */ void add_listen_addr(ServerOptions *options, char *addr); @@ -68,6 +69,8 @@ initialize_server_options(ServerOptions *options) options->num_deny_users = 0; options->num_allow_groups = 0; options->num_deny_groups = 0; + options->ciphers = NULL; + options->protocol = SSH_PROTO_UNKNOWN; } void @@ -139,6 +142,8 @@ fill_default_server_options(ServerOptions *options) options->permit_empty_passwd = 0; if (options->use_login == -1) options->use_login = 0; + if (options->protocol == SSH_PROTO_UNKNOWN) + options->protocol = SSH_PROTO_1; } #define WHITESPACE " \t\r\n" @@ -162,7 +167,7 @@ typedef enum { sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail, sUseLogin, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, - sIgnoreUserKnownHosts, sDSAKeyFile + sIgnoreUserKnownHosts, sDSAKeyFile, sCiphers, sProtocol } ServerOpCodes; /* Textual representation of the tokens. */ @@ -211,6 +216,8 @@ static struct { { "denyusers", sDenyUsers }, { "allowgroups", sAllowGroups }, { "denygroups", sDenyGroups }, + { "ciphers", sCiphers }, + { "protocol", sProtocol }, { NULL, 0 } }; @@ -494,7 +501,7 @@ parse_flag: value = log_facility_number(cp); if (value == (SyslogFacility) - 1) fatal("%.200s line %d: unsupported log facility '%s'\n", - filename, linenum, cp ? cp : ""); + filename, linenum, cp ? cp : ""); if (*intptr == -1) *intptr = (SyslogFacility) value; break; @@ -505,55 +512,67 @@ parse_flag: value = log_level_number(cp); if (value == (LogLevel) - 1) fatal("%.200s line %d: unsupported log level '%s'\n", - filename, linenum, cp ? cp : ""); + filename, linenum, cp ? cp : ""); if (*intptr == -1) *intptr = (LogLevel) value; break; case sAllowUsers: while ((cp = strtok(NULL, WHITESPACE))) { - if (options->num_allow_users >= MAX_ALLOW_USERS) { - fprintf(stderr, "%s line %d: too many allow users.\n", - filename, linenum); - exit(1); - } + if (options->num_allow_users >= MAX_ALLOW_USERS) + fatal("%s line %d: too many allow users.\n", + filename, linenum); options->allow_users[options->num_allow_users++] = xstrdup(cp); } break; case sDenyUsers: while ((cp = strtok(NULL, WHITESPACE))) { - if (options->num_deny_users >= MAX_DENY_USERS) { - fprintf(stderr, "%s line %d: too many deny users.\n", - filename, linenum); - exit(1); - } + if (options->num_deny_users >= MAX_DENY_USERS) + fatal( "%s line %d: too many deny users.\n", + filename, linenum); options->deny_users[options->num_deny_users++] = xstrdup(cp); } break; case sAllowGroups: while ((cp = strtok(NULL, WHITESPACE))) { - if (options->num_allow_groups >= MAX_ALLOW_GROUPS) { - fprintf(stderr, "%s line %d: too many allow groups.\n", - filename, linenum); - exit(1); - } + if (options->num_allow_groups >= MAX_ALLOW_GROUPS) + fatal("%s line %d: too many allow groups.\n", + filename, linenum); options->allow_groups[options->num_allow_groups++] = xstrdup(cp); } break; case sDenyGroups: while ((cp = strtok(NULL, WHITESPACE))) { - if (options->num_deny_groups >= MAX_DENY_GROUPS) { - fprintf(stderr, "%s line %d: too many deny groups.\n", - filename, linenum); - exit(1); - } + if (options->num_deny_groups >= MAX_DENY_GROUPS) + fatal("%s line %d: too many deny groups.\n", + filename, linenum); options->deny_groups[options->num_deny_groups++] = xstrdup(cp); } break; + case sCiphers: + cp = strtok(NULL, WHITESPACE); + if (!ciphers_valid(cp)) + fatal("%s line %d: Bad cipher spec '%s'.", + filename, linenum, cp ? cp : ""); + if (options->ciphers == NULL) + options->ciphers = xstrdup(cp); + break; + + case sProtocol: + intptr = &options->protocol; + cp = strtok(NULL, WHITESPACE); + value = proto_spec(cp); + if (value == SSH_PROTO_UNKNOWN) + fatal("%s line %d: Bad protocol spec '%s'.", + filename, linenum, cp ? cp : ""); + if (*intptr == SSH_PROTO_UNKNOWN) + *intptr = value; + break; + default: fprintf(stderr, "%s line %d: Missing handler for opcode %s (%d)\n", filename, linenum, cp, opcode); diff --git a/servconf.h b/servconf.h index de5383a8..792868c2 100644 --- a/servconf.h +++ b/servconf.h @@ -48,6 +48,8 @@ typedef struct { * searching at */ int strict_modes; /* If true, require string home dir modes. */ int keepalives; /* If true, set SO_KEEPALIVE. */ + char *ciphers; /* Ciphers in order of preference. */ + int protocol; /* Protocol in order of preference. */ SyslogFacility log_facility; /* Facility for system logging. */ LogLevel log_level; /* Level for system logging. */ int rhosts_authentication; /* If true, permit rhosts diff --git a/ssh.c b/ssh.c index afdad173..a387d66c 100644 --- a/ssh.c +++ b/ssh.c @@ -42,6 +42,7 @@ int IPv4or6 = AF_UNSPEC; /* Flag indicating whether debug mode is on. This can be set on the command line. */ int debug_flag = 0; +/* Flag indicating whether a tty should be allocated */ int tty_flag = 0; /* don't exec a shell */ @@ -336,8 +337,10 @@ main(int ac, char **av) case 'v': case 'V': - fprintf(stderr, "SSH Version %s, protocol version %d.%d.\n", - SSH_VERSION, PROTOCOL_MAJOR, PROTOCOL_MINOR); + fprintf(stderr, "SSH Version %s, protocol versions %d.%d/%d.%d.\n", + SSH_VERSION, + PROTOCOL_MAJOR_1, PROTOCOL_MINOR_1, + PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2); fprintf(stderr, "Compiled with SSL (0x%8.8lx).\n", SSLeay()); if (opt == 'V') exit(0); diff --git a/ssh.h b/ssh.h index e1d07dee..47299b62 100644 --- a/ssh.h +++ b/ssh.h @@ -54,14 +54,16 @@ /* * Major protocol version. Different version indicates major incompatiblity * that prevents communication. - */ -#define PROTOCOL_MAJOR 1 - -/* + * * Minor protocol version. Different version indicates minor incompatibility * that does not prevent interoperation. */ -#define PROTOCOL_MINOR 5 +#define PROTOCOL_MAJOR_1 1 +#define PROTOCOL_MINOR_1 5 + +/* We support both SSH1 and SSH2 */ +#define PROTOCOL_MAJOR_2 2 +#define PROTOCOL_MINOR_2 0 /* * Name for the service. The port named by this service overrides the diff --git a/sshconnect.c b/sshconnect.c index 2f949609..167b8e63 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -10,7 +10,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshconnect.c,v 1.61 2000/04/04 21:37:27 markus Exp $"); +RCSID("$OpenBSD: sshconnect.c,v 1.65 2000/04/12 07:56:16 markus Exp $"); #ifdef HAVE_OPENSSL #include @@ -993,7 +993,7 @@ void ssh_exchange_identification() { char buf[256], remote_version[256]; /* must be same size! */ - int remote_major, remote_minor, i; + int remote_major, remote_minor, i, mismatch; int connection_in = packet_get_connection_in(); int connection_out = packet_get_connection_out(); @@ -1027,39 +1027,51 @@ ssh_exchange_identification() debug("Remote protocol version %d.%d, remote software version %.100s", remote_major, remote_minor, remote_version); -/*** XXX option for disabling 2.0 or 1.5 */ compat_datafellows(remote_version); - - /* Check if the remote protocol version is too old. */ - if (remote_major == 1 && remote_minor < 3) - fatal("Remote machine has too old SSH software version."); - - /* We speak 1.3, too. */ - if (remote_major == 1 && remote_minor == 3) { - enable_compat13(); - if (options.forward_agent) { - log("Agent forwarding disabled for protocol 1.3"); - options.forward_agent = 0; + mismatch = 0; + + switch(remote_major) { + case 1: + if (remote_minor == 99 && + (options.protocol & SSH_PROTO_2) && + !(options.protocol & SSH_PROTO_1_PREFERRED)) { + enable_compat20(); + break; } + if (!(options.protocol & SSH_PROTO_1)) { + mismatch = 1; + break; + } + if (remote_minor < 3) { + fatal("Remote machine has too old SSH software version."); + } else if (remote_minor == 3) { + /* We speak 1.3, too. */ + enable_compat13(); + if (options.forward_agent) { + log("Agent forwarding disabled for protocol 1.3"); + options.forward_agent = 0; + } + } + break; + case 2: + if (options.protocol & SSH_PROTO_2) { + enable_compat20(); + break; + } + /* FALLTHROUGH */ + default: + mismatch = 1; + break; } - if ((remote_major == 2 && remote_minor == 0) || - (remote_major == 1 && remote_minor == 99)) { - enable_compat20(); - } -#if 0 - /* - * Removed for now, to permit compatibility with latter versions. The - * server will reject our version and disconnect if it doesn't - * support it. - */ - if (remote_major != PROTOCOL_MAJOR) + if (mismatch) fatal("Protocol major versions differ: %d vs. %d", - PROTOCOL_MAJOR, remote_major); -#endif + (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, + remote_major); + /* Send our own protocol version identification. */ snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", - compat20 ? 2 : PROTOCOL_MAJOR, - compat20 ? 0 : PROTOCOL_MINOR, + compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, + compat20 ? PROTOCOL_MINOR_2 : PROTOCOL_MINOR_1, SSH_VERSION); if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) fatal("write: %.100s", strerror(errno)); @@ -1350,11 +1362,15 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) /* KEXINIT */ debug("Sending KEX init."); - if (options.cipher == SSH_CIPHER_ARCFOUR || + if (options.ciphers != NULL) { + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; + } else if ( + options.cipher == SSH_CIPHER_ARCFOUR || options.cipher == SSH_CIPHER_3DES_CBC || options.cipher == SSH_CIPHER_CAST128_CBC || options.cipher == SSH_CIPHER_BLOWFISH_CBC) { - myproposal[PROPOSAL_ENC_ALGS_CTOS] = cipher_name(options.cipher); + myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = cipher_name(options.cipher); } if (options.compression) { @@ -1404,7 +1420,7 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) debug("Sending SSH2_MSG_KEXDH_INIT."); /* generate and send 'e', client DH public key */ - dh = new_dh_group1(); + dh = dh_new_group1(); packet_start(SSH2_MSG_KEXDH_INIT); packet_put_bignum2(dh->pub_key); packet_send(); @@ -1451,6 +1467,9 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) /* signed H */ signature = packet_get_string(&slen); + if (!dh_pub_is_valid(dh, dh_server_pub)) + packet_disconnect("bad server public DH value"); + klen = DH_size(dh); kbuf = xmalloc(klen); kout = DH_compute_key(kbuf, dh_server_pub, dh); @@ -1507,12 +1526,13 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) packet_write_wait(); debug("done: send SSH2_MSG_NEWKEYS."); +#ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ packet_start(SSH2_MSG_IGNORE); packet_put_cstring("markus"); packet_send(); packet_write_wait(); - +#endif debug("done: KEX2."); } /* @@ -1527,6 +1547,7 @@ ssh_userauth2(int host_key_valid, RSA *own_host_key, unsigned int dlen; int partial; struct passwd *pw; + char prompt[80]; char *server_user, *local_user; char *auths; char *password; @@ -1578,7 +1599,9 @@ ssh_userauth2(int host_key_valid, RSA *own_host_key, fatal("passwd auth not supported: %s", auths); xfree(auths); /* try passwd */ - password = read_passphrase("password: ", 0); + 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); diff --git a/sshd.c b/sshd.c index 44782e39..266146bf 100644 --- a/sshd.c +++ b/sshd.c @@ -14,7 +14,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.99 2000/04/07 09:17:39 markus Exp $"); +RCSID("$OpenBSD: sshd.c,v 1.103 2000/04/12 08:11:36 markus Exp $"); #include "xmalloc.h" #include "rsa.h" @@ -77,9 +77,6 @@ int IPv4or6 = AF_INET; int IPv4or6 = AF_UNSPEC; #endif -/* Flag indicating whether SSH2 is enabled */ -int allow_ssh2 = 0; - /* * Debug mode flag. This can be set on the command line. If debug * mode is enabled, extra debugging output will be sent to the system @@ -284,16 +281,25 @@ chop(char *s) void sshd_exchange_identification(int sock_in, int sock_out) { - int i; + int i, mismatch; int remote_major, remote_minor; + int major, minor; char *s; char buf[256]; /* Must not be larger than remote_version. */ char remote_version[256]; /* Must be at least as big as buf. */ - snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", - allow_ssh2 ? 1 : PROTOCOL_MAJOR, - allow_ssh2 ? 99 : PROTOCOL_MINOR, - SSH_VERSION); + if ((options.protocol & SSH_PROTO_1) && + (options.protocol & SSH_PROTO_2)) { + major = PROTOCOL_MAJOR_1; + minor = 99; + } else if (options.protocol & SSH_PROTO_2) { + major = PROTOCOL_MAJOR_2; + minor = PROTOCOL_MINOR_2; + } else { + major = PROTOCOL_MAJOR_1; + minor = PROTOCOL_MINOR_1; + } + snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); server_version_string = xstrdup(buf); if (client_version_string == NULL) { @@ -314,7 +320,6 @@ sshd_exchange_identification(int sock_in, int sock_out) buf[i] = '\n'; buf[i + 1] = 0; continue; - //break; } if (buf[i] == '\n') { /* buf[i] == '\n' */ @@ -345,8 +350,13 @@ sshd_exchange_identification(int sock_in, int sock_out) compat_datafellows(remote_version); + mismatch = 0; switch(remote_major) { case 1: + if (!(options.protocol & SSH_PROTO_1)) { + mismatch = 1; + break; + } if (remote_minor < 3) { packet_disconnect("Your ssh version is too old and" "is no longer supported. Please install a newer version."); @@ -354,27 +364,37 @@ sshd_exchange_identification(int sock_in, int sock_out) /* note that this disables agent-forwarding */ enable_compat13(); } - if (remote_minor != 99) - break; - /* FALLTHROUGH */ + if (remote_minor == 99) { + if (options.protocol & SSH_PROTO_2) + enable_compat20(); + else + mismatch = 1; + } + break; case 2: - if (allow_ssh2) { + if (options.protocol & SSH_PROTO_2) { enable_compat20(); break; } /* FALLTHROUGH */ default: + mismatch = 1; + break; + } + chop(server_version_string); + chop(client_version_string); + debug("Local version string %.200s", server_version_string); + + if (mismatch) { s = "Protocol major versions differ.\n"; (void) atomicio(write, sock_out, s, strlen(s)); close(sock_in); close(sock_out); - log("Protocol major versions differ for %s: %d vs. %d", - get_remote_ipaddr(), PROTOCOL_MAJOR, remote_major); + log("Protocol major versions differ for %s: %.200s vs. %.200s", + get_remote_ipaddr(), + server_version_string, client_version_string); fatal_cleanup(); - break; } - chop(server_version_string); - chop(client_version_string); } /* @@ -410,11 +430,8 @@ main(int ac, char **av) initialize_server_options(&options); /* Parse command-line arguments. */ - while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ246")) != EOF) { + while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ46")) != EOF) { switch (opt) { - case '2': - allow_ssh2 = 1; - break; case '4': IPv4or6 = AF_INET; break; @@ -593,6 +610,7 @@ main(int ac, char **av) 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); @@ -1126,6 +1144,11 @@ do_ssh2_kex() /* KEXINIT */ + if (options.ciphers != NULL) { + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; + } + debug("Sending KEX init."); for (i = 0; i < PROPOSAL_MAX; i++) @@ -1185,7 +1208,7 @@ do_ssh2_kex() #endif /* generate DH key */ - dh = new_dh_group1(); /* XXX depends on 'kex' */ + dh = dh_new_group1(); /* XXX depends on 'kex' */ #ifdef DEBUG_KEXDH fprintf(stderr, "\np= "); @@ -1196,6 +1219,8 @@ do_ssh2_kex() bignum_print(dh->pub_key); fprintf(stderr, "\n"); #endif + if (!dh_pub_is_valid(dh, dh_client_pub)) + packet_disconnect("bad client public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); @@ -1267,11 +1292,12 @@ do_ssh2_kex() packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS); debug("GOT SSH2_MSG_NEWKEYS."); +#ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ packet_start(SSH2_MSG_IGNORE); packet_put_cstring("markus"); packet_send(); packet_write_wait(); - +#endif debug("done: KEX2."); } -- 2.45.2