1296633Sdes/* $OpenBSD: kexgexs.c,v 1.26 2015/12/04 16:41:28 markus 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 29294332Sdes#ifdef WITH_OPENSSL 30162852Sdes 31294332Sdes#include <sys/param.h> /* MIN MAX */ 32294332Sdes 33162852Sdes#include <stdarg.h> 34162852Sdes#include <stdio.h> 35162852Sdes#include <string.h> 36162852Sdes#include <signal.h> 37162852Sdes 38221420Sdes#include <openssl/dh.h> 39221420Sdes 40294332Sdes#include "sshkey.h" 41162852Sdes#include "cipher.h" 42294332Sdes#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" 53294332Sdes#include "dispatch.h" 54294332Sdes#include "ssherr.h" 55294332Sdes#include "sshbuf.h" 56113908Sdes 57294332Sdesstatic int input_kex_dh_gex_request(int, u_int32_t, void *); 58294332Sdesstatic int input_kex_dh_gex_init(int, u_int32_t, void *); 59294332Sdes 60294332Sdesint 61294332Sdeskexgex_server(struct ssh *ssh) 62113908Sdes{ 63294332Sdes ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST, 64294332Sdes &input_kex_dh_gex_request); 65294332Sdes debug("expecting SSH2_MSG_KEX_DH_GEX_REQUEST"); 66294332Sdes return 0; 67294332Sdes} 68113908Sdes 69294332Sdesstatic int 70294332Sdesinput_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt) 71294332Sdes{ 72294332Sdes struct ssh *ssh = ctxt; 73294332Sdes struct kex *kex = ssh->kex; 74294332Sdes int r; 75294332Sdes u_int min = 0, max = 0, nbits = 0; 76113908Sdes 77294336Sdes debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); 78294336Sdes if ((r = sshpkt_get_u32(ssh, &min)) != 0 || 79294336Sdes (r = sshpkt_get_u32(ssh, &nbits)) != 0 || 80294336Sdes (r = sshpkt_get_u32(ssh, &max)) != 0 || 81294336Sdes (r = sshpkt_get_end(ssh)) != 0) 82294332Sdes goto out; 83294336Sdes kex->nbits = nbits; 84294336Sdes kex->min = min; 85294336Sdes kex->max = max; 86294336Sdes min = MAX(DH_GRP_MIN, min); 87294336Sdes max = MIN(DH_GRP_MAX, max); 88294336Sdes nbits = MAX(DH_GRP_MIN, nbits); 89294336Sdes nbits = MIN(DH_GRP_MAX, nbits); 90113908Sdes 91294332Sdes if (kex->max < kex->min || kex->nbits < kex->min || 92294332Sdes kex->max < kex->nbits) { 93294332Sdes r = SSH_ERR_DH_GEX_OUT_OF_RANGE; 94294332Sdes goto out; 95294332Sdes } 96113908Sdes 97113908Sdes /* Contact privileged parent */ 98294332Sdes kex->dh = PRIVSEP(choose_dh(min, nbits, max)); 99294332Sdes if (kex->dh == NULL) { 100294332Sdes sshpkt_disconnect(ssh, "no matching DH grp found"); 101294332Sdes r = SSH_ERR_ALLOC_FAIL; 102294332Sdes goto out; 103294332Sdes } 104113908Sdes debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); 105294332Sdes if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 || 106294332Sdes (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 || 107294332Sdes (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 || 108294332Sdes (r = sshpkt_send(ssh)) != 0) 109294332Sdes goto out; 110113908Sdes 111113908Sdes /* Compute our exchange value in parallel with the client */ 112294332Sdes if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) 113294332Sdes goto out; 114113908Sdes 115113908Sdes debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); 116294332Sdes ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); 117294332Sdes r = 0; 118294332Sdes out: 119294332Sdes return r; 120294332Sdes} 121113908Sdes 122294332Sdesstatic int 123294332Sdesinput_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt) 124294332Sdes{ 125294332Sdes struct ssh *ssh = ctxt; 126294332Sdes struct kex *kex = ssh->kex; 127294332Sdes BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; 128294332Sdes struct sshkey *server_host_public, *server_host_private; 129294332Sdes u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL; 130294332Sdes u_char hash[SSH_DIGEST_MAX_LENGTH]; 131294332Sdes size_t sbloblen, slen; 132294332Sdes size_t klen = 0, hashlen; 133294332Sdes int kout, r; 134294332Sdes 135294332Sdes if (kex->load_host_public_key == NULL || 136294332Sdes kex->load_host_private_key == NULL) { 137294332Sdes r = SSH_ERR_INVALID_ARGUMENT; 138294332Sdes goto out; 139294332Sdes } 140294332Sdes server_host_public = kex->load_host_public_key(kex->hostkey_type, 141294332Sdes kex->hostkey_nid, ssh); 142294332Sdes server_host_private = kex->load_host_private_key(kex->hostkey_type, 143294332Sdes kex->hostkey_nid, ssh); 144294332Sdes if (server_host_public == NULL) { 145294332Sdes r = SSH_ERR_NO_HOSTKEY_LOADED; 146294332Sdes goto out; 147294332Sdes } 148294332Sdes 149113908Sdes /* key, cert */ 150294332Sdes if ((dh_client_pub = BN_new()) == NULL) { 151294332Sdes r = SSH_ERR_ALLOC_FAIL; 152294332Sdes goto out; 153294332Sdes } 154294332Sdes if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 || 155294332Sdes (r = sshpkt_get_end(ssh)) != 0) 156294332Sdes 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 166294332Sdes DHparams_print_fp(stderr, kex->dh); 167113908Sdes fprintf(stderr, "pub= "); 168294332Sdes BN_print_fp(stderr, kex->dh->pub_key); 169113908Sdes fprintf(stderr, "\n"); 170113908Sdes#endif 171294332Sdes if (!dh_pub_is_valid(kex->dh, dh_client_pub)) { 172294332Sdes sshpkt_disconnect(ssh, "bad client public DH value"); 173294332Sdes r = SSH_ERR_MESSAGE_INCOMPLETE; 174294332Sdes goto out; 175294332Sdes } 176113908Sdes 177294332Sdes klen = DH_size(kex->dh); 178294332Sdes if ((kbuf = malloc(klen)) == NULL || 179294332Sdes (shared_secret = BN_new()) == NULL) { 180294332Sdes r = SSH_ERR_ALLOC_FAIL; 181294332Sdes goto out; 182294332Sdes } 183294332Sdes if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 || 184294332Sdes BN_bin2bn(kbuf, kout, shared_secret) == NULL) { 185294332Sdes r = SSH_ERR_LIBCRYPTO_ERROR; 186294332Sdes goto out; 187294332Sdes } 188113908Sdes#ifdef DEBUG_KEXDH 189113908Sdes dump_digest("shared secret", kbuf, kout); 190113908Sdes#endif 191294332Sdes if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, 192294332Sdes &sbloblen)) != 0) 193294332Sdes goto out; 194157016Sdes /* calc H */ 195294332Sdes hashlen = sizeof(hash); 196294332Sdes if ((r = kexgex_hash( 197261320Sdes kex->hash_alg, 198113908Sdes kex->client_version_string, 199113908Sdes kex->server_version_string, 200294332Sdes sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), 201294332Sdes sshbuf_ptr(kex->my), sshbuf_len(kex->my), 202113908Sdes server_host_key_blob, sbloblen, 203294332Sdes kex->min, kex->nbits, kex->max, 204294332Sdes kex->dh->p, kex->dh->g, 205113908Sdes dh_client_pub, 206294332Sdes kex->dh->pub_key, 207157016Sdes shared_secret, 208294332Sdes hash, &hashlen)) != 0) 209294332Sdes goto out; 210113908Sdes 211113908Sdes /* save session id := H */ 212113908Sdes if (kex->session_id == NULL) { 213157016Sdes kex->session_id_len = hashlen; 214294332Sdes kex->session_id = malloc(kex->session_id_len); 215294332Sdes if (kex->session_id == NULL) { 216294332Sdes r = SSH_ERR_ALLOC_FAIL; 217294332Sdes goto out; 218294332Sdes } 219113908Sdes memcpy(kex->session_id, hash, kex->session_id_len); 220113908Sdes } 221113908Sdes 222113908Sdes /* sign H */ 223296633Sdes if ((r = kex->sign(server_host_private, server_host_public, &signature, 224296633Sdes &slen, hash, hashlen, kex->hostkey_alg, ssh->compat)) < 0) 225294332Sdes goto out; 226113908Sdes 227113908Sdes /* destroy_sensitive_data(); */ 228113908Sdes 229113908Sdes /* send server hostkey, DH pubkey 'f' and singed H */ 230294332Sdes if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 || 231294332Sdes (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || 232294332Sdes (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */ 233294332Sdes (r = sshpkt_put_string(ssh, signature, slen)) != 0 || 234294332Sdes (r = sshpkt_send(ssh)) != 0) 235294332Sdes goto out; 236113908Sdes 237294332Sdes if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) 238294332Sdes r = kex_send_newkeys(ssh); 239294332Sdes out: 240294332Sdes DH_free(kex->dh); 241294332Sdes kex->dh = NULL; 242294332Sdes if (dh_client_pub) 243294332Sdes BN_clear_free(dh_client_pub); 244294332Sdes if (kbuf) { 245294332Sdes explicit_bzero(kbuf, klen); 246294332Sdes free(kbuf); 247294332Sdes } 248294332Sdes if (shared_secret) 249294332Sdes BN_clear_free(shared_secret); 250294332Sdes free(server_host_key_blob); 251255767Sdes free(signature); 252294332Sdes return r; 253113908Sdes} 254294332Sdes#endif /* WITH_OPENSSL */ 255