1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without 5*0Sstevel@tonic-gate * modification, are permitted provided that the following conditions 6*0Sstevel@tonic-gate * are met: 7*0Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 8*0Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 9*0Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 10*0Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 11*0Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 12*0Sstevel@tonic-gate * 13*0Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14*0Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15*0Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16*0Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17*0Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18*0Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19*0Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20*0Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21*0Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22*0Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23*0Sstevel@tonic-gate * 24*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 25*0Sstevel@tonic-gate * Use is subject to license terms. 26*0Sstevel@tonic-gate */ 27*0Sstevel@tonic-gate 28*0Sstevel@tonic-gate #include "includes.h" 29*0Sstevel@tonic-gate RCSID("$OpenBSD: kex.c,v 1.51 2002/06/24 14:55:38 markus Exp $"); 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate #include <locale.h> 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate #include <openssl/crypto.h> 36*0Sstevel@tonic-gate 37*0Sstevel@tonic-gate #include "ssh2.h" 38*0Sstevel@tonic-gate #include "xmalloc.h" 39*0Sstevel@tonic-gate #include "buffer.h" 40*0Sstevel@tonic-gate #include "bufaux.h" 41*0Sstevel@tonic-gate #include "packet.h" 42*0Sstevel@tonic-gate #include "compat.h" 43*0Sstevel@tonic-gate #include "cipher.h" 44*0Sstevel@tonic-gate #include "kex.h" 45*0Sstevel@tonic-gate #include "key.h" 46*0Sstevel@tonic-gate #include "log.h" 47*0Sstevel@tonic-gate #include "mac.h" 48*0Sstevel@tonic-gate #include "match.h" 49*0Sstevel@tonic-gate #include "dispatch.h" 50*0Sstevel@tonic-gate #include "monitor.h" 51*0Sstevel@tonic-gate #include "g11n.h" 52*0Sstevel@tonic-gate 53*0Sstevel@tonic-gate #ifdef GSSAPI 54*0Sstevel@tonic-gate #include "ssh-gss.h" 55*0Sstevel@tonic-gate #endif 56*0Sstevel@tonic-gate 57*0Sstevel@tonic-gate #define KEX_COOKIE_LEN 16 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate /* Use privilege separation for sshd */ 60*0Sstevel@tonic-gate int use_privsep; 61*0Sstevel@tonic-gate struct monitor *pmonitor; 62*0Sstevel@tonic-gate 63*0Sstevel@tonic-gate char *session_lang = NULL; 64*0Sstevel@tonic-gate 65*0Sstevel@tonic-gate 66*0Sstevel@tonic-gate /* prototype */ 67*0Sstevel@tonic-gate static void kex_do_hook(Kex *kex); 68*0Sstevel@tonic-gate static void kex_kexinit_finish(Kex *); 69*0Sstevel@tonic-gate static void kex_choose_conf(Kex *); 70*0Sstevel@tonic-gate 71*0Sstevel@tonic-gate /* put algorithm proposal into buffer */ 72*0Sstevel@tonic-gate static 73*0Sstevel@tonic-gate void 74*0Sstevel@tonic-gate kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX]) 75*0Sstevel@tonic-gate { 76*0Sstevel@tonic-gate int i; 77*0Sstevel@tonic-gate 78*0Sstevel@tonic-gate buffer_clear(b); 79*0Sstevel@tonic-gate /* 80*0Sstevel@tonic-gate * add a dummy cookie, the cookie will be overwritten by 81*0Sstevel@tonic-gate * kex_send_kexinit(), each time a kexinit is set 82*0Sstevel@tonic-gate */ 83*0Sstevel@tonic-gate for (i = 0; i < KEX_COOKIE_LEN; i++) 84*0Sstevel@tonic-gate buffer_put_char(b, 0); 85*0Sstevel@tonic-gate for (i = 0; i < PROPOSAL_MAX; i++) 86*0Sstevel@tonic-gate buffer_put_cstring(b, proposal[i]); 87*0Sstevel@tonic-gate buffer_put_char(b, 0); /* first_kex_packet_follows */ 88*0Sstevel@tonic-gate buffer_put_int(b, 0); /* uint32 reserved */ 89*0Sstevel@tonic-gate } 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate /* parse buffer and return algorithm proposal */ 92*0Sstevel@tonic-gate static 93*0Sstevel@tonic-gate char ** 94*0Sstevel@tonic-gate kex_buf2prop(Buffer *raw, int *first_kex_follows) 95*0Sstevel@tonic-gate { 96*0Sstevel@tonic-gate Buffer b; 97*0Sstevel@tonic-gate int i; 98*0Sstevel@tonic-gate char **proposal; 99*0Sstevel@tonic-gate 100*0Sstevel@tonic-gate proposal = xmalloc(PROPOSAL_MAX * sizeof(char *)); 101*0Sstevel@tonic-gate 102*0Sstevel@tonic-gate buffer_init(&b); 103*0Sstevel@tonic-gate buffer_append(&b, buffer_ptr(raw), buffer_len(raw)); 104*0Sstevel@tonic-gate /* skip cookie */ 105*0Sstevel@tonic-gate for (i = 0; i < KEX_COOKIE_LEN; i++) 106*0Sstevel@tonic-gate buffer_get_char(&b); 107*0Sstevel@tonic-gate /* extract kex init proposal strings */ 108*0Sstevel@tonic-gate for (i = 0; i < PROPOSAL_MAX; i++) { 109*0Sstevel@tonic-gate proposal[i] = buffer_get_string(&b,NULL); 110*0Sstevel@tonic-gate debug2("kex_parse_kexinit: %s", proposal[i]); 111*0Sstevel@tonic-gate } 112*0Sstevel@tonic-gate /* first kex follows / reserved */ 113*0Sstevel@tonic-gate i = buffer_get_char(&b); 114*0Sstevel@tonic-gate if (first_kex_follows != NULL) 115*0Sstevel@tonic-gate *first_kex_follows = i; 116*0Sstevel@tonic-gate debug2("kex_parse_kexinit: first_kex_follows %d ", i); 117*0Sstevel@tonic-gate i = buffer_get_int(&b); 118*0Sstevel@tonic-gate debug2("kex_parse_kexinit: reserved %d ", i); 119*0Sstevel@tonic-gate buffer_free(&b); 120*0Sstevel@tonic-gate return proposal; 121*0Sstevel@tonic-gate } 122*0Sstevel@tonic-gate 123*0Sstevel@tonic-gate static 124*0Sstevel@tonic-gate void 125*0Sstevel@tonic-gate kex_prop_free(char **proposal) 126*0Sstevel@tonic-gate { 127*0Sstevel@tonic-gate int i; 128*0Sstevel@tonic-gate 129*0Sstevel@tonic-gate for (i = 0; i < PROPOSAL_MAX; i++) 130*0Sstevel@tonic-gate xfree(proposal[i]); 131*0Sstevel@tonic-gate xfree(proposal); 132*0Sstevel@tonic-gate } 133*0Sstevel@tonic-gate 134*0Sstevel@tonic-gate static void 135*0Sstevel@tonic-gate kex_protocol_error(int type, u_int32_t seq, void *ctxt) 136*0Sstevel@tonic-gate { 137*0Sstevel@tonic-gate error("Hm, kex protocol error: type %d seq %u", type, seq); 138*0Sstevel@tonic-gate } 139*0Sstevel@tonic-gate 140*0Sstevel@tonic-gate static void 141*0Sstevel@tonic-gate kex_reset_dispatch(void) 142*0Sstevel@tonic-gate { 143*0Sstevel@tonic-gate #ifdef ALTPRIVSEP 144*0Sstevel@tonic-gate /* unprivileged sshd has a kex packet handler that must not be reset */ 145*0Sstevel@tonic-gate debug3("kex_reset_dispatch -- should we dispatch_set(KEXINIT) here? %d && !%d", 146*0Sstevel@tonic-gate packet_is_server(), packet_is_monitor()); 147*0Sstevel@tonic-gate if (packet_is_server() && !packet_is_monitor()) { 148*0Sstevel@tonic-gate debug3("kex_reset_dispatch -- skipping dispatch_set(KEXINIT) in unpriv proc"); 149*0Sstevel@tonic-gate return; 150*0Sstevel@tonic-gate } 151*0Sstevel@tonic-gate #endif /* ALTPRIVSEP */ 152*0Sstevel@tonic-gate 153*0Sstevel@tonic-gate dispatch_range(SSH2_MSG_TRANSPORT_MIN, 154*0Sstevel@tonic-gate SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); 155*0Sstevel@tonic-gate dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 156*0Sstevel@tonic-gate } 157*0Sstevel@tonic-gate 158*0Sstevel@tonic-gate void 159*0Sstevel@tonic-gate kex_finish(Kex *kex) 160*0Sstevel@tonic-gate { 161*0Sstevel@tonic-gate kex_reset_dispatch(); 162*0Sstevel@tonic-gate 163*0Sstevel@tonic-gate packet_start(SSH2_MSG_NEWKEYS); 164*0Sstevel@tonic-gate packet_send(); 165*0Sstevel@tonic-gate /* packet_write_wait(); */ 166*0Sstevel@tonic-gate debug("SSH2_MSG_NEWKEYS sent"); 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate #ifdef ALTPRIVSEP 169*0Sstevel@tonic-gate if (packet_is_monitor()) 170*0Sstevel@tonic-gate goto skip_newkeys; 171*0Sstevel@tonic-gate #endif /* ALTPRIVSEP */ 172*0Sstevel@tonic-gate debug("expecting SSH2_MSG_NEWKEYS"); 173*0Sstevel@tonic-gate packet_read_expect(SSH2_MSG_NEWKEYS); 174*0Sstevel@tonic-gate packet_check_eom(); 175*0Sstevel@tonic-gate debug("SSH2_MSG_NEWKEYS received"); 176*0Sstevel@tonic-gate #ifdef ALTPRIVSEP 177*0Sstevel@tonic-gate skip_newkeys: 178*0Sstevel@tonic-gate #endif /* ALTPRIVSEP */ 179*0Sstevel@tonic-gate 180*0Sstevel@tonic-gate kex->done = 1; 181*0Sstevel@tonic-gate kex->initial_kex_done = 1; /* never to be cleared once set */ 182*0Sstevel@tonic-gate buffer_clear(&kex->peer); 183*0Sstevel@tonic-gate /* buffer_clear(&kex->my); */ 184*0Sstevel@tonic-gate kex->flags &= ~KEX_INIT_SENT; 185*0Sstevel@tonic-gate #if 0 186*0Sstevel@tonic-gate /* Must have this name for use in sshd (audit_save_kex())... */ 187*0Sstevel@tonic-gate xfree(kex->name); 188*0Sstevel@tonic-gate kex->name = NULL; 189*0Sstevel@tonic-gate #endif 190*0Sstevel@tonic-gate } 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate void 193*0Sstevel@tonic-gate kex_send_kexinit(Kex *kex) 194*0Sstevel@tonic-gate { 195*0Sstevel@tonic-gate u_int32_t rand = 0; 196*0Sstevel@tonic-gate u_char *cookie; 197*0Sstevel@tonic-gate int i; 198*0Sstevel@tonic-gate 199*0Sstevel@tonic-gate if (kex == NULL) { 200*0Sstevel@tonic-gate error("kex_send_kexinit: no kex, cannot rekey"); 201*0Sstevel@tonic-gate return; 202*0Sstevel@tonic-gate } 203*0Sstevel@tonic-gate if (kex->flags & KEX_INIT_SENT) { 204*0Sstevel@tonic-gate debug("KEX_INIT_SENT"); 205*0Sstevel@tonic-gate return; 206*0Sstevel@tonic-gate } 207*0Sstevel@tonic-gate kex->done = 0; 208*0Sstevel@tonic-gate 209*0Sstevel@tonic-gate /* update my proposal -- e.g., add/remove GSS kexalgs */ 210*0Sstevel@tonic-gate kex_do_hook(kex); 211*0Sstevel@tonic-gate 212*0Sstevel@tonic-gate /* generate a random cookie */ 213*0Sstevel@tonic-gate if (buffer_len(&kex->my) < KEX_COOKIE_LEN) 214*0Sstevel@tonic-gate fatal("kex_send_kexinit: kex proposal too short"); 215*0Sstevel@tonic-gate cookie = buffer_ptr(&kex->my); 216*0Sstevel@tonic-gate for (i = 0; i < KEX_COOKIE_LEN; i++) { 217*0Sstevel@tonic-gate if (i % 4 == 0) 218*0Sstevel@tonic-gate rand = arc4random(); 219*0Sstevel@tonic-gate cookie[i] = rand; 220*0Sstevel@tonic-gate rand >>= 8; 221*0Sstevel@tonic-gate } 222*0Sstevel@tonic-gate packet_start(SSH2_MSG_KEXINIT); 223*0Sstevel@tonic-gate packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my)); 224*0Sstevel@tonic-gate packet_send(); 225*0Sstevel@tonic-gate debug("SSH2_MSG_KEXINIT sent"); 226*0Sstevel@tonic-gate kex->flags |= KEX_INIT_SENT; 227*0Sstevel@tonic-gate } 228*0Sstevel@tonic-gate 229*0Sstevel@tonic-gate void 230*0Sstevel@tonic-gate kex_input_kexinit(int type, u_int32_t seq, void *ctxt) 231*0Sstevel@tonic-gate { 232*0Sstevel@tonic-gate char *ptr; 233*0Sstevel@tonic-gate u_int dlen; 234*0Sstevel@tonic-gate int i; 235*0Sstevel@tonic-gate Kex *kex = (Kex *)ctxt; 236*0Sstevel@tonic-gate 237*0Sstevel@tonic-gate debug("SSH2_MSG_KEXINIT received"); 238*0Sstevel@tonic-gate if (kex == NULL) 239*0Sstevel@tonic-gate fatal("kex_input_kexinit: no kex, cannot rekey"); 240*0Sstevel@tonic-gate 241*0Sstevel@tonic-gate ptr = packet_get_raw(&dlen); 242*0Sstevel@tonic-gate buffer_append(&kex->peer, ptr, dlen); 243*0Sstevel@tonic-gate 244*0Sstevel@tonic-gate /* discard packet */ 245*0Sstevel@tonic-gate for (i = 0; i < KEX_COOKIE_LEN; i++) 246*0Sstevel@tonic-gate packet_get_char(); 247*0Sstevel@tonic-gate for (i = 0; i < PROPOSAL_MAX; i++) 248*0Sstevel@tonic-gate xfree(packet_get_string(NULL)); 249*0Sstevel@tonic-gate (void) packet_get_char(); 250*0Sstevel@tonic-gate (void) packet_get_int(); 251*0Sstevel@tonic-gate packet_check_eom(); 252*0Sstevel@tonic-gate 253*0Sstevel@tonic-gate kex_kexinit_finish(kex); 254*0Sstevel@tonic-gate } 255*0Sstevel@tonic-gate 256*0Sstevel@tonic-gate /* 257*0Sstevel@tonic-gate * This is for GSS keyex, where actual KEX offer can change at rekey 258*0Sstevel@tonic-gate * time due to credential expiration/renewal... 259*0Sstevel@tonic-gate */ 260*0Sstevel@tonic-gate static 261*0Sstevel@tonic-gate void 262*0Sstevel@tonic-gate kex_do_hook(Kex *kex) 263*0Sstevel@tonic-gate { 264*0Sstevel@tonic-gate char **prop; 265*0Sstevel@tonic-gate 266*0Sstevel@tonic-gate if (kex->kex_hook == NULL) 267*0Sstevel@tonic-gate return; 268*0Sstevel@tonic-gate 269*0Sstevel@tonic-gate /* Unmarshall my proposal, let the hook modify it, remarshall it */ 270*0Sstevel@tonic-gate prop = kex_buf2prop(&kex->my, NULL); 271*0Sstevel@tonic-gate buffer_clear(&kex->my); 272*0Sstevel@tonic-gate (kex->kex_hook)(kex, prop); 273*0Sstevel@tonic-gate kex_prop2buf(&kex->my, prop); 274*0Sstevel@tonic-gate kex_prop_free(prop); 275*0Sstevel@tonic-gate } 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate Kex * 278*0Sstevel@tonic-gate kex_setup(const char *host, char *proposal[PROPOSAL_MAX], Kex_hook_func hook) 279*0Sstevel@tonic-gate { 280*0Sstevel@tonic-gate Kex *kex; 281*0Sstevel@tonic-gate 282*0Sstevel@tonic-gate kex = xmalloc(sizeof(*kex)); 283*0Sstevel@tonic-gate memset(kex, 0, sizeof(*kex)); 284*0Sstevel@tonic-gate buffer_init(&kex->peer); 285*0Sstevel@tonic-gate buffer_init(&kex->my); 286*0Sstevel@tonic-gate 287*0Sstevel@tonic-gate kex->kex_hook = hook; /* called by kex_send_kexinit() */ 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gate if (host != NULL && *host != '\0') 290*0Sstevel@tonic-gate kex->serverhost = xstrdup(host); 291*0Sstevel@tonic-gate else 292*0Sstevel@tonic-gate kex->server = 1; 293*0Sstevel@tonic-gate 294*0Sstevel@tonic-gate kex_prop2buf(&kex->my, proposal); 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate kex_send_kexinit(kex); 297*0Sstevel@tonic-gate kex_reset_dispatch(); 298*0Sstevel@tonic-gate 299*0Sstevel@tonic-gate return kex; 300*0Sstevel@tonic-gate } 301*0Sstevel@tonic-gate 302*0Sstevel@tonic-gate static void 303*0Sstevel@tonic-gate kex_kexinit_finish(Kex *kex) 304*0Sstevel@tonic-gate { 305*0Sstevel@tonic-gate if (!(kex->flags & KEX_INIT_SENT)) 306*0Sstevel@tonic-gate kex_send_kexinit(kex); 307*0Sstevel@tonic-gate 308*0Sstevel@tonic-gate kex_choose_conf(kex); 309*0Sstevel@tonic-gate 310*0Sstevel@tonic-gate if (kex->kex_type >= 0 && kex->kex_type < KEX_MAX && 311*0Sstevel@tonic-gate kex->kex[kex->kex_type] != NULL) 312*0Sstevel@tonic-gate (kex->kex[kex->kex_type])(kex); 313*0Sstevel@tonic-gate else 314*0Sstevel@tonic-gate fatal("Unsupported key exchange %d", kex->kex_type); 315*0Sstevel@tonic-gate } 316*0Sstevel@tonic-gate 317*0Sstevel@tonic-gate static void 318*0Sstevel@tonic-gate choose_lang(char **lang, char *client, char *server) 319*0Sstevel@tonic-gate { 320*0Sstevel@tonic-gate if (datafellows & SSH_BUG_LOCALES_NOT_LANGTAGS) 321*0Sstevel@tonic-gate *lang = match_list(client, server, NULL); 322*0Sstevel@tonic-gate else 323*0Sstevel@tonic-gate *lang = g11n_srvr_locale_negotiate(client, NULL); 324*0Sstevel@tonic-gate } 325*0Sstevel@tonic-gate static void 326*0Sstevel@tonic-gate choose_enc(Enc *enc, char *client, char *server) 327*0Sstevel@tonic-gate { 328*0Sstevel@tonic-gate char *name = match_list(client, server, NULL); 329*0Sstevel@tonic-gate if (name == NULL) 330*0Sstevel@tonic-gate fatal("no matching cipher found: client %s server %s", client, server); 331*0Sstevel@tonic-gate if ((enc->cipher = cipher_by_name(name)) == NULL) 332*0Sstevel@tonic-gate fatal("matching cipher is not supported: %s", name); 333*0Sstevel@tonic-gate enc->name = name; 334*0Sstevel@tonic-gate enc->enabled = 0; 335*0Sstevel@tonic-gate enc->iv = NULL; 336*0Sstevel@tonic-gate enc->key = NULL; 337*0Sstevel@tonic-gate enc->key_len = cipher_keylen(enc->cipher); 338*0Sstevel@tonic-gate enc->block_size = cipher_blocksize(enc->cipher); 339*0Sstevel@tonic-gate } 340*0Sstevel@tonic-gate static void 341*0Sstevel@tonic-gate choose_mac(Mac *mac, char *client, char *server) 342*0Sstevel@tonic-gate { 343*0Sstevel@tonic-gate char *name = match_list(client, server, NULL); 344*0Sstevel@tonic-gate if (name == NULL) 345*0Sstevel@tonic-gate fatal("no matching mac found: client %s server %s", client, server); 346*0Sstevel@tonic-gate if (mac_init(mac, name) < 0) 347*0Sstevel@tonic-gate fatal("unsupported mac %s", name); 348*0Sstevel@tonic-gate /* truncate the key */ 349*0Sstevel@tonic-gate if (datafellows & SSH_BUG_HMAC) 350*0Sstevel@tonic-gate mac->key_len = 16; 351*0Sstevel@tonic-gate mac->name = name; 352*0Sstevel@tonic-gate mac->key = NULL; 353*0Sstevel@tonic-gate mac->enabled = 0; 354*0Sstevel@tonic-gate } 355*0Sstevel@tonic-gate static void 356*0Sstevel@tonic-gate choose_comp(Comp *comp, char *client, char *server) 357*0Sstevel@tonic-gate { 358*0Sstevel@tonic-gate char *name = match_list(client, server, NULL); 359*0Sstevel@tonic-gate if (name == NULL) 360*0Sstevel@tonic-gate fatal("no matching comp found: client %s server %s", client, server); 361*0Sstevel@tonic-gate if (strcmp(name, "zlib") == 0) { 362*0Sstevel@tonic-gate comp->type = 1; 363*0Sstevel@tonic-gate } else if (strcmp(name, "none") == 0) { 364*0Sstevel@tonic-gate comp->type = 0; 365*0Sstevel@tonic-gate } else { 366*0Sstevel@tonic-gate fatal("unsupported comp %s", name); 367*0Sstevel@tonic-gate } 368*0Sstevel@tonic-gate comp->name = name; 369*0Sstevel@tonic-gate } 370*0Sstevel@tonic-gate static void 371*0Sstevel@tonic-gate choose_kex(Kex *k, char *client, char *server) 372*0Sstevel@tonic-gate { 373*0Sstevel@tonic-gate k->name = match_list(client, server, NULL); 374*0Sstevel@tonic-gate if (k->name == NULL) 375*0Sstevel@tonic-gate fatal("no kex alg"); 376*0Sstevel@tonic-gate /* XXX Finish 3.6/7 merge of kex stuff -- choose_kex() done */ 377*0Sstevel@tonic-gate if (strcmp(k->name, KEX_DH1) == 0) { 378*0Sstevel@tonic-gate k->kex_type = KEX_DH_GRP1_SHA1; 379*0Sstevel@tonic-gate } else if (strcmp(k->name, KEX_DHGEX) == 0) { 380*0Sstevel@tonic-gate k->kex_type = KEX_DH_GEX_SHA1; 381*0Sstevel@tonic-gate #ifdef GSSAPI 382*0Sstevel@tonic-gate } else if (strncmp(k->name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) == 0) { 383*0Sstevel@tonic-gate k->kex_type = KEX_GSS_GRP1_SHA1; 384*0Sstevel@tonic-gate #endif 385*0Sstevel@tonic-gate } else 386*0Sstevel@tonic-gate fatal("bad kex alg %s", k->name); 387*0Sstevel@tonic-gate } 388*0Sstevel@tonic-gate static void 389*0Sstevel@tonic-gate choose_hostkeyalg(Kex *k, char *client, char *server) 390*0Sstevel@tonic-gate { 391*0Sstevel@tonic-gate char *hostkeyalg = match_list(client, server, NULL); 392*0Sstevel@tonic-gate if (hostkeyalg == NULL) 393*0Sstevel@tonic-gate fatal("no hostkey alg"); 394*0Sstevel@tonic-gate k->hostkey_type = key_type_from_name(hostkeyalg); 395*0Sstevel@tonic-gate if (k->hostkey_type == KEY_UNSPEC) 396*0Sstevel@tonic-gate fatal("bad hostkey alg '%s'", hostkeyalg); 397*0Sstevel@tonic-gate xfree(hostkeyalg); 398*0Sstevel@tonic-gate } 399*0Sstevel@tonic-gate 400*0Sstevel@tonic-gate static int 401*0Sstevel@tonic-gate proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) 402*0Sstevel@tonic-gate { 403*0Sstevel@tonic-gate static int check[] = { 404*0Sstevel@tonic-gate PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1 405*0Sstevel@tonic-gate }; 406*0Sstevel@tonic-gate int *idx; 407*0Sstevel@tonic-gate char *p; 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate for (idx = &check[0]; *idx != -1; idx++) { 410*0Sstevel@tonic-gate if ((p = strchr(my[*idx], ',')) != NULL) 411*0Sstevel@tonic-gate *p = '\0'; 412*0Sstevel@tonic-gate if ((p = strchr(peer[*idx], ',')) != NULL) 413*0Sstevel@tonic-gate *p = '\0'; 414*0Sstevel@tonic-gate if (strcmp(my[*idx], peer[*idx]) != 0) { 415*0Sstevel@tonic-gate debug2("proposal mismatch: my %s peer %s", 416*0Sstevel@tonic-gate my[*idx], peer[*idx]); 417*0Sstevel@tonic-gate return (0); 418*0Sstevel@tonic-gate } 419*0Sstevel@tonic-gate } 420*0Sstevel@tonic-gate debug2("proposals match"); 421*0Sstevel@tonic-gate return (1); 422*0Sstevel@tonic-gate } 423*0Sstevel@tonic-gate 424*0Sstevel@tonic-gate static void 425*0Sstevel@tonic-gate kex_choose_conf(Kex *kex) 426*0Sstevel@tonic-gate { 427*0Sstevel@tonic-gate Newkeys *newkeys; 428*0Sstevel@tonic-gate char **my, **peer; 429*0Sstevel@tonic-gate char **cprop, **sprop; 430*0Sstevel@tonic-gate char *p_langs_c2s, *p_langs_s2c; /* peer's langs */ 431*0Sstevel@tonic-gate char *plangs = NULL; /* peer's langs*/ 432*0Sstevel@tonic-gate char *mlangs = NULL; /* my langs */ 433*0Sstevel@tonic-gate int nenc, nmac, ncomp; 434*0Sstevel@tonic-gate int mode; 435*0Sstevel@tonic-gate int ctos; /* direction: if true client-to-server */ 436*0Sstevel@tonic-gate int need; 437*0Sstevel@tonic-gate int first_kex_follows, type; 438*0Sstevel@tonic-gate 439*0Sstevel@tonic-gate my = kex_buf2prop(&kex->my, NULL); 440*0Sstevel@tonic-gate peer = kex_buf2prop(&kex->peer, &first_kex_follows); 441*0Sstevel@tonic-gate 442*0Sstevel@tonic-gate if (kex->server) { 443*0Sstevel@tonic-gate cprop=peer; 444*0Sstevel@tonic-gate sprop=my; 445*0Sstevel@tonic-gate } else { 446*0Sstevel@tonic-gate cprop=my; 447*0Sstevel@tonic-gate sprop=peer; 448*0Sstevel@tonic-gate } 449*0Sstevel@tonic-gate 450*0Sstevel@tonic-gate /* Algorithm Negotiation */ 451*0Sstevel@tonic-gate for (mode = 0; mode < MODE_MAX; mode++) { 452*0Sstevel@tonic-gate newkeys = xmalloc(sizeof(*newkeys)); 453*0Sstevel@tonic-gate memset(newkeys, 0, sizeof(*newkeys)); 454*0Sstevel@tonic-gate kex->newkeys[mode] = newkeys; 455*0Sstevel@tonic-gate ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN); 456*0Sstevel@tonic-gate nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; 457*0Sstevel@tonic-gate nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; 458*0Sstevel@tonic-gate ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; 459*0Sstevel@tonic-gate choose_enc (&newkeys->enc, cprop[nenc], sprop[nenc]); 460*0Sstevel@tonic-gate choose_mac (&newkeys->mac, cprop[nmac], sprop[nmac]); 461*0Sstevel@tonic-gate choose_comp(&newkeys->comp, cprop[ncomp], sprop[ncomp]); 462*0Sstevel@tonic-gate debug("kex: %s %s %s %s", 463*0Sstevel@tonic-gate ctos ? "client->server" : "server->client", 464*0Sstevel@tonic-gate newkeys->enc.name, 465*0Sstevel@tonic-gate newkeys->mac.name, 466*0Sstevel@tonic-gate newkeys->comp.name); 467*0Sstevel@tonic-gate } 468*0Sstevel@tonic-gate choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]); 469*0Sstevel@tonic-gate choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], 470*0Sstevel@tonic-gate sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]); 471*0Sstevel@tonic-gate need = 0; 472*0Sstevel@tonic-gate for (mode = 0; mode < MODE_MAX; mode++) { 473*0Sstevel@tonic-gate newkeys = kex->newkeys[mode]; 474*0Sstevel@tonic-gate if (need < newkeys->enc.key_len) 475*0Sstevel@tonic-gate need = newkeys->enc.key_len; 476*0Sstevel@tonic-gate if (need < newkeys->enc.block_size) 477*0Sstevel@tonic-gate need = newkeys->enc.block_size; 478*0Sstevel@tonic-gate if (need < newkeys->mac.key_len) 479*0Sstevel@tonic-gate need = newkeys->mac.key_len; 480*0Sstevel@tonic-gate } 481*0Sstevel@tonic-gate /* XXX need runden? */ 482*0Sstevel@tonic-gate kex->we_need = need; 483*0Sstevel@tonic-gate 484*0Sstevel@tonic-gate /* ignore the next message if the proposals do not match */ 485*0Sstevel@tonic-gate if (first_kex_follows && !proposals_match(my, peer) && 486*0Sstevel@tonic-gate !(datafellows & SSH_BUG_FIRSTKEX)) { 487*0Sstevel@tonic-gate type = packet_read(); 488*0Sstevel@tonic-gate debug2("skipping next packet (type %u)", type); 489*0Sstevel@tonic-gate } 490*0Sstevel@tonic-gate 491*0Sstevel@tonic-gate /* Language/locale negotiation -- not worth doing on re-key */ 492*0Sstevel@tonic-gate 493*0Sstevel@tonic-gate if (!kex->initial_kex_done) { 494*0Sstevel@tonic-gate p_langs_c2s = peer[PROPOSAL_LANG_CTOS]; 495*0Sstevel@tonic-gate p_langs_s2c = peer[PROPOSAL_LANG_STOC]; 496*0Sstevel@tonic-gate debug("Peer sent proposed langtags, ctos: %s", p_langs_c2s); 497*0Sstevel@tonic-gate debug("Peer sent proposed langtags, stoc: %s", p_langs_s2c); 498*0Sstevel@tonic-gate plangs = NULL; 499*0Sstevel@tonic-gate 500*0Sstevel@tonic-gate /* We propose the same langs for each protocol direction */ 501*0Sstevel@tonic-gate mlangs = my[PROPOSAL_LANG_STOC]; 502*0Sstevel@tonic-gate debug("We proposed langtags, ctos: %s", my[PROPOSAL_LANG_CTOS]); 503*0Sstevel@tonic-gate debug("We proposed langtags, stoc: %s", mlangs); 504*0Sstevel@tonic-gate 505*0Sstevel@tonic-gate /* 506*0Sstevel@tonic-gate * Why oh why did they bother with negotiating langs for 507*0Sstevel@tonic-gate * each protocol direction?! 508*0Sstevel@tonic-gate * 509*0Sstevel@tonic-gate * The semantics of this are vaguely specified, but one can 510*0Sstevel@tonic-gate * imagine using one language (locale) for the whole session and 511*0Sstevel@tonic-gate * a different one for message localization (e.g., 'en_US.UTF-8' 512*0Sstevel@tonic-gate * overall and 'fr' for messages). Weird? Maybe. But lang 513*0Sstevel@tonic-gate * tags don't include codeset info, like locales do... 514*0Sstevel@tonic-gate * 515*0Sstevel@tonic-gate * So, server-side we want: 516*0Sstevel@tonic-gate * - setlocale(LC_ALL, c2s_locale); 517*0Sstevel@tonic-gate * and 518*0Sstevel@tonic-gate * - setlocale(LC_MESSAGES, s2c_locale); 519*0Sstevel@tonic-gate * 520*0Sstevel@tonic-gate * Client-side we don't really care. But we could do: 521*0Sstevel@tonic-gate * 522*0Sstevel@tonic-gate * - when very verbose, tell the use what lang the server's 523*0Sstevel@tonic-gate * messages are in, if left out in the protocol 524*0Sstevel@tonic-gate * - when sending messages to the server, and if applicable, we 525*0Sstevel@tonic-gate * can localize them according to the language negotiated for 526*0Sstevel@tonic-gate * that direction. 527*0Sstevel@tonic-gate * 528*0Sstevel@tonic-gate * But for now we do nothing on the client side. 529*0Sstevel@tonic-gate */ 530*0Sstevel@tonic-gate if ((p_langs_c2s && *p_langs_c2s) && !(p_langs_s2c && *p_langs_s2c)) 531*0Sstevel@tonic-gate plangs = p_langs_c2s; 532*0Sstevel@tonic-gate else if ((p_langs_s2c && *p_langs_s2c) && !(p_langs_c2s && *p_langs_c2s)) 533*0Sstevel@tonic-gate plangs = p_langs_s2c; 534*0Sstevel@tonic-gate else 535*0Sstevel@tonic-gate plangs = p_langs_c2s; 536*0Sstevel@tonic-gate 537*0Sstevel@tonic-gate if (kex->server) { 538*0Sstevel@tonic-gate if (plangs && mlangs && *plangs && *mlangs) { 539*0Sstevel@tonic-gate char *locale; 540*0Sstevel@tonic-gate 541*0Sstevel@tonic-gate choose_lang(&locale, plangs, mlangs); 542*0Sstevel@tonic-gate if (locale) { 543*0Sstevel@tonic-gate g11n_setlocale(LC_ALL, locale); 544*0Sstevel@tonic-gate debug("Negotiated main locale: %s", locale); 545*0Sstevel@tonic-gate packet_send_debug("Negotiated main locale: %s", locale); 546*0Sstevel@tonic-gate } 547*0Sstevel@tonic-gate if (plangs != p_langs_s2c && 548*0Sstevel@tonic-gate p_langs_s2c && *p_langs_s2c) { 549*0Sstevel@tonic-gate choose_lang(&locale, p_langs_s2c, mlangs); 550*0Sstevel@tonic-gate if (locale) { 551*0Sstevel@tonic-gate g11n_setlocale(LC_MESSAGES, locale); 552*0Sstevel@tonic-gate debug("Negotiated messages locale: %s", locale); 553*0Sstevel@tonic-gate packet_send_debug("Negotiated messages locale: %s", locale); 554*0Sstevel@tonic-gate } 555*0Sstevel@tonic-gate } 556*0Sstevel@tonic-gate /* 557*0Sstevel@tonic-gate * Should we free locale? Or does setlocale 558*0Sstevel@tonic-gate * retain a reference? 559*0Sstevel@tonic-gate */ 560*0Sstevel@tonic-gate /*xfree(locale);*/ 561*0Sstevel@tonic-gate } 562*0Sstevel@tonic-gate } 563*0Sstevel@tonic-gate else { 564*0Sstevel@tonic-gate if (plangs && mlangs && *plangs && *mlangs && 565*0Sstevel@tonic-gate !(datafellows & SSH_BUG_LOCALES_NOT_LANGTAGS)) { 566*0Sstevel@tonic-gate char *lang; 567*0Sstevel@tonic-gate lang = g11n_clnt_langtag_negotiate(mlangs, plangs); 568*0Sstevel@tonic-gate if (lang) { 569*0Sstevel@tonic-gate session_lang = lang; 570*0Sstevel@tonic-gate debug("Negotiated lang: %s", lang); 571*0Sstevel@tonic-gate } 572*0Sstevel@tonic-gate } 573*0Sstevel@tonic-gate } 574*0Sstevel@tonic-gate } 575*0Sstevel@tonic-gate 576*0Sstevel@tonic-gate kex_prop_free(my); 577*0Sstevel@tonic-gate kex_prop_free(peer); 578*0Sstevel@tonic-gate } 579*0Sstevel@tonic-gate 580*0Sstevel@tonic-gate static u_char * 581*0Sstevel@tonic-gate derive_key(Kex *kex, int id, int need, u_char *hash, BIGNUM *shared_secret) 582*0Sstevel@tonic-gate { 583*0Sstevel@tonic-gate Buffer b; 584*0Sstevel@tonic-gate const EVP_MD *evp_md = EVP_sha1(); 585*0Sstevel@tonic-gate EVP_MD_CTX md; 586*0Sstevel@tonic-gate char c = id; 587*0Sstevel@tonic-gate int have; 588*0Sstevel@tonic-gate int mdsz = EVP_MD_size(evp_md); 589*0Sstevel@tonic-gate u_char *digest = xmalloc(roundup(need, mdsz)); 590*0Sstevel@tonic-gate 591*0Sstevel@tonic-gate buffer_init(&b); 592*0Sstevel@tonic-gate buffer_put_bignum2(&b, shared_secret); 593*0Sstevel@tonic-gate 594*0Sstevel@tonic-gate /* K1 = HASH(K || H || "A" || session_id) */ 595*0Sstevel@tonic-gate EVP_DigestInit(&md, evp_md); 596*0Sstevel@tonic-gate if (!(datafellows & SSH_BUG_DERIVEKEY)) 597*0Sstevel@tonic-gate EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); 598*0Sstevel@tonic-gate EVP_DigestUpdate(&md, hash, mdsz); 599*0Sstevel@tonic-gate EVP_DigestUpdate(&md, &c, 1); 600*0Sstevel@tonic-gate EVP_DigestUpdate(&md, kex->session_id, kex->session_id_len); 601*0Sstevel@tonic-gate EVP_DigestFinal(&md, digest, NULL); 602*0Sstevel@tonic-gate 603*0Sstevel@tonic-gate /* 604*0Sstevel@tonic-gate * expand key: 605*0Sstevel@tonic-gate * Kn = HASH(K || H || K1 || K2 || ... || Kn-1) 606*0Sstevel@tonic-gate * Key = K1 || K2 || ... || Kn 607*0Sstevel@tonic-gate */ 608*0Sstevel@tonic-gate for (have = mdsz; need > have; have += mdsz) { 609*0Sstevel@tonic-gate EVP_DigestInit(&md, evp_md); 610*0Sstevel@tonic-gate if (!(datafellows & SSH_BUG_DERIVEKEY)) 611*0Sstevel@tonic-gate EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); 612*0Sstevel@tonic-gate EVP_DigestUpdate(&md, hash, mdsz); 613*0Sstevel@tonic-gate EVP_DigestUpdate(&md, digest, have); 614*0Sstevel@tonic-gate EVP_DigestFinal(&md, digest + have, NULL); 615*0Sstevel@tonic-gate } 616*0Sstevel@tonic-gate buffer_free(&b); 617*0Sstevel@tonic-gate #ifdef DEBUG_KEX 618*0Sstevel@tonic-gate fprintf(stderr, "key '%c'== ", c); 619*0Sstevel@tonic-gate dump_digest("key", digest, need); 620*0Sstevel@tonic-gate #endif 621*0Sstevel@tonic-gate return digest; 622*0Sstevel@tonic-gate } 623*0Sstevel@tonic-gate 624*0Sstevel@tonic-gate Newkeys *current_keys[MODE_MAX]; 625*0Sstevel@tonic-gate 626*0Sstevel@tonic-gate #define NKEYS 6 627*0Sstevel@tonic-gate void 628*0Sstevel@tonic-gate kex_derive_keys(Kex *kex, u_char *hash, BIGNUM *shared_secret) 629*0Sstevel@tonic-gate { 630*0Sstevel@tonic-gate u_char *keys[NKEYS]; 631*0Sstevel@tonic-gate int i, mode, ctos; 632*0Sstevel@tonic-gate 633*0Sstevel@tonic-gate for (i = 0; i < NKEYS; i++) 634*0Sstevel@tonic-gate keys[i] = derive_key(kex, 'A'+i, kex->we_need, hash, shared_secret); 635*0Sstevel@tonic-gate 636*0Sstevel@tonic-gate debug2("kex_derive_keys"); 637*0Sstevel@tonic-gate for (mode = 0; mode < MODE_MAX; mode++) { 638*0Sstevel@tonic-gate current_keys[mode] = kex->newkeys[mode]; 639*0Sstevel@tonic-gate kex->newkeys[mode] = NULL; 640*0Sstevel@tonic-gate ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN); 641*0Sstevel@tonic-gate current_keys[mode]->enc.iv = keys[ctos ? 0 : 1]; 642*0Sstevel@tonic-gate current_keys[mode]->enc.key = keys[ctos ? 2 : 3]; 643*0Sstevel@tonic-gate current_keys[mode]->mac.key = keys[ctos ? 4 : 5]; 644*0Sstevel@tonic-gate } 645*0Sstevel@tonic-gate } 646*0Sstevel@tonic-gate 647*0Sstevel@tonic-gate Newkeys * 648*0Sstevel@tonic-gate kex_get_newkeys(int mode) 649*0Sstevel@tonic-gate { 650*0Sstevel@tonic-gate Newkeys *ret; 651*0Sstevel@tonic-gate 652*0Sstevel@tonic-gate ret = current_keys[mode]; 653*0Sstevel@tonic-gate current_keys[mode] = NULL; 654*0Sstevel@tonic-gate return ret; 655*0Sstevel@tonic-gate } 656*0Sstevel@tonic-gate 657*0Sstevel@tonic-gate #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) 658*0Sstevel@tonic-gate void 659*0Sstevel@tonic-gate dump_digest(char *msg, u_char *digest, int len) 660*0Sstevel@tonic-gate { 661*0Sstevel@tonic-gate int i; 662*0Sstevel@tonic-gate 663*0Sstevel@tonic-gate fprintf(stderr, "%s\n", msg); 664*0Sstevel@tonic-gate for (i = 0; i< len; i++) { 665*0Sstevel@tonic-gate fprintf(stderr, "%02x", digest[i]); 666*0Sstevel@tonic-gate if (i%32 == 31) 667*0Sstevel@tonic-gate fprintf(stderr, "\n"); 668*0Sstevel@tonic-gate else if (i%8 == 7) 669*0Sstevel@tonic-gate fprintf(stderr, " "); 670*0Sstevel@tonic-gate } 671*0Sstevel@tonic-gate fprintf(stderr, "\n"); 672*0Sstevel@tonic-gate } 673*0Sstevel@tonic-gate #endif 674