From c69e6c47d102b53013f60a07fb8ee6bd16c69fad Mon Sep 17 00:00:00 2001 From: markus Date: Mon, 3 Apr 2000 20:06:14 +0000 Subject: [PATCH] DSA, keyexchange, algorithm agreement for ssh2 --- usr.bin/ssh/dsa.c | 298 +++++++++++++++++++++++++++++ usr.bin/ssh/dsa.h | 20 ++ usr.bin/ssh/hmac.c | 59 ++++++ usr.bin/ssh/hmac.h | 11 ++ usr.bin/ssh/kex.c | 392 +++++++++++++++++++++++++++++++++++++++ usr.bin/ssh/kex.h | 111 +++++++++++ usr.bin/ssh/lib/Makefile | 2 +- 7 files changed, 892 insertions(+), 1 deletion(-) create mode 100644 usr.bin/ssh/dsa.c create mode 100644 usr.bin/ssh/dsa.h create mode 100644 usr.bin/ssh/hmac.c create mode 100644 usr.bin/ssh/hmac.h create mode 100644 usr.bin/ssh/kex.c create mode 100644 usr.bin/ssh/kex.h diff --git a/usr.bin/ssh/dsa.c b/usr.bin/ssh/dsa.c new file mode 100644 index 00000000000..86c66cbb484 --- /dev/null +++ b/usr.bin/ssh/dsa.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$Id: dsa.c,v 1.1 2000/04/03 20:06:14 markus Exp $"); + +#include "ssh.h" +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "compat.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "kex.h" +#include "key.h" + +#define INTBLOB_LEN 20 +#define SIGBLOB_LEN (2*INTBLOB_LEN) + +Key * +dsa_serverkey_from_blob( + char *serverhostkey, int serverhostkeylen) +{ + Buffer b; + char *ktype; + int rlen; + DSA *dsa; + Key *key; + + /* fetch & parse DSA/DSS pubkey */ + key = key_new(KEY_DSA); + dsa = key->dsa; + buffer_init(&b); + 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); + key_free(key); + return NULL; + } + buffer_get_bignum2(&b, dsa->p); + buffer_get_bignum2(&b, dsa->q); + buffer_get_bignum2(&b, dsa->g); + 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); + buffer_free(&b); + + log("keytype %s", ktype); +#ifdef DEBUG_DSS + DSA_print_fp(stderr, dsa, 8); +#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) +{ + Buffer b; + int len; + unsigned char *buf; + + if (key == NULL || key->type != KEY_DSA) + return 0; + buffer_init(&b); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_bignum2(&b, key->dsa->p); + buffer_put_bignum2(&b, key->dsa->q); + buffer_put_bignum2(&b, key->dsa->g); + buffer_put_bignum2(&b, key->dsa->pub_key); + len = buffer_len(&b); + buf = xmalloc(len); + memcpy(buf, buffer_ptr(&b), len); + memset(buffer_ptr(&b), 0, len); + buffer_free(&b); + if (lenp != NULL) + *lenp = len; + if (blobp != NULL) + *blobp = buf; + return len; +} +int +dsa_sign( + Key *key, + unsigned char **sigp, int *lenp, + unsigned char *hash, int hlen) +{ + unsigned char *digest; + unsigned char *ret; + DSA_SIG *sig; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + unsigned int rlen; + unsigned int slen; + unsigned int len; + unsigned char sigblob[SIGBLOB_LEN]; + Buffer b; + + if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { + log("dsa_sign: no DSA key"); + return -1; + } + digest = xmalloc(evp_md->md_size); + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, hash, hlen); + EVP_DigestFinal(&md, digest, NULL); + + sig = DSA_do_sign(digest, evp_md->md_size, key->dsa); + + 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); + DSA_SIG_free(sig); + return -1; + } + log("sig size %d %d", rlen, slen); + + memset(sigblob, 0, SIGBLOB_LEN); + BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen); + BN_bn2bin(sig->s, sigblob+ SIGBLOB_LEN - slen); + DSA_SIG_free(sig); + + if (datafellows) { + log("datafellows"); + ret = xmalloc(SIGBLOB_LEN); + memcpy(ret, sigblob, SIGBLOB_LEN); + if (lenp != NULL) + *lenp = SIGBLOB_LEN; + if (sigp != NULL) + *sigp = ret; + } else { + /* ietf-drafts */ + buffer_init(&b); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_string(&b, sigblob, SIGBLOB_LEN); + len = buffer_len(&b); + ret = xmalloc(len); + memcpy(ret, buffer_ptr(&b), len); + buffer_free(&b); + if (lenp != NULL) + *lenp = len; + if (sigp != NULL) + *sigp = ret; + } + return 0; +} +int +dsa_verify( + Key *key, + unsigned char *signature, int signaturelen, + unsigned char *hash, int hlen) +{ + Buffer b; + unsigned char *digest; + DSA_SIG *sig; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + char *ktype; + unsigned char *sigblob; + char *txt; + unsigned int len; + int rlen; + int ret; + + if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { + log("dsa_verify: no DSA key"); + return -1; + } + + if (datafellows && signaturelen != SIGBLOB_LEN) { + log("heh? datafellows ssh2 complies with ietf-drafts????"); + datafellows = 0; + } + + log("len %d datafellows %d", signaturelen, datafellows); + + /* fetch signature */ + if (datafellows) { + sigblob = signature; + len = signaturelen; + } else { + /* ietf-drafts */ + buffer_init(&b); + buffer_append(&b, (char *) signature, signaturelen); + ktype = buffer_get_string(&b, NULL); + sigblob = (unsigned char *)buffer_get_string(&b, &len); + rlen = buffer_len(&b); + if(rlen != 0) + log("remaining bytes in signature %d", rlen); + buffer_free(&b); + } + + if (len != SIGBLOB_LEN) { + fatal("bad sigbloblen %d != SIGBLOB_LEN", len); + } + + /* parse signature */ + sig = DSA_SIG_new(); + sig->r = BN_new(); + sig->s = BN_new(); + BN_bin2bn(sigblob, INTBLOB_LEN, sig->r); + BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s); + if (!datafellows) { + memset(sigblob, 0, len); + xfree(sigblob); + } + + /* sha1 the signed data (== session_id == hash) */ + digest = xmalloc(evp_md->md_size); + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, hash, hlen); + EVP_DigestFinal(&md, digest, NULL); + + ret = DSA_do_verify(digest, evp_md->md_size, sig, key->dsa); + + memset(digest, 0, evp_md->md_size); + xfree(digest); + DSA_SIG_free(sig); + + switch (ret) { + case 1: + txt = "correct"; + break; + case 0: + txt = "incorrect"; + break; + case -1: + default: + txt = "error"; + break; + } + log("dsa_verify: signature %s", txt); + return ret; +} diff --git a/usr.bin/ssh/dsa.h b/usr.bin/ssh/dsa.h new file mode 100644 index 00000000000..65e651d9b91 --- /dev/null +++ b/usr.bin/ssh/dsa.h @@ -0,0 +1,20 @@ +#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); + +int +dsa_sign( + Key *key, + unsigned char **sigp, int *lenp, + unsigned char *hash, int hlen); + +int +dsa_verify( + Key *key, + unsigned char *signature, int signaturelen, + unsigned char *hash, int hlen); + +#endif diff --git a/usr.bin/ssh/hmac.c b/usr.bin/ssh/hmac.c new file mode 100644 index 00000000000..6d27ebc6a30 --- /dev/null +++ b/usr.bin/ssh/hmac.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$Id: hmac.c,v 1.1 2000/04/03 20:06:15 markus Exp $"); + +#include "xmalloc.h" +#include "ssh.h" +#include "getput.h" + +#include + +unsigned char * +hmac( + EVP_MD *evp_md, + unsigned int seqno, + unsigned char *data, int datalen, + unsigned char *key, int keylen) +{ + HMAC_CTX c; + static unsigned char m[EVP_MAX_MD_SIZE]; + unsigned char b[4]; + + if (key == NULL) + fatal("hmac: no key"); + HMAC_Init(&c, key, keylen, evp_md); + PUT_32BIT(b, seqno); + HMAC_Update(&c, b, sizeof b); + HMAC_Update(&c, data, datalen); + HMAC_Final(&c, m, NULL); + HMAC_cleanup(&c); + return(m); +} diff --git a/usr.bin/ssh/hmac.h b/usr.bin/ssh/hmac.h new file mode 100644 index 00000000000..fb680292739 --- /dev/null +++ b/usr.bin/ssh/hmac.h @@ -0,0 +1,11 @@ +#ifndef HMAC_H +#define HMAC_H + +unsigned char * +hmac( + EVP_MD *evp_md, + unsigned int seqno, + unsigned char *data, int datalen, + unsigned char *key, int len); + +#endif diff --git a/usr.bin/ssh/kex.c b/usr.bin/ssh/kex.c new file mode 100644 index 00000000000..a52af72abf3 --- /dev/null +++ b/usr.bin/ssh/kex.c @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$Id: kex.c,v 1.1 2000/04/03 20:06:15 markus Exp $"); + +#include "ssh.h" +#include "ssh2.h" +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "cipher.h" +#include "compat.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "kex.h" + +Buffer * +kex_init(char *myproposal[PROPOSAL_MAX]) +{ + char c = 0; + unsigned char cookie[16]; + u_int32_t rand = 0; + int i; + Buffer *ki = xmalloc(sizeof(*ki)); + for (i = 0; i < 16; i++) { + if (i % 4 == 0) + rand = arc4random(); + cookie[i] = rand & 0xff; + rand >>= 8; + } + buffer_init(ki); + buffer_append(ki, (char *)cookie, sizeof cookie); + for (i = 0; i < PROPOSAL_MAX; i++) + buffer_put_cstring(ki, myproposal[i]); + buffer_append(ki, &c, 1); /* boolean first_kex_packet_follows */ + buffer_put_int(ki, 0); /* uint32 0 (reserved for future extension) */ + return ki; +} + +/* diffie-hellman-group1-sha1 */ + +DH * +new_dh_group1() +{ + static char *group1 = + "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" + "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" + "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381" + "FFFFFFFF" "FFFFFFFF"; + DH *dh; + int ret; + dh = DH_new(); + if(dh == NULL) + fatal("DH_new"); + 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); + if (DH_generate_key(dh) == 0) + fatal("DH_generate_key"); + return dh; +} + +void +bignum_print(BIGNUM *b) +{ + BN_print_fp(stderr,b); +} + +void +dump_digest(unsigned char *digest, int len) +{ + int i; + for (i = 0; i< len; i++){ + fprintf(stderr, "%02x", digest[i]); + if(i%2!=0) + fprintf(stderr, " "); + } + fprintf(stderr, "\n"); +} + +unsigned char * +kex_hash( + char *client_version_string, + char *server_version_string, + char *ckexinit, int ckexinitlen, + char *skexinit, int skexinitlen, + char *serverhostkeyblob, int sbloblen, + BIGNUM *client_dh_pub, + BIGNUM *server_dh_pub, + BIGNUM *shared_secret) +{ + Buffer b; + static unsigned char digest[EVP_MAX_MD_SIZE]; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + + buffer_init(&b); + buffer_put_string(&b, client_version_string, strlen(client_version_string)); + buffer_put_string(&b, server_version_string, strlen(server_version_string)); + + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + buffer_put_int(&b, ckexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, ckexinit, ckexinitlen); + buffer_put_int(&b, skexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, skexinit, skexinitlen); + + buffer_put_string(&b, serverhostkeyblob, sbloblen); + buffer_put_bignum2(&b, client_dh_pub); + buffer_put_bignum2(&b, server_dh_pub); + buffer_put_bignum2(&b, shared_secret); + +#ifdef DEBUG_KEX + buffer_dump(&b); +#endif + + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestFinal(&md, digest, NULL); + + buffer_free(&b); + +#ifdef DEBUG_KEX + dump_digest(digest, evp_md->md_size); +#endif + return digest; +} + +unsigned char * +derive_key(int id, int need, char unsigned *hash, BIGNUM *shared_secret) +{ + Buffer b; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + char c = id; + int have; + int mdsz = evp_md->md_size; + unsigned char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz); + + buffer_init(&b); + buffer_put_bignum2(&b, shared_secret); + + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); /* shared_secret K */ + EVP_DigestUpdate(&md, hash, mdsz); /* transport-06 */ + EVP_DigestUpdate(&md, &c, 1); /* key id */ + EVP_DigestUpdate(&md, hash, mdsz); /* session id */ + EVP_DigestFinal(&md, digest, NULL); + + /* expand */ + for (have = mdsz; need > have; have += mdsz) { + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestUpdate(&md, hash, mdsz); + EVP_DigestUpdate(&md, digest, have); + EVP_DigestFinal(&md, digest + have, NULL); + } + buffer_free(&b); +#ifdef DEBUG_KEX + fprintf(stderr, "Digest '%c'== ", c); + dump_digest(digest, need); +#endif + return digest; +} + +#define NKEYS 6 + +#define MAX_PROP 20 +#define SEP "," + +char * +get_match(char *client, char *server) +{ + char *sproposals[MAX_PROP]; + char *p; + int i, j, nproposals; + + for ((p = strtok(server, SEP)), i=0; p; (p = strtok(NULL, SEP)), i++) { + if (i < MAX_PROP) + sproposals[i] = p; + else + break; + } + nproposals = i; + + for ((p = strtok(client, SEP)), i=0; p; (p = strtok(NULL, SEP)), i++) { + for (j = 0; j < nproposals; j++) + if (strcmp(p, sproposals[j]) == 0) + return xstrdup(p); + } + return NULL; +} +void +choose_enc(Enc *enc, char *client, char *server) +{ + char *name = get_match(client, server); + if (name == NULL) + fatal("no matching cipher found: client %s server %s", client, server); + enc->type = cipher_number(name); + + switch (enc->type) { + case SSH_CIPHER_3DES_CBC: + enc->key_len = 24; + enc->iv_len = 8; + enc->block_size = 8; + break; + case SSH_CIPHER_BLOWFISH_CBC: + case SSH_CIPHER_CAST128_CBC: + enc->key_len = 16; + enc->iv_len = 8; + enc->block_size = 8; + break; + case SSH_CIPHER_ARCFOUR: + enc->key_len = 16; + enc->iv_len = 0; + enc->block_size = 8; + break; + default: + fatal("unsupported cipher %s", name); + } + enc->name = name; + enc->enabled = 0; + enc->iv = NULL; + enc->key = NULL; +} +void +choose_mac(Mac *mac, char *client, char *server) +{ + char *name = get_match(client, server); + if (name == NULL) + fatal("no matching mac found: client %s server %s", client, server); + if (strcmp(name, "hmac-md5") == 0) { + mac->md = EVP_md5(); + } else if (strcmp(name, "hmac-sha1") == 0) { + mac->md = EVP_sha1(); + } else if (strcmp(name, "hmac-ripemd160@openssh.com") == 0) { + mac->md = EVP_ripemd160(); + } else { + fatal("unsupported mac %s", name); + } + mac->name = name; + mac->mac_len = mac->md->md_size; + mac->key_len = datafellows ? 16 : mac->mac_len; + mac->key = NULL; + mac->enabled = 0; +} +void +choose_comp(Comp *comp, char *client, char *server) +{ + char *name = get_match(client, server); + if (name == NULL) + fatal("no matching comp found: client %s server %s", client, server); + if (strcmp(name, "zlib") == 0) { + comp->type = 1; + } else if (strcmp(name, "none") == 0) { + comp->type = 0; + } else { + fatal("unsupported comp %s", name); + } + comp->name = name; +} +void +choose_kex(Kex *k, char *client, char *server) +{ + k->name = get_match(client, server); + if (k->name == NULL) + fatal("no kex alg"); + if (strcmp(k->name, KEX_DH1) != 0) + fatal("bad kex alg %s", k->name); +} +void +choose_hostkeyalg(Kex *k, char *client, char *server) +{ + k->hostkeyalg = get_match(client, server); + if (k->hostkeyalg == NULL) + fatal("no hostkey alg"); + if (strcmp(k->hostkeyalg, KEX_DSS) != 0) + fatal("bad hostkey alg %s", k->hostkeyalg); +} + +Kex * +kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server) +{ + int i; + int mode; + int ctos; /* direction: if true client-to-server */ + int need; + Kex *k; + + k = xmalloc(sizeof(*k)); + memset(k, 0, sizeof(*k)); + k->server = server; + + for (mode = 0; mode < MODE_MAX; mode++) { + int nenc, nmac, ncomp; + ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN); + nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; + nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; + ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; + 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", + ctos ? "client->server" : "server->client", + k->enc[mode].name, + k->mac[mode].name, + k->comp[mode].name); + } + choose_kex(k, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]); + choose_hostkeyalg(k, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], + sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]); + for (i = 0; i < PROPOSAL_MAX; i++) { + xfree(cprop[i]); + xfree(sprop[i]); + } + need = 0; + for (mode = 0; mode < MODE_MAX; mode++) { + if (need < k->enc[mode].key_len) + need = k->enc[mode].key_len; + if (need < k->enc[mode].iv_len) + need = k->enc[mode].iv_len; + if (need < k->mac[mode].key_len) + need = k->mac[mode].key_len; + } + /* need runden? */ +#define WE_NEED 32 + k->we_need = WE_NEED; + k->we_need = need; + return k; +} + +int +kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret) +{ + int i; + int mode; + int ctos; + unsigned char *keys[NKEYS]; + + for (i = 0; i < NKEYS; i++) + keys[i] = derive_key('A'+i, k->we_need, hash, shared_secret); + + for (mode = 0; mode < MODE_MAX; mode++) { + ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN); + k->enc[mode].iv = keys[ctos ? 0 : 1]; + k->enc[mode].key = keys[ctos ? 2 : 3]; + k->mac[mode].key = keys[ctos ? 4 : 5]; + } + return 0; +} diff --git a/usr.bin/ssh/kex.h b/usr.bin/ssh/kex.h new file mode 100644 index 00000000000..f9e79994888 --- /dev/null +++ b/usr.bin/ssh/kex.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef KEX_H +#define KEX_H + +#define KEX_DH1 "diffie-hellman-group1-sha1" +#define KEX_DSS "ssh-dss" + +enum kex_init_proposals { + PROPOSAL_KEX_ALGS, + PROPOSAL_SERVER_HOST_KEY_ALGS, + PROPOSAL_ENC_ALGS_CTOS, + PROPOSAL_ENC_ALGS_STOC, + PROPOSAL_MAC_ALGS_CTOS, + PROPOSAL_MAC_ALGS_STOC, + PROPOSAL_COMP_ALGS_CTOS, + PROPOSAL_COMP_ALGS_STOC, + PROPOSAL_LANG_CTOS, + PROPOSAL_LANG_STOC, + PROPOSAL_MAX +}; + +enum kex_modes { + MODE_IN, + MODE_OUT, + MODE_MAX +}; + +typedef struct Kex Kex; +typedef struct Mac Mac; +typedef struct Comp Comp; +typedef struct Enc Enc; + +struct Enc { + int type; + int enabled; + int block_size; + unsigned char *key; + unsigned char *iv; + int key_len; + int iv_len; + char *name; +}; +struct Mac { + EVP_MD *md; + int enabled; + int mac_len; + unsigned char *key; + int key_len; + char *name; +}; +struct Comp { + int type; + int enabled; + char *name; +}; +struct Kex { + Enc enc [MODE_MAX]; + Mac mac [MODE_MAX]; + Comp comp[MODE_MAX]; + int we_need; + int server; + char *name; + char *hostkeyalg; +}; + +Buffer *kex_init(char *myproposal[PROPOSAL_MAX]); +DH *new_dh_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); +void packet_set_kex(Kex *k); + +unsigned char * +kex_hash( + char *client_version_string, + char *server_version_string, + char *ckexinit, int ckexinitlen, + char *skexinit, int skexinitlen, + char *serverhostkeyblob, int sbloblen, + BIGNUM *client_dh_pub, + BIGNUM *server_dh_pub, + BIGNUM *shared_secret); + +#endif diff --git a/usr.bin/ssh/lib/Makefile b/usr.bin/ssh/lib/Makefile index 262273cc35c..4c695afa741 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 + key.c dispatch.c dsa.c kex.c hmac.c NOPROFILE= yes NOPIC= yes -- 2.20.1