source: trunk/third/openssh/kex.c @ 18763

Revision 18763, 11.9 KB checked in by zacheiss, 22 years ago (diff)
Merge openssh 3.5p1.
Line 
1/*
2 * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26RCSID("$OpenBSD: kex.c,v 1.51 2002/06/24 14:55:38 markus Exp $");
27
28#include <openssl/crypto.h>
29
30#include "ssh2.h"
31#include "xmalloc.h"
32#include "buffer.h"
33#include "bufaux.h"
34#include "packet.h"
35#include "compat.h"
36#include "cipher.h"
37#include "kex.h"
38#include "key.h"
39#include "log.h"
40#include "mac.h"
41#include "match.h"
42#include "dispatch.h"
43#include "monitor.h"
44
45#ifdef GSSAPI
46#include "ssh-gss.h"
47#endif
48
49#define KEX_COOKIE_LEN  16
50
51/* Use privilege separation for sshd */
52int use_privsep;
53struct monitor *pmonitor;
54
55
56/* prototype */
57static void kex_kexinit_finish(Kex *);
58static void kex_choose_conf(Kex *);
59
60/* put algorithm proposal into buffer */
61static void
62kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
63{
64        int i;
65
66        buffer_clear(b);
67        /*
68         * add a dummy cookie, the cookie will be overwritten by
69         * kex_send_kexinit(), each time a kexinit is set
70         */
71        for (i = 0; i < KEX_COOKIE_LEN; i++)
72                buffer_put_char(b, 0);
73        for (i = 0; i < PROPOSAL_MAX; i++)
74                buffer_put_cstring(b, proposal[i]);
75        buffer_put_char(b, 0);                  /* first_kex_packet_follows */
76        buffer_put_int(b, 0);                   /* uint32 reserved */
77}
78
79/* parse buffer and return algorithm proposal */
80static char **
81kex_buf2prop(Buffer *raw)
82{
83        Buffer b;
84        int i;
85        char **proposal;
86
87        proposal = xmalloc(PROPOSAL_MAX * sizeof(char *));
88
89        buffer_init(&b);
90        buffer_append(&b, buffer_ptr(raw), buffer_len(raw));
91        /* skip cookie */
92        for (i = 0; i < KEX_COOKIE_LEN; i++)
93                buffer_get_char(&b);
94        /* extract kex init proposal strings */
95        for (i = 0; i < PROPOSAL_MAX; i++) {
96                proposal[i] = buffer_get_string(&b,NULL);
97                debug2("kex_parse_kexinit: %s", proposal[i]);
98        }
99        /* first kex follows / reserved */
100        i = buffer_get_char(&b);
101        debug2("kex_parse_kexinit: first_kex_follows %d ", i);
102        i = buffer_get_int(&b);
103        debug2("kex_parse_kexinit: reserved %d ", i);
104        buffer_free(&b);
105        return proposal;
106}
107
108static void
109kex_prop_free(char **proposal)
110{
111        int i;
112
113        for (i = 0; i < PROPOSAL_MAX; i++)
114                xfree(proposal[i]);
115        xfree(proposal);
116}
117
118static void
119kex_protocol_error(int type, u_int32_t seq, void *ctxt)
120{
121        error("Hm, kex protocol error: type %d seq %u", type, seq);
122}
123
124static void
125kex_reset_dispatch(void)
126{
127        dispatch_range(SSH2_MSG_TRANSPORT_MIN,
128            SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
129        dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
130}
131
132void
133kex_finish(Kex *kex)
134{
135        kex_reset_dispatch();
136
137        packet_start(SSH2_MSG_NEWKEYS);
138        packet_send();
139        /* packet_write_wait(); */
140        debug("SSH2_MSG_NEWKEYS sent");
141
142        debug("waiting for SSH2_MSG_NEWKEYS");
143        packet_read_expect(SSH2_MSG_NEWKEYS);
144        packet_check_eom();
145        debug("SSH2_MSG_NEWKEYS received");
146
147        kex->done = 1;
148        buffer_clear(&kex->peer);
149        /* buffer_clear(&kex->my); */
150        kex->flags &= ~KEX_INIT_SENT;
151        xfree(kex->name);
152        kex->name = NULL;
153}
154
155void
156kex_send_kexinit(Kex *kex)
157{
158        u_int32_t rand = 0;
159        u_char *cookie;
160        int i;
161
162        if (kex == NULL) {
163                error("kex_send_kexinit: no kex, cannot rekey");
164                return;
165        }
166        if (kex->flags & KEX_INIT_SENT) {
167                debug("KEX_INIT_SENT");
168                return;
169        }
170        kex->done = 0;
171
172        /* generate a random cookie */
173        if (buffer_len(&kex->my) < KEX_COOKIE_LEN)
174                fatal("kex_send_kexinit: kex proposal too short");
175        cookie = buffer_ptr(&kex->my);
176        for (i = 0; i < KEX_COOKIE_LEN; i++) {
177                if (i % 4 == 0)
178                        rand = arc4random();
179                cookie[i] = rand;
180                rand >>= 8;
181        }
182        packet_start(SSH2_MSG_KEXINIT);
183        packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my));
184        packet_send();
185        debug("SSH2_MSG_KEXINIT sent");
186        kex->flags |= KEX_INIT_SENT;
187}
188
189void
190kex_input_kexinit(int type, u_int32_t seq, void *ctxt)
191{
192        char *ptr;
193        int dlen;
194        int i;
195        Kex *kex = (Kex *)ctxt;
196
197        debug("SSH2_MSG_KEXINIT received");
198        if (kex == NULL)
199                fatal("kex_input_kexinit: no kex, cannot rekey");
200
201        ptr = packet_get_raw(&dlen);
202        buffer_append(&kex->peer, ptr, dlen);
203
204        /* discard packet */
205        for (i = 0; i < KEX_COOKIE_LEN; i++)
206                packet_get_char();
207        for (i = 0; i < PROPOSAL_MAX; i++)
208                xfree(packet_get_string(NULL));
209        (void) packet_get_char();
210        (void) packet_get_int();
211        packet_check_eom();
212
213        kex_kexinit_finish(kex);
214}
215
216Kex *
217kex_setup(char *proposal[PROPOSAL_MAX])
218{
219        Kex *kex;
220
221        kex = xmalloc(sizeof(*kex));
222        memset(kex, 0, sizeof(*kex));
223        buffer_init(&kex->peer);
224        buffer_init(&kex->my);
225        kex_prop2buf(&kex->my, proposal);
226        kex->done = 0;
227
228        kex_send_kexinit(kex);                                  /* we start */
229        kex_reset_dispatch();
230
231        return kex;
232}
233
234static void
235kex_kexinit_finish(Kex *kex)
236{
237        if (!(kex->flags & KEX_INIT_SENT))
238                kex_send_kexinit(kex);
239
240        kex_choose_conf(kex);
241
242        switch (kex->kex_type) {
243        case DH_GRP1_SHA1:
244                kexdh(kex);
245                break;
246        case DH_GEX_SHA1:
247                kexgex(kex);
248                break;
249#ifdef GSSAPI
250        case GSS_GRP1_SHA1:
251                kexgss(kex);
252                break;
253#endif
254        default:
255                fatal("Unsupported key exchange %d", kex->kex_type);
256        }
257}
258
259static void
260choose_enc(Enc *enc, char *client, char *server)
261{
262        char *name = match_list(client, server, NULL);
263        if (name == NULL)
264                fatal("no matching cipher found: client %s server %s", client, server);
265        if ((enc->cipher = cipher_by_name(name)) == NULL)
266                fatal("matching cipher is not supported: %s", name);
267        enc->name = name;
268        enc->enabled = 0;
269        enc->iv = NULL;
270        enc->key = NULL;
271        enc->key_len = cipher_keylen(enc->cipher);
272        enc->block_size = cipher_blocksize(enc->cipher);
273}
274static void
275choose_mac(Mac *mac, char *client, char *server)
276{
277        char *name = match_list(client, server, NULL);
278        if (name == NULL)
279                fatal("no matching mac found: client %s server %s", client, server);
280        if (mac_init(mac, name) < 0)
281                fatal("unsupported mac %s", name);
282        /* truncate the key */
283        if (datafellows & SSH_BUG_HMAC)
284                mac->key_len = 16;
285        mac->name = name;
286        mac->key = NULL;
287        mac->enabled = 0;
288}
289static void
290choose_comp(Comp *comp, char *client, char *server)
291{
292        char *name = match_list(client, server, NULL);
293        if (name == NULL)
294                fatal("no matching comp found: client %s server %s", client, server);
295        if (strcmp(name, "zlib") == 0) {
296                comp->type = 1;
297        } else if (strcmp(name, "none") == 0) {
298                comp->type = 0;
299        } else {
300                fatal("unsupported comp %s", name);
301        }
302        comp->name = name;
303}
304static void
305choose_kex(Kex *k, char *client, char *server)
306{
307        k->name = match_list(client, server, NULL);
308        if (k->name == NULL)
309                fatal("No key exchange algorithm");
310        if (strcmp(k->name, KEX_DH1) == 0) {
311                k->kex_type = DH_GRP1_SHA1;
312        } else if (strcmp(k->name, KEX_DHGEX) == 0) {
313                k->kex_type = DH_GEX_SHA1;
314#ifdef GSSAPI
315        } else if (strncmp(k->name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) == 0) {
316                k->kex_type = GSS_GRP1_SHA1;
317#endif
318        } else
319                fatal("bad kex alg %s", k->name);
320}
321static void
322choose_hostkeyalg(Kex *k, char *client, char *server)
323{
324        char *hostkeyalg = match_list(client, server, NULL);
325        if (hostkeyalg == NULL)
326                fatal("no hostkey alg");
327        k->hostkey_type = key_type_from_name(hostkeyalg);
328        if (k->hostkey_type == KEY_UNSPEC)
329                fatal("bad hostkey alg '%s'", hostkeyalg);
330        xfree(hostkeyalg);
331}
332
333static void
334kex_choose_conf(Kex *kex)
335{
336        Newkeys *newkeys;
337        char **my, **peer;
338        char **cprop, **sprop;
339        int nenc, nmac, ncomp;
340        int mode;
341        int ctos;                               /* direction: if true client-to-server */
342        int need;
343
344        my   = kex_buf2prop(&kex->my);
345        peer = kex_buf2prop(&kex->peer);
346
347        if (kex->server) {
348                cprop=peer;
349                sprop=my;
350        } else {
351                cprop=my;
352                sprop=peer;
353        }
354
355        /* Algorithm Negotiation */
356        for (mode = 0; mode < MODE_MAX; mode++) {
357                newkeys = xmalloc(sizeof(*newkeys));
358                memset(newkeys, 0, sizeof(*newkeys));
359                kex->newkeys[mode] = newkeys;
360                ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN);
361                nenc  = ctos ? PROPOSAL_ENC_ALGS_CTOS  : PROPOSAL_ENC_ALGS_STOC;
362                nmac  = ctos ? PROPOSAL_MAC_ALGS_CTOS  : PROPOSAL_MAC_ALGS_STOC;
363                ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
364                choose_enc (&newkeys->enc,  cprop[nenc],  sprop[nenc]);
365                choose_mac (&newkeys->mac,  cprop[nmac],  sprop[nmac]);
366                choose_comp(&newkeys->comp, cprop[ncomp], sprop[ncomp]);
367                debug("kex: %s %s %s %s",
368                    ctos ? "client->server" : "server->client",
369                    newkeys->enc.name,
370                    newkeys->mac.name,
371                    newkeys->comp.name);
372        }
373        choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]);
374        choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
375            sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]);
376        need = 0;
377        for (mode = 0; mode < MODE_MAX; mode++) {
378                newkeys = kex->newkeys[mode];
379                if (need < newkeys->enc.key_len)
380                        need = newkeys->enc.key_len;
381                if (need < newkeys->enc.block_size)
382                        need = newkeys->enc.block_size;
383                if (need < newkeys->mac.key_len)
384                        need = newkeys->mac.key_len;
385        }
386        /* XXX need runden? */
387        kex->we_need = need;
388
389        kex_prop_free(my);
390        kex_prop_free(peer);
391}
392
393static u_char *
394derive_key(Kex *kex, int id, int need, u_char *hash, BIGNUM *shared_secret)
395{
396        Buffer b;
397        const EVP_MD *evp_md = EVP_sha1();
398        EVP_MD_CTX md;
399        char c = id;
400        int have;
401        int mdsz = EVP_MD_size(evp_md);
402        u_char *digest = xmalloc(roundup(need, mdsz));
403
404        buffer_init(&b);
405        buffer_put_bignum2(&b, shared_secret);
406
407        /* K1 = HASH(K || H || "A" || session_id) */
408        EVP_DigestInit(&md, evp_md);
409        if (!(datafellows & SSH_BUG_DERIVEKEY))
410                EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
411        EVP_DigestUpdate(&md, hash, mdsz);
412        EVP_DigestUpdate(&md, &c, 1);
413        EVP_DigestUpdate(&md, kex->session_id, kex->session_id_len);
414        EVP_DigestFinal(&md, digest, NULL);
415
416        /*
417         * expand key:
418         * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
419         * Key = K1 || K2 || ... || Kn
420         */
421        for (have = mdsz; need > have; have += mdsz) {
422                EVP_DigestInit(&md, evp_md);
423                if (!(datafellows & SSH_BUG_DERIVEKEY))
424                        EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
425                EVP_DigestUpdate(&md, hash, mdsz);
426                EVP_DigestUpdate(&md, digest, have);
427                EVP_DigestFinal(&md, digest + have, NULL);
428        }
429        buffer_free(&b);
430#ifdef DEBUG_KEX
431        fprintf(stderr, "key '%c'== ", c);
432        dump_digest("key", digest, need);
433#endif
434        return digest;
435}
436
437Newkeys *current_keys[MODE_MAX];
438
439#define NKEYS   6
440void
441kex_derive_keys(Kex *kex, u_char *hash, BIGNUM *shared_secret)
442{
443        u_char *keys[NKEYS];
444        int i, mode, ctos;
445
446        for (i = 0; i < NKEYS; i++)
447                keys[i] = derive_key(kex, 'A'+i, kex->we_need, hash, shared_secret);
448
449        debug("kex_derive_keys");
450        for (mode = 0; mode < MODE_MAX; mode++) {
451                current_keys[mode] = kex->newkeys[mode];
452                kex->newkeys[mode] = NULL;
453                ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN);
454                current_keys[mode]->enc.iv  = keys[ctos ? 0 : 1];
455                current_keys[mode]->enc.key = keys[ctos ? 2 : 3];
456                current_keys[mode]->mac.key = keys[ctos ? 4 : 5];
457        }
458}
459
460Newkeys *
461kex_get_newkeys(int mode)
462{
463        Newkeys *ret;
464
465        ret = current_keys[mode];
466        current_keys[mode] = NULL;
467        return ret;
468}
469
470#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH)
471void
472dump_digest(char *msg, u_char *digest, int len)
473{
474        int i;
475
476        fprintf(stderr, "%s\n", msg);
477        for (i = 0; i< len; i++) {
478                fprintf(stderr, "%02x", digest[i]);
479                if (i%32 == 31)
480                        fprintf(stderr, "\n");
481                else if (i%8 == 7)
482                        fprintf(stderr, " ");
483        }
484        fprintf(stderr, "\n");
485}
486#endif
Note: See TracBrowser for help on using the repository browser.