|
1 /* |
|
2 * Copyright (c) 2001-2009 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 <string.h> |
|
30 |
|
31 #include <openssl/crypto.h> |
|
32 #include <openssl/bn.h> |
|
33 |
|
34 #include "xmalloc.h" |
|
35 #include "buffer.h" |
|
36 #include "ssh2.h" |
|
37 #include "key.h" |
|
38 #include "cipher.h" |
|
39 #include "kex.h" |
|
40 #include "log.h" |
|
41 #include "packet.h" |
|
42 #include "dh.h" |
|
43 #include "ssh-gss.h" |
|
44 #include "monitor_wrap.h" |
|
45 |
|
46 void |
|
47 kexgss_server(Kex *kex) |
|
48 { |
|
49 OM_uint32 maj_status, min_status; |
|
50 |
|
51 /* |
|
52 * Some GSSAPI implementations use the input value of ret_flags (an |
|
53 * output variable) as a means of triggering mechanism specific |
|
54 * features. Initializing it to zero avoids inadvertently |
|
55 * activating this non-standard behaviour. |
|
56 */ |
|
57 |
|
58 OM_uint32 ret_flags = 0; |
|
59 gss_buffer_desc gssbuf, recv_tok, msg_tok; |
|
60 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; |
|
61 Gssctxt *ctxt = NULL; |
|
62 uint_t slen, klen, kout, hashlen; |
|
63 uchar_t *kbuf, *hash; |
|
64 DH *dh; |
|
65 int min = -1, max = -1, nbits = -1; |
|
66 BIGNUM *shared_secret = NULL; |
|
67 BIGNUM *dh_client_pub = NULL; |
|
68 int type = 0; |
|
69 gss_OID oid; |
|
70 char *mechs; |
|
71 |
|
72 /* Initialise GSSAPI */ |
|
73 |
|
74 /* |
|
75 * If we're rekeying, privsep means that some of the private structures |
|
76 * in the GSSAPI code are no longer available. This kludges them back |
|
77 * into life |
|
78 */ |
|
79 if (!ssh_gssapi_oid_table_ok()) |
|
80 if ((mechs = ssh_gssapi_server_mechanisms())) |
|
81 free(mechs); |
|
82 |
|
83 debug2("%s: Identifying %s", __func__, kex->name); |
|
84 oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); |
|
85 if (oid == GSS_C_NO_OID) |
|
86 fatal("Unknown gssapi mechanism"); |
|
87 |
|
88 debug2("%s: Acquiring credentials", __func__); |
|
89 |
|
90 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) |
|
91 fatal("Unable to acquire credentials for the server"); |
|
92 |
|
93 switch (kex->kex_type) { |
|
94 case KEX_GSS_GRP1_SHA1: |
|
95 dh = dh_new_group1(); |
|
96 break; |
|
97 case KEX_GSS_GRP14_SHA1: |
|
98 dh = dh_new_group14(); |
|
99 break; |
|
100 case KEX_GSS_GEX_SHA1: |
|
101 debug("Doing group exchange"); |
|
102 packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); |
|
103 min = packet_get_int(); |
|
104 nbits = packet_get_int(); |
|
105 max = packet_get_int(); |
|
106 min = MAX(DH_GRP_MIN, min); |
|
107 max = MIN(DH_GRP_MAX, max); |
|
108 packet_check_eom(); |
|
109 if (max < min || nbits < min || max < nbits) |
|
110 fatal("GSS_GEX, bad parameters: %d !< %d !< %d", |
|
111 min, nbits, max); |
|
112 dh = PRIVSEP(choose_dh(min, nbits, max)); |
|
113 if (dh == NULL) |
|
114 packet_disconnect("Protocol error:" |
|
115 " no matching group found"); |
|
116 |
|
117 packet_start(SSH2_MSG_KEXGSS_GROUP); |
|
118 packet_put_bignum2(dh->p); |
|
119 packet_put_bignum2(dh->g); |
|
120 packet_send(); |
|
121 |
|
122 packet_write_wait(); |
|
123 break; |
|
124 default: |
|
125 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); |
|
126 } |
|
127 |
|
128 dh_gen_key(dh, kex->we_need * 8); |
|
129 |
|
130 do { |
|
131 debug("Wait SSH2_MSG_GSSAPI_INIT"); |
|
132 type = packet_read(); |
|
133 switch (type) { |
|
134 case SSH2_MSG_KEXGSS_INIT: |
|
135 if (dh_client_pub != NULL) |
|
136 fatal("Received KEXGSS_INIT after" |
|
137 " initialising"); |
|
138 recv_tok.value = packet_get_string(&slen); |
|
139 recv_tok.length = slen; |
|
140 |
|
141 if ((dh_client_pub = BN_new()) == NULL) |
|
142 fatal("dh_client_pub == NULL"); |
|
143 |
|
144 packet_get_bignum2(dh_client_pub); |
|
145 |
|
146 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ |
|
147 break; |
|
148 case SSH2_MSG_KEXGSS_CONTINUE: |
|
149 recv_tok.value = packet_get_string(&slen); |
|
150 recv_tok.length = slen; |
|
151 break; |
|
152 default: |
|
153 packet_disconnect( |
|
154 "Protocol error: didn't expect packet type %d", |
|
155 type); |
|
156 } |
|
157 |
|
158 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, |
|
159 &send_tok, &ret_flags)); |
|
160 |
|
161 free(recv_tok.value); |
|
162 |
|
163 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) |
|
164 fatal("Zero length token output when incomplete"); |
|
165 |
|
166 if (dh_client_pub == NULL) |
|
167 fatal("No client public key"); |
|
168 |
|
169 if (maj_status & GSS_S_CONTINUE_NEEDED) { |
|
170 debug("Sending GSSAPI_CONTINUE"); |
|
171 packet_start(SSH2_MSG_KEXGSS_CONTINUE); |
|
172 packet_put_string(send_tok.value, send_tok.length); |
|
173 packet_send(); |
|
174 gss_release_buffer(&min_status, &send_tok); |
|
175 } |
|
176 } while (maj_status & GSS_S_CONTINUE_NEEDED); |
|
177 |
|
178 if (GSS_ERROR(maj_status)) { |
|
179 if (send_tok.length > 0) { |
|
180 packet_start(SSH2_MSG_KEXGSS_CONTINUE); |
|
181 packet_put_string(send_tok.value, send_tok.length); |
|
182 packet_send(); |
|
183 } |
|
184 fatal("accept_ctx died"); |
|
185 } |
|
186 |
|
187 if (!(ret_flags & GSS_C_MUTUAL_FLAG)) |
|
188 fatal("Mutual Authentication flag wasn't set"); |
|
189 |
|
190 if (!(ret_flags & GSS_C_INTEG_FLAG)) |
|
191 fatal("Integrity flag wasn't set"); |
|
192 |
|
193 if (!dh_pub_is_valid(dh, dh_client_pub)) |
|
194 packet_disconnect("bad client public DH value"); |
|
195 |
|
196 klen = DH_size(dh); |
|
197 kbuf = xmalloc(klen); |
|
198 kout = DH_compute_key(kbuf, dh_client_pub, dh); |
|
199 if (kout < 0) |
|
200 fatal("DH_compute_key: failed"); |
|
201 |
|
202 shared_secret = BN_new(); |
|
203 if (shared_secret == NULL) |
|
204 fatal("kexgss_server: BN_new failed"); |
|
205 |
|
206 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) |
|
207 fatal("kexgss_server: BN_bin2bn failed"); |
|
208 |
|
209 memset(kbuf, 0, klen); |
|
210 free(kbuf); |
|
211 |
|
212 switch (kex->kex_type) { |
|
213 case KEX_GSS_GRP1_SHA1: |
|
214 case KEX_GSS_GRP14_SHA1: |
|
215 kex_dh_hash( |
|
216 kex->client_version_string, kex->server_version_string, |
|
217 buffer_ptr(&kex->peer), buffer_len(&kex->peer), |
|
218 buffer_ptr(&kex->my), buffer_len(&kex->my), |
|
219 NULL, 0, /* Change this if we start sending host keys */ |
|
220 dh_client_pub, dh->pub_key, shared_secret, |
|
221 &hash, &hashlen); |
|
222 break; |
|
223 case KEX_GSS_GEX_SHA1: |
|
224 kexgex_hash( |
|
225 kex->hash_alg, |
|
226 kex->client_version_string, kex->server_version_string, |
|
227 buffer_ptr(&kex->peer), buffer_len(&kex->peer), |
|
228 buffer_ptr(&kex->my), buffer_len(&kex->my), |
|
229 NULL, 0, |
|
230 min, nbits, max, |
|
231 dh->p, dh->g, |
|
232 dh_client_pub, |
|
233 dh->pub_key, |
|
234 shared_secret, |
|
235 &hash, &hashlen); |
|
236 break; |
|
237 default: |
|
238 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); |
|
239 } |
|
240 |
|
241 BN_clear_free(dh_client_pub); |
|
242 |
|
243 if (kex->session_id == NULL) { |
|
244 kex->session_id_len = hashlen; |
|
245 kex->session_id = xmalloc(kex->session_id_len); |
|
246 memcpy(kex->session_id, hash, kex->session_id_len); |
|
247 } |
|
248 |
|
249 gssbuf.value = hash; |
|
250 gssbuf.length = hashlen; |
|
251 |
|
252 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok)))) |
|
253 fatal("Couldn't get MIC"); |
|
254 |
|
255 packet_start(SSH2_MSG_KEXGSS_COMPLETE); |
|
256 packet_put_bignum2(dh->pub_key); |
|
257 packet_put_string(msg_tok.value, msg_tok.length); |
|
258 |
|
259 if (send_tok.length != 0) { |
|
260 packet_put_char(1); /* true */ |
|
261 packet_put_string(send_tok.value, send_tok.length); |
|
262 } else { |
|
263 packet_put_char(0); /* false */ |
|
264 } |
|
265 packet_send(); |
|
266 |
|
267 gss_release_buffer(&min_status, &send_tok); |
|
268 gss_release_buffer(&min_status, &msg_tok); |
|
269 |
|
270 if (gss_kex_context == NULL) |
|
271 gss_kex_context = ctxt; |
|
272 else |
|
273 ssh_gssapi_delete_ctx(&ctxt); |
|
274 |
|
275 DH_free(dh); |
|
276 |
|
277 kex_derive_keys_bn(kex, hash, hashlen, shared_secret); |
|
278 BN_clear_free(shared_secret); |
|
279 kex_finish(kex); |
|
280 } |
|
281 #endif /* GSSAPI */ |