1323124Sdes/* $OpenBSD: kexgexs.c,v 1.29 2016/06/08 02:13:01 dtucker Exp $ */ 2113908Sdes/* 3113908Sdes * Copyright (c) 2000 Niels Provos. All rights reserved. 4113908Sdes * Copyright (c) 2001 Markus Friedl. All rights reserved. 5113908Sdes * 6113908Sdes * Redistribution and use in source and binary forms, with or without 7113908Sdes * modification, are permitted provided that the following conditions 8113908Sdes * are met: 9113908Sdes * 1. Redistributions of source code must retain the above copyright 10113908Sdes * notice, this list of conditions and the following disclaimer. 11113908Sdes * 2. Redistributions in binary form must reproduce the above copyright 12113908Sdes * notice, this list of conditions and the following disclaimer in the 13113908Sdes * documentation and/or other materials provided with the distribution. 14113908Sdes * 15113908Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16113908Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17113908Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18113908Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19113908Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20113908Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21113908Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22113908Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23113908Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24113908Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25113908Sdes */ 26113908Sdes 27113908Sdes#include "includes.h" 28113908Sdes 29295367Sdes#ifdef WITH_OPENSSL 30162852Sdes 31295367Sdes#include <sys/param.h> /* MIN MAX */ 32295367Sdes 33162852Sdes#include <stdarg.h> 34162852Sdes#include <stdio.h> 35162852Sdes#include <string.h> 36162852Sdes#include <signal.h> 37162852Sdes 38221420Sdes#include <openssl/dh.h> 39221420Sdes 40295367Sdes#include "sshkey.h" 41162852Sdes#include "cipher.h" 42295367Sdes#include "digest.h" 43113908Sdes#include "kex.h" 44113908Sdes#include "log.h" 45113908Sdes#include "packet.h" 46113908Sdes#include "dh.h" 47113908Sdes#include "ssh2.h" 48113908Sdes#include "compat.h" 49162852Sdes#ifdef GSSAPI 50162852Sdes#include "ssh-gss.h" 51162852Sdes#endif 52113908Sdes#include "monitor_wrap.h" 53295367Sdes#include "dispatch.h" 54295367Sdes#include "ssherr.h" 55295367Sdes#include "sshbuf.h" 56113908Sdes 57295367Sdesstatic int input_kex_dh_gex_request(int, u_int32_t, void *); 58295367Sdesstatic int input_kex_dh_gex_init(int, u_int32_t, void *); 59295367Sdes 60295367Sdesint 61295367Sdeskexgex_server(struct ssh *ssh) 62113908Sdes{ 63295367Sdes ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST, 64295367Sdes &input_kex_dh_gex_request); 65295367Sdes debug("expecting SSH2_MSG_KEX_DH_GEX_REQUEST"); 66295367Sdes return 0; 67295367Sdes} 68113908Sdes 69295367Sdesstatic int 70295367Sdesinput_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt) 71295367Sdes{ 72295367Sdes struct ssh *ssh = ctxt; 73295367Sdes struct kex *kex = ssh->kex; 74295367Sdes int r; 75295367Sdes u_int min = 0, max = 0, nbits = 0; 76113908Sdes 77295367Sdes debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); 78295367Sdes if ((r = sshpkt_get_u32(ssh, &min)) != 0 || 79295367Sdes (r = sshpkt_get_u32(ssh, &nbits)) != 0 || 80295367Sdes (r = sshpkt_get_u32(ssh, &max)) != 0 || 81295367Sdes (r = sshpkt_get_end(ssh)) != 0) 82295367Sdes goto out; 83295367Sdes kex->nbits = nbits; 84295367Sdes kex->min = min; 85295367Sdes kex->max = max; 86295367Sdes min = MAX(DH_GRP_MIN, min); 87295367Sdes max = MIN(DH_GRP_MAX, max); 88295367Sdes nbits = MAX(DH_GRP_MIN, nbits); 89295367Sdes nbits = MIN(DH_GRP_MAX, nbits); 90295367Sdes 91295367Sdes if (kex->max < kex->min || kex->nbits < kex->min || 92323124Sdes kex->max < kex->nbits || kex->max < DH_GRP_MIN) { 93295367Sdes r = SSH_ERR_DH_GEX_OUT_OF_RANGE; 94295367Sdes goto out; 95113908Sdes } 96113908Sdes 97113908Sdes /* Contact privileged parent */ 98295367Sdes kex->dh = PRIVSEP(choose_dh(min, nbits, max)); 99295367Sdes if (kex->dh == NULL) { 100295367Sdes sshpkt_disconnect(ssh, "no matching DH grp found"); 101295367Sdes r = SSH_ERR_ALLOC_FAIL; 102295367Sdes goto out; 103295367Sdes } 104113908Sdes debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); 105295367Sdes if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 || 106295367Sdes (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 || 107295367Sdes (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 || 108295367Sdes (r = sshpkt_send(ssh)) != 0) 109295367Sdes goto out; 110113908Sdes 111113908Sdes /* Compute our exchange value in parallel with the client */ 112295367Sdes if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) 113295367Sdes goto out; 114113908Sdes 115113908Sdes debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); 116295367Sdes ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); 117295367Sdes r = 0; 118295367Sdes out: 119295367Sdes return r; 120295367Sdes} 121113908Sdes 122295367Sdesstatic int 123295367Sdesinput_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt) 124295367Sdes{ 125295367Sdes struct ssh *ssh = ctxt; 126295367Sdes struct kex *kex = ssh->kex; 127295367Sdes BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; 128295367Sdes struct sshkey *server_host_public, *server_host_private; 129295367Sdes u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL; 130295367Sdes u_char hash[SSH_DIGEST_MAX_LENGTH]; 131295367Sdes size_t sbloblen, slen; 132295367Sdes size_t klen = 0, hashlen; 133295367Sdes int kout, r; 134295367Sdes 135295367Sdes if (kex->load_host_public_key == NULL || 136295367Sdes kex->load_host_private_key == NULL) { 137295367Sdes r = SSH_ERR_INVALID_ARGUMENT; 138295367Sdes goto out; 139295367Sdes } 140295367Sdes server_host_public = kex->load_host_public_key(kex->hostkey_type, 141295367Sdes kex->hostkey_nid, ssh); 142295367Sdes server_host_private = kex->load_host_private_key(kex->hostkey_type, 143295367Sdes kex->hostkey_nid, ssh); 144295367Sdes if (server_host_public == NULL) { 145295367Sdes r = SSH_ERR_NO_HOSTKEY_LOADED; 146295367Sdes goto out; 147295367Sdes } 148295367Sdes 149113908Sdes /* key, cert */ 150295367Sdes if ((dh_client_pub = BN_new()) == NULL) { 151295367Sdes r = SSH_ERR_ALLOC_FAIL; 152295367Sdes goto out; 153295367Sdes } 154295367Sdes if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 || 155295367Sdes (r = sshpkt_get_end(ssh)) != 0) 156295367Sdes goto out; 157113908Sdes 158113908Sdes#ifdef DEBUG_KEXDH 159113908Sdes fprintf(stderr, "dh_client_pub= "); 160113908Sdes BN_print_fp(stderr, dh_client_pub); 161113908Sdes fprintf(stderr, "\n"); 162113908Sdes debug("bits %d", BN_num_bits(dh_client_pub)); 163113908Sdes#endif 164113908Sdes 165113908Sdes#ifdef DEBUG_KEXDH 166295367Sdes DHparams_print_fp(stderr, kex->dh); 167113908Sdes fprintf(stderr, "pub= "); 168295367Sdes BN_print_fp(stderr, kex->dh->pub_key); 169113908Sdes fprintf(stderr, "\n"); 170113908Sdes#endif 171295367Sdes if (!dh_pub_is_valid(kex->dh, dh_client_pub)) { 172295367Sdes sshpkt_disconnect(ssh, "bad client public DH value"); 173295367Sdes r = SSH_ERR_MESSAGE_INCOMPLETE; 174295367Sdes goto out; 175295367Sdes } 176113908Sdes 177295367Sdes klen = DH_size(kex->dh); 178295367Sdes if ((kbuf = malloc(klen)) == NULL || 179295367Sdes (shared_secret = BN_new()) == NULL) { 180295367Sdes r = SSH_ERR_ALLOC_FAIL; 181295367Sdes goto out; 182295367Sdes } 183295367Sdes if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 || 184295367Sdes BN_bin2bn(kbuf, kout, shared_secret) == NULL) { 185295367Sdes r = SSH_ERR_LIBCRYPTO_ERROR; 186295367Sdes goto out; 187295367Sdes } 188113908Sdes#ifdef DEBUG_KEXDH 189113908Sdes dump_digest("shared secret", kbuf, kout); 190113908Sdes#endif 191295367Sdes if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, 192295367Sdes &sbloblen)) != 0) 193295367Sdes goto out; 194157016Sdes /* calc H */ 195295367Sdes hashlen = sizeof(hash); 196295367Sdes if ((r = kexgex_hash( 197262566Sdes kex->hash_alg, 198113908Sdes kex->client_version_string, 199113908Sdes kex->server_version_string, 200295367Sdes sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), 201295367Sdes sshbuf_ptr(kex->my), sshbuf_len(kex->my), 202113908Sdes server_host_key_blob, sbloblen, 203295367Sdes kex->min, kex->nbits, kex->max, 204295367Sdes kex->dh->p, kex->dh->g, 205113908Sdes dh_client_pub, 206295367Sdes kex->dh->pub_key, 207157016Sdes shared_secret, 208295367Sdes hash, &hashlen)) != 0) 209295367Sdes goto out; 210113908Sdes 211113908Sdes /* save session id := H */ 212113908Sdes if (kex->session_id == NULL) { 213157016Sdes kex->session_id_len = hashlen; 214295367Sdes kex->session_id = malloc(kex->session_id_len); 215295367Sdes if (kex->session_id == NULL) { 216295367Sdes r = SSH_ERR_ALLOC_FAIL; 217295367Sdes goto out; 218295367Sdes } 219113908Sdes memcpy(kex->session_id, hash, kex->session_id_len); 220113908Sdes } 221113908Sdes 222113908Sdes /* sign H */ 223296781Sdes if ((r = kex->sign(server_host_private, server_host_public, &signature, 224296781Sdes &slen, hash, hashlen, kex->hostkey_alg, ssh->compat)) < 0) 225295367Sdes goto out; 226113908Sdes 227113908Sdes /* destroy_sensitive_data(); */ 228113908Sdes 229113908Sdes /* send server hostkey, DH pubkey 'f' and singed H */ 230295367Sdes if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 || 231295367Sdes (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || 232295367Sdes (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */ 233295367Sdes (r = sshpkt_put_string(ssh, signature, slen)) != 0 || 234295367Sdes (r = sshpkt_send(ssh)) != 0) 235295367Sdes goto out; 236113908Sdes 237295367Sdes if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) 238295367Sdes r = kex_send_newkeys(ssh); 239295367Sdes out: 240295367Sdes DH_free(kex->dh); 241295367Sdes kex->dh = NULL; 242295367Sdes if (dh_client_pub) 243295367Sdes BN_clear_free(dh_client_pub); 244295367Sdes if (kbuf) { 245295367Sdes explicit_bzero(kbuf, klen); 246295367Sdes free(kbuf); 247295367Sdes } 248295367Sdes if (shared_secret) 249295367Sdes BN_clear_free(shared_secret); 250295367Sdes free(server_host_key_blob); 251255767Sdes free(signature); 252295367Sdes return r; 253113908Sdes} 254295367Sdes#endif /* WITH_OPENSSL */ 255