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

Revision 17193, 12.9 KB checked in by zacheiss, 23 years ago (diff)
GSSAPI support for v2 of the ssh protocol.
Line 
1/*
2 * Copyright (c) 2001 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
42/* This is now the same as the DH hash ... */
43
44u_char *
45kex_gssapi_hash(
46    char *client_version_string,
47    char *server_version_string,
48    char *ckexinit, int ckexinitlen,
49    char *skexinit, int skexinitlen,
50    u_char *serverhostkeyblob, int sbloblen,
51    BIGNUM *client_dh_pub,
52    BIGNUM *server_dh_pub,
53    BIGNUM *shared_secret)
54{
55        Buffer b;
56        static u_char digest[EVP_MAX_MD_SIZE];
57        EVP_MD *evp_md = EVP_sha1();
58        EVP_MD_CTX md;
59
60        buffer_init(&b);
61        buffer_put_string(&b, client_version_string, strlen(client_version_string));
62        buffer_put_string(&b, server_version_string, strlen(server_version_string));
63
64        /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
65        buffer_put_int(&b, ckexinitlen+1);
66        buffer_put_char(&b, SSH2_MSG_KEXINIT);
67        buffer_append(&b, ckexinit, ckexinitlen);
68        buffer_put_int(&b, skexinitlen+1);
69        buffer_put_char(&b, SSH2_MSG_KEXINIT);
70        buffer_append(&b, skexinit, skexinitlen);
71
72        buffer_put_string(&b, serverhostkeyblob, sbloblen);
73        buffer_put_bignum2(&b, client_dh_pub);
74        buffer_put_bignum2(&b, server_dh_pub);
75        buffer_put_bignum2(&b, shared_secret);
76
77#ifdef DEBUG_KEX
78        buffer_dump(&b);
79#endif
80        EVP_DigestInit(&md, evp_md);
81        EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
82        EVP_DigestFinal(&md, digest, NULL);
83
84        buffer_free(&b);
85
86#ifdef DEBUG_KEX
87        dump_digest("hash", digest, evp_md->md_size);
88#endif
89        return digest;
90}
91
92void
93kexgss_client(Kex *kex)
94{
95        gss_buffer_desc gssbuf,send_tok,recv_tok, msg_tok, *token_ptr;
96        Gssctxt ctxt;
97        OM_uint32 maj_status, min_status, ret_flags;
98        int plen,dlen;
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)) {
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(&plen);
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, &dlen);
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;
297        int plen;
298        int dlen=0;
299        unsigned int klen, kout;
300        unsigned char *kbuf;
301        unsigned char *hash;
302        DH *dh;
303        BIGNUM *shared_secret = 0;
304        BIGNUM *dh_client_pub = 0;
305        int type =0;
306       
307        /* Initialise GSSAPI */
308
309        ssh_gssapi_build_ctx(&ctxt);
310        if (ssh_gssapi_id_kex(&ctxt,kex->name))
311                fatal("Unknown gssapi mechanism");
312        if (ssh_gssapi_acquire_cred(&ctxt))
313                fatal("Unable to acquire credentials for the server");
314                                                                                                                               
315        /* Initialise some bignums */
316        dh_client_pub = BN_new();
317        if (dh_client_pub == NULL)
318                fatal("dh_client_pub == NULL");
319
320        do {
321                debug("Wait SSH2_MSG_GSSAPI_INIT");
322                type = packet_read(&plen);
323                switch(type) {
324                case SSH2_MSG_KEXGSS_INIT:
325                        if (dlen!=0)
326                                fatal("Received KEXGSS_INIT after initialising");
327                        recv_tok.value=packet_get_string(&recv_tok.length);
328                        packet_get_bignum2(dh_client_pub, &dlen);
329                        /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
330                        break;
331                case SSH2_MSG_KEXGSS_CONTINUE:
332                        if (dlen==0)
333                                fatal("Received KEXGSS_CONTINUE without initialising");
334                        recv_tok.value=packet_get_string(&recv_tok.length);
335                        break;
336                default:
337                        packet_disconnect("Protocol error: didn't expect packet type %d",
338                                           type);
339                }
340                maj_status=ssh_gssapi_accept_ctx(&ctxt,&recv_tok, &send_tok,
341                                                 &ret_flags);
342
343                gss_release_buffer(&min_status,&recv_tok);
344               
345                if (maj_status & GSS_S_CONTINUE_NEEDED) {
346                        debug("Sending GSSAPI_CONTINUE");
347                        packet_start(SSH2_MSG_KEXGSS_CONTINUE);
348                        packet_put_string(send_tok.value,send_tok.length);
349                        packet_send();
350                        packet_write_wait();
351                        gss_release_buffer(&min_status, &send_tok);
352                }
353        } while (maj_status & GSS_S_CONTINUE_NEEDED);
354
355        if (GSS_ERROR(maj_status))
356                fatal("gss_accept_context died");
357       
358        debug("gss_complete");
359        if (!(ret_flags & GSS_C_MUTUAL_FLAG))
360                fatal("mutual authentication flag wasn't set");
361               
362        if (!(ret_flags & GSS_C_INTEG_FLAG))
363                fatal("Integrity flag wasn't set");
364               
365       
366        dh = dh_new_group1();
367        dh_gen_key(dh, kex->we_need * 8);
368       
369        if (!dh_pub_is_valid(dh, dh_client_pub))
370                packet_disconnect("bad client public DH value");
371
372        klen = DH_size(dh);
373        kbuf = xmalloc(klen);
374        kout = DH_compute_key(kbuf, dh_client_pub, dh);
375
376        shared_secret = BN_new();
377        BN_bin2bn(kbuf, kout, shared_secret);
378        memset(kbuf, 0, klen);
379        xfree(kbuf);
380       
381        hash = kex_gssapi_hash(
382            kex->client_version_string,
383            kex->server_version_string,
384            buffer_ptr(&kex->peer), buffer_len(&kex->peer),
385            buffer_ptr(&kex->my), buffer_len(&kex->my),
386            NULL, 0, /* Change this if we start sending host keys */
387            dh_client_pub,
388            dh->pub_key,
389            shared_secret
390        );
391        BN_free(dh_client_pub);
392               
393        if (kex->session_id == NULL) {
394                kex->session_id_len = 20;
395                kex->session_id = xmalloc(kex->session_id_len);
396                memcpy(kex->session_id, hash, kex->session_id_len);
397        }
398                               
399        gssbuf.value = hash;
400        gssbuf.length = 20; /* Hashlen appears to always be 20 */
401       
402        if ((maj_status=gss_get_mic(&min_status,
403                               ctxt.context,
404                               GSS_C_QOP_DEFAULT,
405                               &gssbuf,
406                               &msg_tok))) {
407                ssh_gssapi_error(maj_status,min_status);
408                fatal("Couldn't get MIC");
409        }       
410                             
411        packet_start(SSH2_MSG_KEXGSS_COMPLETE);
412        packet_put_bignum2(dh->pub_key);
413        packet_put_string((char *)msg_tok.value,msg_tok.length);
414
415        if (send_tok.length!=0) {
416                packet_put_char(1); /* true */
417                packet_put_string((char *)send_tok.value,send_tok.length);
418        } else {
419                packet_put_char(0); /* false */
420        }
421        packet_send();
422        packet_write_wait();
423
424        /* Store the client name, and the delegated credentials for later
425         * use */
426        if (ssh_gssapi_getclient(&ctxt,&gssapi_client_type,
427                                       &gssapi_client_name,
428                                       &gssapi_client_creds)) {
429                fatal("Couldn't convert client name");
430        }
431       
432        gss_release_buffer(&min_status, &send_tok);     
433        ssh_gssapi_delete_ctx(&ctxt);
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.