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

Revision 18763, 13.0 KB checked in by zacheiss, 22 years ago (diff)
Merge openssh 3.5p1.
Line 
1/*
2 * Copyright (c) 2001,2002 Simon Wilkinson. 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"
26
27#ifdef GSSAPI
28
29#include <openssl/crypto.h>
30#include <openssl/bn.h>
31
32#include "xmalloc.h"
33#include "buffer.h"
34#include "bufaux.h"
35#include "kex.h"
36#include "log.h"
37#include "packet.h"
38#include "dh.h"
39#include "ssh2.h"
40#include "ssh-gss.h"
41#include "monitor_wrap.h"
42
43/* This is now the same as the DH hash ... */
44
45u_char *
46kex_gssapi_hash(
47    char *client_version_string,
48    char *server_version_string,
49    char *ckexinit, int ckexinitlen,
50    char *skexinit, int skexinitlen,
51    u_char *serverhostkeyblob, int sbloblen,
52    BIGNUM *client_dh_pub,
53    BIGNUM *server_dh_pub,
54    BIGNUM *shared_secret)
55{
56        Buffer b;
57        static u_char digest[EVP_MAX_MD_SIZE];
58        EVP_MD *evp_md = EVP_sha1();
59        EVP_MD_CTX md;
60
61        buffer_init(&b);
62        buffer_put_string(&b, client_version_string, strlen(client_version_string));
63        buffer_put_string(&b, server_version_string, strlen(server_version_string));
64
65        /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
66        buffer_put_int(&b, ckexinitlen+1);
67        buffer_put_char(&b, SSH2_MSG_KEXINIT);
68        buffer_append(&b, ckexinit, ckexinitlen);
69        buffer_put_int(&b, skexinitlen+1);
70        buffer_put_char(&b, SSH2_MSG_KEXINIT);
71        buffer_append(&b, skexinit, skexinitlen);
72
73        buffer_put_string(&b, serverhostkeyblob, sbloblen);
74        buffer_put_bignum2(&b, client_dh_pub);
75        buffer_put_bignum2(&b, server_dh_pub);
76        buffer_put_bignum2(&b, shared_secret);
77
78#ifdef DEBUG_KEX
79        buffer_dump(&b);
80#endif
81        EVP_DigestInit(&md, evp_md);
82        EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
83        EVP_DigestFinal(&md, digest, NULL);
84
85        buffer_free(&b);
86
87#ifdef DEBUG_KEX
88        dump_digest("hash", digest, evp_md->md_size);
89#endif
90        return digest;
91}
92
93void
94kexgss_client(Kex *kex)
95{
96        gss_buffer_desc gssbuf,send_tok,recv_tok, msg_tok, *token_ptr;
97        Gssctxt *ctxt;
98        OM_uint32 maj_status, min_status, ret_flags;
99        unsigned int klen, kout;
100        DH *dh;
101        BIGNUM *dh_server_pub = 0;
102        BIGNUM *shared_secret = 0;     
103        unsigned char *kbuf;
104        unsigned char *hash;
105        unsigned char *serverhostkey;
106        int type = 0;
107        int first = 1;
108        int slen = 0;
109       
110        /* Initialise our GSSAPI world */
111        ssh_gssapi_build_ctx(&ctxt);
112        if (ssh_gssapi_id_kex(ctxt,kex->name)==NULL) {
113                fatal("Couldn't identify host exchange");
114        }
115        if (ssh_gssapi_import_name(ctxt,kex->host)) {
116                fatal("Couldn't import hostname ");
117        }
118       
119        /* This code should match that in ssh_dh1_client */
120               
121        /* Step 1 - e is dh->pub_key */
122        dh = dh_new_group1();
123        dh_gen_key(dh, kex->we_need * 8);
124
125        /* This is f, we initialise it now to make life easier */
126        dh_server_pub = BN_new();
127        if (dh_server_pub == NULL) {
128                fatal("dh_server_pub == NULL");
129        }
130               
131        token_ptr = GSS_C_NO_BUFFER;
132                         
133        do {
134                debug("Calling gss_init_sec_context");
135               
136                maj_status=ssh_gssapi_init_ctx(ctxt,
137                                               kex->options.gss_deleg_creds,
138                                               token_ptr,&send_tok,
139                                               &ret_flags);
140
141                if (GSS_ERROR(maj_status)) {
142                        fatal("gss_init_context failed");
143                }
144
145                /* If we've got an old receive buffer get rid of it */
146                if (token_ptr != GSS_C_NO_BUFFER)
147                        (void) gss_release_buffer(&min_status, &recv_tok);
148       
149               
150                if (maj_status == GSS_S_COMPLETE) {
151                        /* If mutual state flag is not true, kex fails */
152                        if (!(ret_flags & GSS_C_MUTUAL_FLAG)) {
153                                fatal("Mutual authentication failed");
154                        }
155                        /* If integ avail flag is not true kex fails */
156                        if (!(ret_flags & GSS_C_INTEG_FLAG)) {
157                                fatal("Integrity check failed");
158                        }
159                }
160               
161                /* If we have data to send, then the last message that we
162                 * received cannot have been a 'complete'. */
163                if (send_tok.length !=0) {
164                        if (first) {
165                                packet_start(SSH2_MSG_KEXGSS_INIT);
166                                packet_put_string(send_tok.value,
167                                                  send_tok.length);
168                                packet_put_bignum2(dh->pub_key);
169                                first=0;
170                        } else {
171                                packet_start(SSH2_MSG_KEXGSS_CONTINUE);
172                                packet_put_string(send_tok.value,
173                                                  send_tok.length);
174                        }
175                        packet_send();
176                        packet_write_wait();
177
178                       
179                        /* If we've sent them data, they'd better be polite
180                         * and reply. */
181               
182                        type = packet_read();
183                        switch (type) {
184                        case SSH2_MSG_KEXGSS_HOSTKEY:
185                                debug("Received KEXGSS_HOSTKEY");
186                                serverhostkey=packet_get_string(&slen);
187                                break;
188                        case SSH2_MSG_KEXGSS_CONTINUE:
189                                debug("Received GSSAPI_CONTINUE");
190                                if (maj_status == GSS_S_COMPLETE)
191                                        fatal("GSSAPI Continue received from server when complete");
192                                recv_tok.value=packet_get_string(&recv_tok.length);
193                                break;
194                        case SSH2_MSG_KEXGSS_COMPLETE:
195                                debug("Received GSSAPI_COMPLETE");
196                                packet_get_bignum2(dh_server_pub);
197                                msg_tok.value=
198                                    packet_get_string(&msg_tok.length);
199
200                                /* Is there a token included? */
201                                if (packet_get_char()) {
202                                        recv_tok.value=
203                                            packet_get_string(&recv_tok.length);
204                                        /* If we're already complete - protocol error */
205                                        if (maj_status == GSS_S_COMPLETE)
206                                                packet_disconnect("Protocol error: received token when complete");
207                                } else {
208                                        /* No token included */
209                                        if (maj_status != GSS_S_COMPLETE)
210                                                packet_disconnect("Protocol error: did not receive final token");
211                                }
212                                break;
213                        default:
214                                packet_disconnect("Protocol error: didn't expect packet type %d",
215                                type);
216                        }
217                        token_ptr=&recv_tok;
218                }
219
220        } while (maj_status & GSS_S_CONTINUE_NEEDED);
221       
222        /* We _must_ have received a COMPLETE message in reply from the
223         * server, which will have set dh_server_pub and msg_tok */
224         
225        if (type!=SSH2_MSG_KEXGSS_COMPLETE)
226           fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
227                       
228        /* Check f in range [1, p-1] */
229        if (!dh_pub_is_valid(dh, dh_server_pub))
230                        packet_disconnect("bad server public DH value");
231                       
232        /* compute K=f^x mod p */
233        klen = DH_size(dh);
234        kbuf = xmalloc(klen);
235        kout = DH_compute_key(kbuf, dh_server_pub, dh);
236       
237        shared_secret = BN_new();
238        BN_bin2bn(kbuf,kout, shared_secret);
239        memset(kbuf, 0, klen);
240        xfree(kbuf);
241       
242        hash = kex_gssapi_hash(
243            kex->client_version_string,
244            kex->server_version_string,
245            buffer_ptr(&kex->my), buffer_len(&kex->my),
246            buffer_ptr(&kex->peer), buffer_len(&kex->peer),
247            serverhostkey, slen, /* server host key */
248            dh->pub_key,        /* e */
249            dh_server_pub,      /* f */
250            shared_secret       /* K */
251        );
252       
253        gssbuf.value=hash;
254        gssbuf.length=20;
255       
256        /* Verify that H matches the token we just got. */
257                if ((maj_status = gss_verify_mic(&min_status,
258                                         ctxt->context,
259                                         &gssbuf,
260                                         &msg_tok,
261                                         NULL))) {
262
263                packet_disconnect("Hash's MIC didn't verify");
264        }       
265       
266        DH_free(dh);
267        ssh_gssapi_delete_ctx(&ctxt);
268        /* save session id */
269        if (kex->session_id == NULL) {
270                kex->session_id_len = 20;
271                kex->session_id = xmalloc(kex->session_id_len);
272                memcpy(kex->session_id, hash, kex->session_id_len);
273        }
274       
275        kex_derive_keys(kex, hash, shared_secret);
276        BN_clear_free(shared_secret);
277        kex_finish(kex);
278}
279
280
281
282
283void
284kexgss_server(Kex *kex)
285{
286
287        OM_uint32 maj_status, min_status;
288       
289        /* Some GSSAPI implementations use the input value of ret_flags (an
290         * output variable) as a means of triggering mechanism specific
291         * features. Initializing it to zero avoids inadvertently
292         * activating this non-standard behaviour.*/
293
294        OM_uint32 ret_flags = 0;
295        gss_buffer_desc gssbuf,send_tok,recv_tok,msg_tok;
296        Gssctxt *ctxt = NULL;
297        unsigned int klen, kout;
298        unsigned char *kbuf;
299        unsigned char *hash;
300        DH *dh;
301        BIGNUM *shared_secret = NULL;
302        BIGNUM *dh_client_pub = NULL;
303        int type =0;
304        gss_OID oid;
305       
306        /* Initialise GSSAPI */
307
308        debug2("%s: Identifying %s",__func__,kex->name);
309        oid=ssh_gssapi_id_kex(ctxt,kex->name);
310        if (oid==NULL) {
311           fatal("Unknown gssapi mechanism");
312        }
313       
314        debug2("%s: Acquiring credentials",__func__);
315       
316        if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,oid))))
317                fatal("Unable to acquire credentials for the server");
318                                                                                                                               
319        do {
320                debug("Wait SSH2_MSG_GSSAPI_INIT");
321                type = packet_read();
322                switch(type) {
323                case SSH2_MSG_KEXGSS_INIT:
324                        if (dh_client_pub!=NULL)
325                                fatal("Received KEXGSS_INIT after initialising");
326                        recv_tok.value=packet_get_string(&recv_tok.length);
327
328                        dh_client_pub = BN_new();
329                       
330                        if (dh_client_pub == NULL)
331                                fatal("dh_client_pub == NULL");
332                        packet_get_bignum2(dh_client_pub);
333                       
334                        /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
335                        break;
336                case SSH2_MSG_KEXGSS_CONTINUE:
337                        if (dh_client_pub == NULL)
338                                fatal("Received KEXGSS_CONTINUE without initialising");
339                        recv_tok.value=packet_get_string(&recv_tok.length);
340                        break;
341                default:
342                        packet_disconnect("Protocol error: didn't expect packet type %d",
343                                           type);
344                }
345               
346                maj_status=PRIVSEP(ssh_gssapi_accept_ctx(ctxt,&recv_tok,
347                                                         &send_tok, &ret_flags));
348
349                gss_release_buffer(&min_status,&recv_tok);
350               
351                if (maj_status & GSS_S_CONTINUE_NEEDED) {
352                        debug("Sending GSSAPI_CONTINUE");
353                        packet_start(SSH2_MSG_KEXGSS_CONTINUE);
354                        packet_put_string(send_tok.value,send_tok.length);
355                        packet_send();
356                        packet_write_wait();
357                        gss_release_buffer(&min_status, &send_tok);
358                }
359        } while (maj_status & GSS_S_CONTINUE_NEEDED);
360
361        if (GSS_ERROR(maj_status))
362                fatal("gss_accept_context died");
363       
364        debug("gss_complete");
365        if (!(ret_flags & GSS_C_MUTUAL_FLAG))
366                fatal("mutual authentication flag wasn't set");
367               
368        if (!(ret_flags & GSS_C_INTEG_FLAG))
369                fatal("Integrity flag wasn't set");
370                       
371        dh = dh_new_group1();
372        dh_gen_key(dh, kex->we_need * 8);
373       
374        if (!dh_pub_is_valid(dh, dh_client_pub))
375                packet_disconnect("bad client public DH value");
376
377        klen = DH_size(dh);
378        kbuf = xmalloc(klen);
379        kout = DH_compute_key(kbuf, dh_client_pub, dh);
380
381        shared_secret = BN_new();
382        BN_bin2bn(kbuf, kout, shared_secret);
383        memset(kbuf, 0, klen);
384        xfree(kbuf);
385       
386        hash = kex_gssapi_hash(
387            kex->client_version_string,
388            kex->server_version_string,
389            buffer_ptr(&kex->peer), buffer_len(&kex->peer),
390            buffer_ptr(&kex->my), buffer_len(&kex->my),
391            NULL, 0, /* Change this if we start sending host keys */
392            dh_client_pub,
393            dh->pub_key,
394            shared_secret
395        );
396        BN_free(dh_client_pub);
397               
398        if (kex->session_id == NULL) {
399                kex->session_id_len = 20;
400                kex->session_id = xmalloc(kex->session_id_len);
401                memcpy(kex->session_id, hash, kex->session_id_len);
402        }
403                               
404        gssbuf.value = hash;
405        gssbuf.length = 20; /* Hashlen appears to always be 20 */
406       
407        if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok))))
408                fatal("Couldn't get MIC");
409       
410        packet_start(SSH2_MSG_KEXGSS_COMPLETE);
411        packet_put_bignum2(dh->pub_key);
412        packet_put_string((char *)msg_tok.value,msg_tok.length);
413
414        if (send_tok.length!=0) {
415                packet_put_char(1); /* true */
416                packet_put_string((char *)send_tok.value,send_tok.length);
417        } else {
418                packet_put_char(0); /* false */
419        }
420        packet_send();
421        packet_write_wait();
422
423        /* We used to store the client name and credentials here for later
424         * use. With privsep, its easier to do this as a by product of the
425         * call to accept_context, which stores delegated information when
426         * the context is complete */
427         
428        gss_release_buffer(&min_status, &send_tok);     
429
430        /* If we've got a context, delete it. It may be NULL if we've been
431         * using privsep */
432        ssh_gssapi_delete_ctx(&ctxt);
433       
434        DH_free(dh);
435
436        kex_derive_keys(kex, hash, shared_secret);
437        BN_clear_free(shared_secret);
438        kex_finish(kex);
439}
440
441void
442kexgss(Kex *kex)
443{
444        if (kex->server)
445                kexgss_server(kex);
446        else
447                kexgss_client(kex);
448}
449
450#endif /* GSSAPI */
Note: See TracBrowser for help on using the repository browser.