*/
#include "includes.h"
-RCSID("$OpenBSD: sshconnect2.c,v 1.51 2001/03/08 21:42:33 markus Exp $");
+RCSID("$OpenBSD: sshconnect2.c,v 1.67 2001/04/05 10:42:56 markus Exp $");
#include <openssl/bn.h>
#include <openssl/md5.h>
#include "sshconnect.h"
#include "authfile.h"
#include "cli.h"
-#include "dispatch.h"
+#include "dh.h"
#include "authfd.h"
#include "log.h"
#include "readconf.h"
#include "readpass.h"
-
-void ssh_dh1_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *);
-void ssh_dhgex_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *);
+#include "match.h"
+#include "dispatch.h"
/* import */
extern char *client_version_string;
u_char *session_id2 = NULL;
int session_id2_len = 0;
+char *xxx_host;
+struct sockaddr *xxx_hostaddr;
+
+Kex *xxx_kex = NULL;
+
+int
+check_host_key_callback(Key *hostkey)
+{
+ check_host_key(xxx_host, xxx_hostaddr, hostkey,
+ options.user_hostfile2, options.system_hostfile2);
+ return 0;
+}
+
void
ssh_kex2(char *host, struct sockaddr *hostaddr)
{
- int i, plen;
Kex *kex;
- Buffer *client_kexinit, *server_kexinit;
- char *sprop[PROPOSAL_MAX];
+
+ xxx_host = host;
+ xxx_hostaddr = hostaddr;
if (options.ciphers == (char *)-1) {
log("No valid ciphers for protocol version 2 given, using defaults.");
myproposal[PROPOSAL_ENC_ALGS_CTOS] =
myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
}
+ myproposal[PROPOSAL_ENC_ALGS_CTOS] =
+ compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]);
+ myproposal[PROPOSAL_ENC_ALGS_STOC] =
+ compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]);
if (options.compression) {
myproposal[PROPOSAL_COMP_ALGS_CTOS] =
myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib";
myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
}
- /* buffers with raw kexinit messages */
- server_kexinit = xmalloc(sizeof(*server_kexinit));
- buffer_init(server_kexinit);
- client_kexinit = kex_init(myproposal);
-
- /* algorithm negotiation */
- kex_exchange_kexinit(client_kexinit, server_kexinit, sprop);
- kex = kex_choose_conf(myproposal, sprop, 0);
- for (i = 0; i < PROPOSAL_MAX; i++)
- xfree(sprop[i]);
-
- /* server authentication and session key agreement */
- switch(kex->kex_type) {
- case DH_GRP1_SHA1:
- ssh_dh1_client(kex, host, hostaddr,
- client_kexinit, server_kexinit);
- break;
- case DH_GEX_SHA1:
- ssh_dhgex_client(kex, host, hostaddr, client_kexinit,
- server_kexinit);
- break;
- default:
- fatal("Unsupported key exchange %d", kex->kex_type);
- }
+ /* start key exchange */
+ kex = kex_setup(myproposal);
+ kex->client_version_string=client_version_string;
+ kex->server_version_string=server_version_string;
+ kex->check_host_key=&check_host_key_callback;
- buffer_free(client_kexinit);
- buffer_free(server_kexinit);
- xfree(client_kexinit);
- xfree(server_kexinit);
+ xxx_kex = kex;
- debug("Wait SSH2_MSG_NEWKEYS.");
- packet_read_expect(&plen, SSH2_MSG_NEWKEYS);
- packet_done();
- debug("GOT SSH2_MSG_NEWKEYS.");
+ dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
- debug("send SSH2_MSG_NEWKEYS.");
- packet_start(SSH2_MSG_NEWKEYS);
- packet_send();
- packet_write_wait();
- debug("done: send SSH2_MSG_NEWKEYS.");
+ session_id2 = kex->session_id;
+ session_id2_len = kex->session_id_len;
#ifdef DEBUG_KEXDH
/* send 1st encrypted/maced/compressed message */
packet_send();
packet_write_wait();
#endif
- debug("done: KEX2.");
-}
-
-/* diffie-hellman-group1-sha1 */
-
-void
-ssh_dh1_client(Kex *kex, char *host, struct sockaddr *hostaddr,
- Buffer *client_kexinit, Buffer *server_kexinit)
-{
-#ifdef DEBUG_KEXDH
- int i;
-#endif
- int plen, dlen;
- u_int klen, kout;
- char *signature = NULL;
- u_int slen;
- char *server_host_key_blob = NULL;
- Key *server_host_key;
- u_int sbloblen;
- DH *dh;
- BIGNUM *dh_server_pub = 0;
- BIGNUM *shared_secret = 0;
- u_char *kbuf;
- u_char *hash;
-
- debug("Sending SSH2_MSG_KEXDH_INIT.");
- /* generate and send 'e', client DH public key */
- dh = dh_new_group1();
- dh_gen_key(dh, kex->we_need * 8);
- packet_start(SSH2_MSG_KEXDH_INIT);
- packet_put_bignum2(dh->pub_key);
- packet_send();
- packet_write_wait();
-
-#ifdef DEBUG_KEXDH
- fprintf(stderr, "\np= ");
- BN_print_fp(stderr, dh->p);
- fprintf(stderr, "\ng= ");
- BN_print_fp(stderr, dh->g);
- fprintf(stderr, "\npub= ");
- BN_print_fp(stderr, dh->pub_key);
- fprintf(stderr, "\n");
- DHparams_print_fp(stderr, dh);
-#endif
-
- debug("Wait SSH2_MSG_KEXDH_REPLY.");
-
- packet_read_expect(&plen, SSH2_MSG_KEXDH_REPLY);
-
- debug("Got SSH2_MSG_KEXDH_REPLY.");
-
- /* key, cert */
- server_host_key_blob = packet_get_string(&sbloblen);
- server_host_key = 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,
- options.user_hostfile2, options.system_hostfile2);
-
- /* DH paramter f, server public DH key */
- dh_server_pub = BN_new();
- if (dh_server_pub == NULL)
- fatal("dh_server_pub == NULL");
- packet_get_bignum2(dh_server_pub, &dlen);
-
-#ifdef DEBUG_KEXDH
- fprintf(stderr, "\ndh_server_pub= ");
- BN_print_fp(stderr, dh_server_pub);
- fprintf(stderr, "\n");
- debug("bits %d", BN_num_bits(dh_server_pub));
-#endif
-
- /* signed H */
- signature = packet_get_string(&slen);
- packet_done();
-
- 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);
-#ifdef DEBUG_KEXDH
- debug("shared secret: len %d/%d", klen, kout);
- fprintf(stderr, "shared secret == ");
- for (i = 0; i< kout; i++)
- fprintf(stderr, "%02x", (kbuf[i])&0xff);
- fprintf(stderr, "\n");
-#endif
- shared_secret = BN_new();
-
- BN_bin2bn(kbuf, kout, shared_secret);
- memset(kbuf, 0, klen);
- xfree(kbuf);
-
- /* calc and verify H */
- hash = kex_hash(
- client_version_string,
- server_version_string,
- buffer_ptr(client_kexinit), buffer_len(client_kexinit),
- buffer_ptr(server_kexinit), buffer_len(server_kexinit),
- server_host_key_blob, sbloblen,
- dh->pub_key,
- dh_server_pub,
- shared_secret
- );
- xfree(server_host_key_blob);
- DH_free(dh);
- BN_free(dh_server_pub);
-#ifdef DEBUG_KEXDH
- fprintf(stderr, "hash == ");
- for (i = 0; i< 20; i++)
- fprintf(stderr, "%02x", (hash[i])&0xff);
- fprintf(stderr, "\n");
-#endif
- if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1)
- fatal("key_verify failed for server_host_key");
- key_free(server_host_key);
- xfree(signature);
-
- kex_derive_keys(kex, hash, shared_secret);
- BN_clear_free(shared_secret);
- packet_set_kex(kex);
-
- /* save session id */
- session_id2_len = 20;
- session_id2 = xmalloc(session_id2_len);
- memcpy(session_id2, hash, session_id2_len);
-}
-
-/* diffie-hellman-group-exchange-sha1 */
-
-/*
- * Estimates the group order for a Diffie-Hellman group that has an
- * attack complexity approximately the same as O(2**bits). Estimate
- * with: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3)))
- */
-
-int
-dh_estimate(int bits)
-{
-
- if (bits < 64)
- return (512); /* O(2**63) */
- if (bits < 128)
- return (1024); /* O(2**86) */
- if (bits < 192)
- return (2048); /* O(2**116) */
- return (4096); /* O(2**156) */
-}
-
-void
-ssh_dhgex_client(Kex *kex, char *host, struct sockaddr *hostaddr,
- Buffer *client_kexinit, Buffer *server_kexinit)
-{
-#ifdef DEBUG_KEXDH
- int i;
-#endif
- int plen, dlen;
- u_int klen, kout;
- char *signature = NULL;
- u_int slen, nbits;
- char *server_host_key_blob = NULL;
- Key *server_host_key;
- u_int sbloblen;
- DH *dh;
- BIGNUM *dh_server_pub = 0;
- BIGNUM *shared_secret = 0;
- BIGNUM *p = 0, *g = 0;
- u_char *kbuf;
- u_char *hash;
-
- nbits = dh_estimate(kex->we_need * 8);
-
- debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST.");
- packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST);
- packet_put_int(nbits);
- packet_send();
- packet_write_wait();
-
-#ifdef DEBUG_KEXDH
- fprintf(stderr, "\nnbits = %d", nbits);
-#endif
-
- debug("Wait SSH2_MSG_KEX_DH_GEX_GROUP.");
-
- packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_GROUP);
-
- debug("Got SSH2_MSG_KEX_DH_GEX_GROUP.");
-
- if ((p = BN_new()) == NULL)
- fatal("BN_new");
- packet_get_bignum2(p, &dlen);
- if ((g = BN_new()) == NULL)
- fatal("BN_new");
- packet_get_bignum2(g, &dlen);
- dh = dh_new_group(g, p);
-
- dh_gen_key(dh, kex->we_need * 8);
-
-#ifdef DEBUG_KEXDH
- fprintf(stderr, "\np= ");
- BN_print_fp(stderr, dh->p);
- fprintf(stderr, "\ng= ");
- BN_print_fp(stderr, dh->g);
- fprintf(stderr, "\npub= ");
- BN_print_fp(stderr, dh->pub_key);
- fprintf(stderr, "\n");
- DHparams_print_fp(stderr, dh);
-#endif
-
- debug("Sending SSH2_MSG_KEX_DH_GEX_INIT.");
- /* generate and send 'e', client DH public key */
- packet_start(SSH2_MSG_KEX_DH_GEX_INIT);
- packet_put_bignum2(dh->pub_key);
- packet_send();
- packet_write_wait();
-
- debug("Wait SSH2_MSG_KEX_DH_GEX_REPLY.");
-
- packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_REPLY);
-
- debug("Got SSH2_MSG_KEXDH_REPLY.");
-
- /* key, cert */
- server_host_key_blob = packet_get_string(&sbloblen);
- server_host_key = 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,
- options.user_hostfile2, options.system_hostfile2);
-
- /* DH paramter f, server public DH key */
- dh_server_pub = BN_new();
- if (dh_server_pub == NULL)
- fatal("dh_server_pub == NULL");
- packet_get_bignum2(dh_server_pub, &dlen);
-
-#ifdef DEBUG_KEXDH
- fprintf(stderr, "\ndh_server_pub= ");
- BN_print_fp(stderr, dh_server_pub);
- fprintf(stderr, "\n");
- debug("bits %d", BN_num_bits(dh_server_pub));
-#endif
-
- /* signed H */
- signature = packet_get_string(&slen);
- packet_done();
-
- 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);
-#ifdef DEBUG_KEXDH
- debug("shared secret: len %d/%d", klen, kout);
- fprintf(stderr, "shared secret == ");
- for (i = 0; i< kout; i++)
- fprintf(stderr, "%02x", (kbuf[i])&0xff);
- fprintf(stderr, "\n");
-#endif
- shared_secret = BN_new();
-
- BN_bin2bn(kbuf, kout, shared_secret);
- memset(kbuf, 0, klen);
- xfree(kbuf);
-
- /* calc and verify H */
- hash = kex_hash_gex(
- client_version_string,
- server_version_string,
- buffer_ptr(client_kexinit), buffer_len(client_kexinit),
- buffer_ptr(server_kexinit), buffer_len(server_kexinit),
- server_host_key_blob, sbloblen,
- nbits, dh->p, dh->g,
- dh->pub_key,
- dh_server_pub,
- shared_secret
- );
- xfree(server_host_key_blob);
- DH_free(dh);
- BN_free(dh_server_pub);
-#ifdef DEBUG_KEXDH
- fprintf(stderr, "hash == ");
- for (i = 0; i< 20; i++)
- fprintf(stderr, "%02x", (hash[i])&0xff);
- fprintf(stderr, "\n");
-#endif
- if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1)
- fatal("key_verify failed for server_host_key");
- key_free(server_host_key);
- xfree(signature);
-
- kex_derive_keys(kex, hash, shared_secret);
- BN_clear_free(shared_secret);
- packet_set_kex(kex);
-
- /* save session id */
- session_id2_len = 20;
- session_id2 = xmalloc(session_id2_len);
- memcpy(session_id2, hash, session_id2_len);
+ debug("done: ssh_kex2.");
}
/*
sign_cb_fn *sign_callback);
void clear_auth_state(Authctxt *authctxt);
-void authmethod_clear(void);
Authmethod *authmethod_get(char *authlist);
Authmethod *authmethod_lookup(const char *name);
+char *authmethods_get(void);
Authmethod authmethods[] = {
{"publickey",
packet_done();
debug("got SSH2_MSG_SERVICE_ACCEPT");
+ if (options.preferred_authentications == NULL)
+ options.preferred_authentications = authmethods_get();
+
/* setup authentication context */
authctxt.agent = ssh_get_authentication_connection();
authctxt.server_user = server_user;
authctxt.authlist = NULL;
if (authctxt.method == NULL)
fatal("ssh_userauth2: internal error: cannot send userauth none request");
- authmethod_clear();
/* initial userauth request */
userauth_none(&authctxt);
Key *key = NULL;
Buffer b;
int alen, blen, pktype, sent = 0;
- char *pkalg, *pkblob;
+ char *pkalg, *pkblob, *fp;
if (authctxt == NULL)
fatal("input_userauth_pk_ok: no authentication context");
debug("no last key or no sign cb");
break;
}
- debug2("last_key %s", key_fingerprint(authctxt->last_key));
if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) {
debug("unknown pkalg %s", pkalg);
break;
debug("no key from blob. pkalg %s", pkalg);
break;
}
- debug2("input_userauth_pk_ok: fp %s", key_fingerprint(key));
+ fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
+ debug2("input_userauth_pk_ok: fp %s", fp);
+ xfree(fp);
if (!key_equal(key, authctxt->last_key)) {
debug("key != last_key");
break;
{
Key *private;
char prompt[300], *passphrase;
- int success = 0, quit, i;
+ int quit, i;
+ struct stat st;
- private = key_new(KEY_UNSPEC);
- if (!load_private_key(filename, "", private, NULL)) {
- if (options.batch_mode) {
- key_free(private);
+ if (stat(filename, &st) < 0) {
+ debug3("no such identity: %s", filename);
+ return NULL;
+ }
+ private = key_load_private_type(KEY_UNSPEC, filename, "", NULL);
+ if (private == NULL) {
+ if (options.batch_mode)
return NULL;
- }
snprintf(prompt, sizeof prompt,
"Enter passphrase for key '%.100s': ", filename);
for (i = 0; i < options.number_of_password_prompts; i++) {
passphrase = read_passphrase(prompt, 0);
if (strcmp(passphrase, "") != 0) {
- success = load_private_key(filename,
- passphrase, private, NULL);
+ private = key_load_private_type(KEY_UNSPEC, filename,
+ passphrase, NULL);
quit = 0;
} else {
debug2("no passphrase given, try next key");
}
memset(passphrase, 0, strlen(passphrase));
xfree(passphrase);
- if (success || quit)
+ if (private != NULL || quit)
break;
debug2("bad passphrase given, try again...");
}
- if (!success) {
- key_free(private);
- return NULL;
- }
}
return private;
}
int key_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
u_char *data, int datalen)
{
- return key_sign(key, sigp, lenp, data, datalen);
+ return key_sign(key, sigp, lenp, data, datalen);
}
int
/* find auth method */
-#define DELIM ","
-
-static char *def_authlist = "publickey,password";
-static char *authlist_current = NULL; /* clean copy used for comparison */
-static char *authname_current = NULL; /* last used auth method */
-static char *authlist_working = NULL; /* copy that gets modified by strtok_r() */
-static char *authlist_state = NULL; /* state variable for strtok_r() */
-
-/*
- * Before starting to use a new authentication method list sent by the
- * server, reset internal variables. This should also be called when
- * finished processing server list to free resources.
- */
-void
-authmethod_clear(void)
-{
- if (authlist_current != NULL) {
- xfree(authlist_current);
- authlist_current = NULL;
- }
- if (authlist_working != NULL) {
- xfree(authlist_working);
- authlist_working = NULL;
- }
- if (authname_current != NULL) {
- xfree(authname_current);
- authname_current = NULL;
- }
- if (authlist_state != NULL)
- authlist_state = NULL;
- return;
-}
-
/*
* given auth method name, if configurable options permit this method fill
* in auth_ident field and return true, otherwise return false.
return NULL;
}
+/* XXX internal state */
+static Authmethod *current = NULL;
+static char *supported = NULL;
+static char *preferred = NULL;
/*
* Given the authentication method list sent by the server, return the
* next method we should try. If the server initially sends a nil list,
- * use a built-in default list. If the server sends a nil list after
- * previously sending a valid list, continue using the list originally
- * sent.
+ * use a built-in default list.
*/
-
Authmethod *
authmethod_get(char *authlist)
{
- char *name = NULL, *authname_old;
- Authmethod *method = NULL;
+
+ char *name = NULL;
+ int next;
/* Use a suitable default if we're passed a nil list. */
if (authlist == NULL || strlen(authlist) == 0)
- authlist = def_authlist;
-
- if (authlist_current == NULL || strcmp(authlist, authlist_current) != 0) {
- /* start over if passed a different list */
- debug3("start over, passed a different list");
- authmethod_clear();
- authlist_current = xstrdup(authlist);
- authlist_working = xstrdup(authlist);
- name = strtok_r(authlist_working, DELIM, &authlist_state);
- } else {
- /*
- * try to use previously used authentication method
- * or continue to use previously passed list
- */
- name = (authname_current != NULL) ?
- authname_current : strtok_r(NULL, DELIM, &authlist_state);
- }
+ authlist = options.preferred_authentications;
+
+ if (supported == NULL || strcmp(authlist, supported) != 0) {
+ debug3("start over, passed a different list %s", authlist);
+ if (supported != NULL)
+ xfree(supported);
+ supported = xstrdup(authlist);
+ preferred = options.preferred_authentications;
+ debug3("preferred %s", preferred);
+ current = NULL;
+ } else if (current != NULL && authmethod_is_enabled(current))
+ return current;
- while (name != NULL) {
+ for (;;) {
+ if ((name = match_list(preferred, supported, &next)) == NULL) {
+ debug("no more auth methods to try");
+ current = NULL;
+ return NULL;
+ }
+ preferred += next;
debug3("authmethod_lookup %s", name);
- method = authmethod_lookup(name);
- if (method != NULL && authmethod_is_enabled(method)) {
+ debug3("remaining preferred: %s", preferred);
+ if ((current = authmethod_lookup(name)) != NULL &&
+ authmethod_is_enabled(current)) {
debug3("authmethod_is_enabled %s", name);
- break;
+ debug("next auth method to try is %s", name);
+ return current;
}
- name = strtok_r(NULL, DELIM, &authlist_state);
- method = NULL;
- }
-
- authname_old = authname_current;
- if (method != NULL) {
- debug("next auth method to try is %s", name);
- authname_current = xstrdup(name);
- } else {
- debug("no more auth methods to try");
- authname_current = NULL;
}
+}
- if (authname_old != NULL)
- xfree(authname_old);
- return (method);
+#define DELIM ","
+char *
+authmethods_get(void)
+{
+ Authmethod *method = NULL;
+ char buf[1024];
+
+ buf[0] = '\0';
+ for (method = authmethods; method->name != NULL; method++) {
+ if (authmethod_is_enabled(method)) {
+ if (buf[0] != '\0')
+ strlcat(buf, DELIM, sizeof buf);
+ strlcat(buf, method->name, sizeof buf);
+ }
+ }
+ return xstrdup(buf);
}