diff options
author | Colin Watson <cjwatson@debian.org> | 2019-06-05 06:41:44 +0100 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2019-06-09 22:09:07 +0100 |
commit | 865a97e05b6aab1619e1c8eeb33ccb8f9a9e48d3 (patch) | |
tree | 7bb2128eb663180bacfabca88f26d26bf0733824 /kexgssc.c | |
parent | ba627ba172d6649919baedff5ba2789610da382a (diff) | |
parent | 7d50f9e5be88179325983a1f58c9d51bb58f025a (diff) |
New upstream release (8.0p1)
Diffstat (limited to 'kexgssc.c')
-rw-r--r-- | kexgssc.c | 613 |
1 files changed, 439 insertions, 174 deletions
@@ -24,7 +24,7 @@ | |||
24 | 24 | ||
25 | #include "includes.h" | 25 | #include "includes.h" |
26 | 26 | ||
27 | #ifdef GSSAPI | 27 | #if defined(GSSAPI) && defined(WITH_OPENSSL) |
28 | 28 | ||
29 | #include "includes.h" | 29 | #include "includes.h" |
30 | 30 | ||
@@ -43,113 +43,88 @@ | |||
43 | #include "packet.h" | 43 | #include "packet.h" |
44 | #include "dh.h" | 44 | #include "dh.h" |
45 | #include "digest.h" | 45 | #include "digest.h" |
46 | #include "ssherr.h" | ||
46 | 47 | ||
47 | #include "ssh-gss.h" | 48 | #include "ssh-gss.h" |
48 | 49 | ||
49 | int | 50 | int |
50 | kexgss_client(struct ssh *ssh) { | 51 | kexgss_client(struct ssh *ssh) |
51 | gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; | 52 | { |
52 | gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; | 53 | struct kex *kex = ssh->kex; |
54 | gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER, | ||
55 | recv_tok = GSS_C_EMPTY_BUFFER, | ||
56 | gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr; | ||
53 | Gssctxt *ctxt; | 57 | Gssctxt *ctxt; |
54 | OM_uint32 maj_status, min_status, ret_flags; | 58 | OM_uint32 maj_status, min_status, ret_flags; |
55 | u_int klen, kout, slen = 0, strlen; | 59 | struct sshbuf *server_blob = NULL; |
56 | DH *dh; | 60 | struct sshbuf *shared_secret = NULL; |
57 | BIGNUM *dh_server_pub = NULL; | 61 | struct sshbuf *server_host_key_blob = NULL; |
58 | BIGNUM *shared_secret = NULL; | 62 | struct sshbuf *empty = NULL; |
59 | const BIGNUM *pub_key, *dh_p, *dh_g; | 63 | u_char *msg; |
60 | BIGNUM *p = NULL; | ||
61 | BIGNUM *g = NULL; | ||
62 | u_char *kbuf; | ||
63 | u_char *serverhostkey = NULL; | ||
64 | u_char *empty = ""; | ||
65 | char *msg; | ||
66 | int type = 0; | 64 | int type = 0; |
67 | int first = 1; | 65 | int first = 1; |
68 | int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; | ||
69 | u_char hash[SSH_DIGEST_MAX_LENGTH]; | 66 | u_char hash[SSH_DIGEST_MAX_LENGTH]; |
70 | size_t hashlen; | 67 | size_t hashlen; |
68 | u_char c; | ||
69 | int r; | ||
71 | 70 | ||
72 | /* Initialise our GSSAPI world */ | 71 | /* Initialise our GSSAPI world */ |
73 | ssh_gssapi_build_ctx(&ctxt); | 72 | ssh_gssapi_build_ctx(&ctxt); |
74 | if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type) | 73 | if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) |
75 | == GSS_C_NO_OID) | 74 | == GSS_C_NO_OID) |
76 | fatal("Couldn't identify host exchange"); | 75 | fatal("Couldn't identify host exchange"); |
77 | 76 | ||
78 | if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host)) | 77 | if (ssh_gssapi_import_name(ctxt, kex->gss_host)) |
79 | fatal("Couldn't import hostname"); | 78 | fatal("Couldn't import hostname"); |
80 | 79 | ||
81 | if (ssh->kex->gss_client && | 80 | if (kex->gss_client && |
82 | ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client)) | 81 | ssh_gssapi_client_identity(ctxt, kex->gss_client)) |
83 | fatal("Couldn't acquire client credentials"); | 82 | fatal("Couldn't acquire client credentials"); |
84 | 83 | ||
85 | switch (ssh->kex->kex_type) { | 84 | /* Step 1 */ |
85 | switch (kex->kex_type) { | ||
86 | case KEX_GSS_GRP1_SHA1: | 86 | case KEX_GSS_GRP1_SHA1: |
87 | dh = dh_new_group1(); | ||
88 | break; | ||
89 | case KEX_GSS_GRP14_SHA1: | 87 | case KEX_GSS_GRP14_SHA1: |
90 | dh = dh_new_group14(); | 88 | case KEX_GSS_GRP14_SHA256: |
89 | case KEX_GSS_GRP16_SHA512: | ||
90 | r = kex_dh_keypair(kex); | ||
91 | break; | 91 | break; |
92 | case KEX_GSS_GEX_SHA1: | 92 | case KEX_GSS_NISTP256_SHA256: |
93 | debug("Doing group exchange\n"); | 93 | r = kex_ecdh_keypair(kex); |
94 | nbits = dh_estimate(ssh->kex->we_need * 8); | 94 | break; |
95 | packet_start(SSH2_MSG_KEXGSS_GROUPREQ); | 95 | case KEX_GSS_C25519_SHA256: |
96 | packet_put_int(min); | 96 | r = kex_c25519_keypair(kex); |
97 | packet_put_int(nbits); | ||
98 | packet_put_int(max); | ||
99 | |||
100 | packet_send(); | ||
101 | |||
102 | packet_read_expect(SSH2_MSG_KEXGSS_GROUP); | ||
103 | |||
104 | if ((p = BN_new()) == NULL) | ||
105 | fatal("BN_new() failed"); | ||
106 | packet_get_bignum2(p); | ||
107 | if ((g = BN_new()) == NULL) | ||
108 | fatal("BN_new() failed"); | ||
109 | packet_get_bignum2(g); | ||
110 | packet_check_eom(); | ||
111 | |||
112 | if (BN_num_bits(p) < min || BN_num_bits(p) > max) | ||
113 | fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", | ||
114 | min, BN_num_bits(p), max); | ||
115 | |||
116 | dh = dh_new_group(g, p); | ||
117 | break; | 97 | break; |
118 | default: | 98 | default: |
119 | fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); | 99 | fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); |
120 | } | 100 | } |
121 | 101 | if (r != 0) | |
122 | /* Step 1 - e is dh->pub_key */ | 102 | return r; |
123 | dh_gen_key(dh, ssh->kex->we_need * 8); | ||
124 | DH_get0_key(dh, &pub_key, NULL); | ||
125 | DH_get0_pqg(dh, &dh_p, NULL, &dh_g); | ||
126 | |||
127 | /* This is f, we initialise it now to make life easier */ | ||
128 | dh_server_pub = BN_new(); | ||
129 | if (dh_server_pub == NULL) | ||
130 | fatal("dh_server_pub == NULL"); | ||
131 | 103 | ||
132 | token_ptr = GSS_C_NO_BUFFER; | 104 | token_ptr = GSS_C_NO_BUFFER; |
133 | 105 | ||
134 | do { | 106 | do { |
135 | debug("Calling gss_init_sec_context"); | 107 | debug("Calling gss_init_sec_context"); |
136 | 108 | ||
137 | maj_status = ssh_gssapi_init_ctx(ctxt, | 109 | maj_status = ssh_gssapi_init_ctx(ctxt, |
138 | ssh->kex->gss_deleg_creds, token_ptr, &send_tok, | 110 | kex->gss_deleg_creds, token_ptr, &send_tok, |
139 | &ret_flags); | 111 | &ret_flags); |
140 | 112 | ||
141 | if (GSS_ERROR(maj_status)) { | 113 | if (GSS_ERROR(maj_status)) { |
114 | /* XXX Useles code: Missing send? */ | ||
142 | if (send_tok.length != 0) { | 115 | if (send_tok.length != 0) { |
143 | packet_start(SSH2_MSG_KEXGSS_CONTINUE); | 116 | if ((r = sshpkt_start(ssh, |
144 | packet_put_string(send_tok.value, | 117 | SSH2_MSG_KEXGSS_CONTINUE)) != 0 || |
145 | send_tok.length); | 118 | (r = sshpkt_put_string(ssh, send_tok.value, |
119 | send_tok.length)) != 0) | ||
120 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
146 | } | 121 | } |
147 | fatal("gss_init_context failed"); | 122 | fatal("gss_init_context failed"); |
148 | } | 123 | } |
149 | 124 | ||
150 | /* If we've got an old receive buffer get rid of it */ | 125 | /* If we've got an old receive buffer get rid of it */ |
151 | if (token_ptr != GSS_C_NO_BUFFER) | 126 | if (token_ptr != GSS_C_NO_BUFFER) |
152 | free(recv_tok.value); | 127 | gss_release_buffer(&min_status, &recv_tok); |
153 | 128 | ||
154 | if (maj_status == GSS_S_COMPLETE) { | 129 | if (maj_status == GSS_S_COMPLETE) { |
155 | /* If mutual state flag is not true, kex fails */ | 130 | /* If mutual state flag is not true, kex fails */ |
@@ -161,75 +136,90 @@ kexgss_client(struct ssh *ssh) { | |||
161 | fatal("Integrity check failed"); | 136 | fatal("Integrity check failed"); |
162 | } | 137 | } |
163 | 138 | ||
164 | /* | 139 | /* |
165 | * If we have data to send, then the last message that we | 140 | * If we have data to send, then the last message that we |
166 | * received cannot have been a 'complete'. | 141 | * received cannot have been a 'complete'. |
167 | */ | 142 | */ |
168 | if (send_tok.length != 0) { | 143 | if (send_tok.length != 0) { |
169 | if (first) { | 144 | if (first) { |
170 | packet_start(SSH2_MSG_KEXGSS_INIT); | 145 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 || |
171 | packet_put_string(send_tok.value, | 146 | (r = sshpkt_put_string(ssh, send_tok.value, |
172 | send_tok.length); | 147 | send_tok.length)) != 0 || |
173 | packet_put_bignum2(pub_key); | 148 | (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0) |
149 | fatal("failed to construct packet: %s", ssh_err(r)); | ||
174 | first = 0; | 150 | first = 0; |
175 | } else { | 151 | } else { |
176 | packet_start(SSH2_MSG_KEXGSS_CONTINUE); | 152 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || |
177 | packet_put_string(send_tok.value, | 153 | (r = sshpkt_put_string(ssh, send_tok.value, |
178 | send_tok.length); | 154 | send_tok.length)) != 0) |
155 | fatal("failed to construct packet: %s", ssh_err(r)); | ||
179 | } | 156 | } |
180 | packet_send(); | 157 | if ((r = sshpkt_send(ssh)) != 0) |
158 | fatal("failed to send packet: %s", ssh_err(r)); | ||
181 | gss_release_buffer(&min_status, &send_tok); | 159 | gss_release_buffer(&min_status, &send_tok); |
182 | 160 | ||
183 | /* If we've sent them data, they should reply */ | 161 | /* If we've sent them data, they should reply */ |
184 | do { | 162 | do { |
185 | type = packet_read(); | 163 | type = ssh_packet_read(ssh); |
186 | if (type == SSH2_MSG_KEXGSS_HOSTKEY) { | 164 | if (type == SSH2_MSG_KEXGSS_HOSTKEY) { |
187 | debug("Received KEXGSS_HOSTKEY"); | 165 | debug("Received KEXGSS_HOSTKEY"); |
188 | if (serverhostkey) | 166 | if (server_host_key_blob) |
189 | fatal("Server host key received more than once"); | 167 | fatal("Server host key received more than once"); |
190 | serverhostkey = | 168 | if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0) |
191 | packet_get_string(&slen); | 169 | fatal("Failed to read server host key: %s", ssh_err(r)); |
192 | } | 170 | } |
193 | } while (type == SSH2_MSG_KEXGSS_HOSTKEY); | 171 | } while (type == SSH2_MSG_KEXGSS_HOSTKEY); |
194 | 172 | ||
195 | switch (type) { | 173 | switch (type) { |
196 | case SSH2_MSG_KEXGSS_CONTINUE: | 174 | case SSH2_MSG_KEXGSS_CONTINUE: |
197 | debug("Received GSSAPI_CONTINUE"); | 175 | debug("Received GSSAPI_CONTINUE"); |
198 | if (maj_status == GSS_S_COMPLETE) | 176 | if (maj_status == GSS_S_COMPLETE) |
199 | fatal("GSSAPI Continue received from server when complete"); | 177 | fatal("GSSAPI Continue received from server when complete"); |
200 | recv_tok.value = packet_get_string(&strlen); | 178 | if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, |
201 | recv_tok.length = strlen; | 179 | &recv_tok)) != 0 || |
180 | (r = sshpkt_get_end(ssh)) != 0) | ||
181 | fatal("Failed to read token: %s", ssh_err(r)); | ||
202 | break; | 182 | break; |
203 | case SSH2_MSG_KEXGSS_COMPLETE: | 183 | case SSH2_MSG_KEXGSS_COMPLETE: |
204 | debug("Received GSSAPI_COMPLETE"); | 184 | debug("Received GSSAPI_COMPLETE"); |
205 | packet_get_bignum2(dh_server_pub); | 185 | if (msg_tok.value != NULL) |
206 | msg_tok.value = packet_get_string(&strlen); | 186 | fatal("Received GSSAPI_COMPLETE twice?"); |
207 | msg_tok.length = strlen; | 187 | if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 || |
188 | (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, | ||
189 | &msg_tok)) != 0) | ||
190 | fatal("Failed to read message: %s", ssh_err(r)); | ||
208 | 191 | ||
209 | /* Is there a token included? */ | 192 | /* Is there a token included? */ |
210 | if (packet_get_char()) { | 193 | if ((r = sshpkt_get_u8(ssh, &c)) != 0) |
211 | recv_tok.value= | 194 | fatal("sshpkt failed: %s", ssh_err(r)); |
212 | packet_get_string(&strlen); | 195 | if (c) { |
213 | recv_tok.length = strlen; | 196 | if ((r = ssh_gssapi_sshpkt_get_buffer_desc( |
197 | ssh, &recv_tok)) != 0) | ||
198 | fatal("Failed to read token: %s", ssh_err(r)); | ||
214 | /* If we're already complete - protocol error */ | 199 | /* If we're already complete - protocol error */ |
215 | if (maj_status == GSS_S_COMPLETE) | 200 | if (maj_status == GSS_S_COMPLETE) |
216 | packet_disconnect("Protocol error: received token when complete"); | 201 | sshpkt_disconnect(ssh, "Protocol error: received token when complete"); |
217 | } else { | 202 | } else { |
218 | /* No token included */ | 203 | /* No token included */ |
219 | if (maj_status != GSS_S_COMPLETE) | 204 | if (maj_status != GSS_S_COMPLETE) |
220 | packet_disconnect("Protocol error: did not receive final token"); | 205 | sshpkt_disconnect(ssh, "Protocol error: did not receive final token"); |
206 | } | ||
207 | if ((r = sshpkt_get_end(ssh)) != 0) { | ||
208 | fatal("Expecting end of packet."); | ||
221 | } | 209 | } |
222 | break; | 210 | break; |
223 | case SSH2_MSG_KEXGSS_ERROR: | 211 | case SSH2_MSG_KEXGSS_ERROR: |
224 | debug("Received Error"); | 212 | debug("Received Error"); |
225 | maj_status = packet_get_int(); | 213 | if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 || |
226 | min_status = packet_get_int(); | 214 | (r = sshpkt_get_u32(ssh, &min_status)) != 0 || |
227 | msg = packet_get_string(NULL); | 215 | (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || |
228 | (void) packet_get_string_ptr(NULL); | 216 | (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */ |
229 | fatal("GSSAPI Error: \n%.400s",msg); | 217 | (r = sshpkt_get_end(ssh)) != 0) |
218 | fatal("sshpkt_get failed: %s", ssh_err(r)); | ||
219 | fatal("GSSAPI Error: \n%.400s", msg); | ||
230 | default: | 220 | default: |
231 | packet_disconnect("Protocol error: didn't expect packet type %d", | 221 | sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d", |
232 | type); | 222 | type); |
233 | } | 223 | } |
234 | token_ptr = &recv_tok; | 224 | token_ptr = &recv_tok; |
235 | } else { | 225 | } else { |
@@ -239,93 +229,358 @@ kexgss_client(struct ssh *ssh) { | |||
239 | } | 229 | } |
240 | } while (maj_status & GSS_S_CONTINUE_NEEDED); | 230 | } while (maj_status & GSS_S_CONTINUE_NEEDED); |
241 | 231 | ||
242 | /* | 232 | /* |
243 | * We _must_ have received a COMPLETE message in reply from the | 233 | * We _must_ have received a COMPLETE message in reply from the |
244 | * server, which will have set dh_server_pub and msg_tok | 234 | * server, which will have set server_blob and msg_tok |
245 | */ | 235 | */ |
246 | 236 | ||
247 | if (type != SSH2_MSG_KEXGSS_COMPLETE) | 237 | if (type != SSH2_MSG_KEXGSS_COMPLETE) |
248 | fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); | 238 | fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); |
249 | 239 | ||
250 | /* Check f in range [1, p-1] */ | 240 | /* compute shared secret */ |
251 | if (!dh_pub_is_valid(dh, dh_server_pub)) | 241 | switch (kex->kex_type) { |
252 | packet_disconnect("bad server public DH value"); | ||
253 | |||
254 | /* compute K=f^x mod p */ | ||
255 | klen = DH_size(dh); | ||
256 | kbuf = xmalloc(klen); | ||
257 | kout = DH_compute_key(kbuf, dh_server_pub, dh); | ||
258 | if (kout < 0) | ||
259 | fatal("DH_compute_key: failed"); | ||
260 | |||
261 | shared_secret = BN_new(); | ||
262 | if (shared_secret == NULL) | ||
263 | fatal("kexgss_client: BN_new failed"); | ||
264 | |||
265 | if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) | ||
266 | fatal("kexdh_client: BN_bin2bn failed"); | ||
267 | |||
268 | memset(kbuf, 0, klen); | ||
269 | free(kbuf); | ||
270 | |||
271 | hashlen = sizeof(hash); | ||
272 | switch (ssh->kex->kex_type) { | ||
273 | case KEX_GSS_GRP1_SHA1: | 242 | case KEX_GSS_GRP1_SHA1: |
274 | case KEX_GSS_GRP14_SHA1: | 243 | case KEX_GSS_GRP14_SHA1: |
275 | kex_dh_hash( | 244 | case KEX_GSS_GRP14_SHA256: |
276 | ssh->kex->hash_alg, | 245 | case KEX_GSS_GRP16_SHA512: |
277 | ssh->kex->client_version_string, | 246 | r = kex_dh_dec(kex, server_blob, &shared_secret); |
278 | ssh->kex->server_version_string, | 247 | break; |
279 | sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), | 248 | case KEX_GSS_C25519_SHA256: |
280 | sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), | 249 | if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80) |
281 | (serverhostkey ? serverhostkey : empty), slen, | 250 | fatal("The received key has MSB of last octet set!"); |
282 | pub_key, /* e */ | 251 | r = kex_c25519_dec(kex, server_blob, &shared_secret); |
283 | dh_server_pub, /* f */ | ||
284 | shared_secret, /* K */ | ||
285 | hash, &hashlen | ||
286 | ); | ||
287 | break; | 252 | break; |
288 | case KEX_GSS_GEX_SHA1: | 253 | case KEX_GSS_NISTP256_SHA256: |
289 | kexgex_hash( | 254 | if (sshbuf_len(server_blob) != 65) |
290 | ssh->kex->hash_alg, | 255 | fatal("The received NIST-P256 key did not match" |
291 | ssh->kex->client_version_string, | 256 | "expected length (expected 65, got %zu)", sshbuf_len(server_blob)); |
292 | ssh->kex->server_version_string, | 257 | |
293 | sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), | 258 | if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED) |
294 | sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), | 259 | fatal("The received NIST-P256 key does not have first octet 0x04"); |
295 | (serverhostkey ? serverhostkey : empty), slen, | 260 | |
296 | min, nbits, max, | 261 | r = kex_ecdh_dec(kex, server_blob, &shared_secret); |
297 | dh_p, dh_g, | ||
298 | pub_key, | ||
299 | dh_server_pub, | ||
300 | shared_secret, | ||
301 | hash, &hashlen | ||
302 | ); | ||
303 | break; | 262 | break; |
304 | default: | 263 | default: |
305 | fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); | 264 | r = SSH_ERR_INVALID_ARGUMENT; |
265 | break; | ||
266 | } | ||
267 | if (r != 0) | ||
268 | goto out; | ||
269 | |||
270 | if ((empty = sshbuf_new()) == NULL) { | ||
271 | r = SSH_ERR_ALLOC_FAIL; | ||
272 | goto out; | ||
306 | } | 273 | } |
307 | 274 | ||
275 | hashlen = sizeof(hash); | ||
276 | if ((r = kex_gen_hash( | ||
277 | kex->hash_alg, | ||
278 | kex->client_version, | ||
279 | kex->server_version, | ||
280 | kex->my, | ||
281 | kex->peer, | ||
282 | (server_host_key_blob ? server_host_key_blob : empty), | ||
283 | kex->client_pub, | ||
284 | server_blob, | ||
285 | shared_secret, | ||
286 | hash, &hashlen)) != 0) | ||
287 | fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); | ||
288 | |||
308 | gssbuf.value = hash; | 289 | gssbuf.value = hash; |
309 | gssbuf.length = hashlen; | 290 | gssbuf.length = hashlen; |
310 | 291 | ||
311 | /* Verify that the hash matches the MIC we just got. */ | 292 | /* Verify that the hash matches the MIC we just got. */ |
312 | if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) | 293 | if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) |
313 | packet_disconnect("Hash's MIC didn't verify"); | 294 | sshpkt_disconnect(ssh, "Hash's MIC didn't verify"); |
314 | 295 | ||
315 | free(msg_tok.value); | 296 | gss_release_buffer(&min_status, &msg_tok); |
316 | 297 | ||
317 | DH_free(dh); | 298 | if (kex->gss_deleg_creds) |
318 | free(serverhostkey); | 299 | ssh_gssapi_credentials_updated(ctxt); |
319 | BN_clear_free(dh_server_pub); | 300 | |
301 | if (gss_kex_context == NULL) | ||
302 | gss_kex_context = ctxt; | ||
303 | else | ||
304 | ssh_gssapi_delete_ctx(&ctxt); | ||
305 | |||
306 | if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) | ||
307 | r = kex_send_newkeys(ssh); | ||
308 | |||
309 | out: | ||
310 | explicit_bzero(hash, sizeof(hash)); | ||
311 | explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key)); | ||
312 | sshbuf_free(empty); | ||
313 | sshbuf_free(server_host_key_blob); | ||
314 | sshbuf_free(server_blob); | ||
315 | sshbuf_free(shared_secret); | ||
316 | sshbuf_free(kex->client_pub); | ||
317 | kex->client_pub = NULL; | ||
318 | return r; | ||
319 | } | ||
320 | |||
321 | int | ||
322 | kexgssgex_client(struct ssh *ssh) | ||
323 | { | ||
324 | struct kex *kex = ssh->kex; | ||
325 | gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER, | ||
326 | recv_tok = GSS_C_EMPTY_BUFFER, gssbuf, | ||
327 | msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr; | ||
328 | Gssctxt *ctxt; | ||
329 | OM_uint32 maj_status, min_status, ret_flags; | ||
330 | struct sshbuf *shared_secret = NULL; | ||
331 | BIGNUM *p = NULL; | ||
332 | BIGNUM *g = NULL; | ||
333 | struct sshbuf *buf = NULL; | ||
334 | struct sshbuf *server_host_key_blob = NULL; | ||
335 | struct sshbuf *server_blob = NULL; | ||
336 | BIGNUM *dh_server_pub = NULL; | ||
337 | u_char *msg; | ||
338 | int type = 0; | ||
339 | int first = 1; | ||
340 | u_char hash[SSH_DIGEST_MAX_LENGTH]; | ||
341 | size_t hashlen; | ||
342 | const BIGNUM *pub_key, *dh_p, *dh_g; | ||
343 | int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; | ||
344 | struct sshbuf *empty = NULL; | ||
345 | u_char c; | ||
346 | int r; | ||
347 | |||
348 | /* Initialise our GSSAPI world */ | ||
349 | ssh_gssapi_build_ctx(&ctxt); | ||
350 | if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) | ||
351 | == GSS_C_NO_OID) | ||
352 | fatal("Couldn't identify host exchange"); | ||
353 | |||
354 | if (ssh_gssapi_import_name(ctxt, kex->gss_host)) | ||
355 | fatal("Couldn't import hostname"); | ||
356 | |||
357 | if (kex->gss_client && | ||
358 | ssh_gssapi_client_identity(ctxt, kex->gss_client)) | ||
359 | fatal("Couldn't acquire client credentials"); | ||
360 | |||
361 | debug("Doing group exchange"); | ||
362 | nbits = dh_estimate(kex->dh_need * 8); | ||
363 | |||
364 | kex->min = DH_GRP_MIN; | ||
365 | kex->max = DH_GRP_MAX; | ||
366 | kex->nbits = nbits; | ||
367 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 || | ||
368 | (r = sshpkt_put_u32(ssh, min)) != 0 || | ||
369 | (r = sshpkt_put_u32(ssh, nbits)) != 0 || | ||
370 | (r = sshpkt_put_u32(ssh, max)) != 0 || | ||
371 | (r = sshpkt_send(ssh)) != 0) | ||
372 | fatal("Failed to construct a packet: %s", ssh_err(r)); | ||
373 | |||
374 | if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0) | ||
375 | fatal("Error: %s", ssh_err(r)); | ||
376 | |||
377 | if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 || | ||
378 | (r = sshpkt_get_bignum2(ssh, &g)) != 0 || | ||
379 | (r = sshpkt_get_end(ssh)) != 0) | ||
380 | fatal("shpkt_get_bignum2 failed: %s", ssh_err(r)); | ||
381 | |||
382 | if (BN_num_bits(p) < min || BN_num_bits(p) > max) | ||
383 | fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", | ||
384 | min, BN_num_bits(p), max); | ||
385 | |||
386 | if ((kex->dh = dh_new_group(g, p)) == NULL) | ||
387 | fatal("dn_new_group() failed"); | ||
388 | p = g = NULL; /* belong to kex->dh now */ | ||
389 | |||
390 | if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) | ||
391 | goto out; | ||
392 | DH_get0_key(kex->dh, &pub_key, NULL); | ||
393 | |||
394 | token_ptr = GSS_C_NO_BUFFER; | ||
395 | |||
396 | do { | ||
397 | /* Step 2 - call GSS_Init_sec_context() */ | ||
398 | debug("Calling gss_init_sec_context"); | ||
399 | |||
400 | maj_status = ssh_gssapi_init_ctx(ctxt, | ||
401 | kex->gss_deleg_creds, token_ptr, &send_tok, | ||
402 | &ret_flags); | ||
403 | |||
404 | if (GSS_ERROR(maj_status)) { | ||
405 | /* XXX Useles code: Missing send? */ | ||
406 | if (send_tok.length != 0) { | ||
407 | if ((r = sshpkt_start(ssh, | ||
408 | SSH2_MSG_KEXGSS_CONTINUE)) != 0 || | ||
409 | (r = sshpkt_put_string(ssh, send_tok.value, | ||
410 | send_tok.length)) != 0) | ||
411 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
412 | } | ||
413 | fatal("gss_init_context failed"); | ||
414 | } | ||
415 | |||
416 | /* If we've got an old receive buffer get rid of it */ | ||
417 | if (token_ptr != GSS_C_NO_BUFFER) | ||
418 | gss_release_buffer(&min_status, &recv_tok); | ||
419 | |||
420 | if (maj_status == GSS_S_COMPLETE) { | ||
421 | /* If mutual state flag is not true, kex fails */ | ||
422 | if (!(ret_flags & GSS_C_MUTUAL_FLAG)) | ||
423 | fatal("Mutual authentication failed"); | ||
424 | |||
425 | /* If integ avail flag is not true kex fails */ | ||
426 | if (!(ret_flags & GSS_C_INTEG_FLAG)) | ||
427 | fatal("Integrity check failed"); | ||
428 | } | ||
429 | |||
430 | /* | ||
431 | * If we have data to send, then the last message that we | ||
432 | * received cannot have been a 'complete'. | ||
433 | */ | ||
434 | if (send_tok.length != 0) { | ||
435 | if (first) { | ||
436 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 || | ||
437 | (r = sshpkt_put_string(ssh, send_tok.value, | ||
438 | send_tok.length)) != 0 || | ||
439 | (r = sshpkt_put_bignum2(ssh, pub_key)) != 0) | ||
440 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
441 | first = 0; | ||
442 | } else { | ||
443 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || | ||
444 | (r = sshpkt_put_string(ssh,send_tok.value, | ||
445 | send_tok.length)) != 0) | ||
446 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
447 | } | ||
448 | if ((r = sshpkt_send(ssh)) != 0) | ||
449 | fatal("sshpkt_send failed: %s", ssh_err(r)); | ||
450 | gss_release_buffer(&min_status, &send_tok); | ||
451 | |||
452 | /* If we've sent them data, they should reply */ | ||
453 | do { | ||
454 | type = ssh_packet_read(ssh); | ||
455 | if (type == SSH2_MSG_KEXGSS_HOSTKEY) { | ||
456 | debug("Received KEXGSS_HOSTKEY"); | ||
457 | if (server_host_key_blob) | ||
458 | fatal("Server host key received more than once"); | ||
459 | if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0) | ||
460 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
461 | } | ||
462 | } while (type == SSH2_MSG_KEXGSS_HOSTKEY); | ||
463 | |||
464 | switch (type) { | ||
465 | case SSH2_MSG_KEXGSS_CONTINUE: | ||
466 | debug("Received GSSAPI_CONTINUE"); | ||
467 | if (maj_status == GSS_S_COMPLETE) | ||
468 | fatal("GSSAPI Continue received from server when complete"); | ||
469 | if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, | ||
470 | &recv_tok)) != 0 || | ||
471 | (r = sshpkt_get_end(ssh)) != 0) | ||
472 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
473 | break; | ||
474 | case SSH2_MSG_KEXGSS_COMPLETE: | ||
475 | debug("Received GSSAPI_COMPLETE"); | ||
476 | if (msg_tok.value != NULL) | ||
477 | fatal("Received GSSAPI_COMPLETE twice?"); | ||
478 | if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 || | ||
479 | (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, | ||
480 | &msg_tok)) != 0) | ||
481 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
482 | |||
483 | /* Is there a token included? */ | ||
484 | if ((r = sshpkt_get_u8(ssh, &c)) != 0) | ||
485 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
486 | if (c) { | ||
487 | if ((r = ssh_gssapi_sshpkt_get_buffer_desc( | ||
488 | ssh, &recv_tok)) != 0 || | ||
489 | (r = sshpkt_get_end(ssh)) != 0) | ||
490 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
491 | /* If we're already complete - protocol error */ | ||
492 | if (maj_status == GSS_S_COMPLETE) | ||
493 | sshpkt_disconnect(ssh, "Protocol error: received token when complete"); | ||
494 | } else { | ||
495 | /* No token included */ | ||
496 | if (maj_status != GSS_S_COMPLETE) | ||
497 | sshpkt_disconnect(ssh, "Protocol error: did not receive final token"); | ||
498 | } | ||
499 | break; | ||
500 | case SSH2_MSG_KEXGSS_ERROR: | ||
501 | debug("Received Error"); | ||
502 | if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 || | ||
503 | (r = sshpkt_get_u32(ssh, &min_status)) != 0 || | ||
504 | (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || | ||
505 | (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */ | ||
506 | (r = sshpkt_get_end(ssh)) != 0) | ||
507 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
508 | fatal("GSSAPI Error: \n%.400s", msg); | ||
509 | default: | ||
510 | sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d", | ||
511 | type); | ||
512 | } | ||
513 | token_ptr = &recv_tok; | ||
514 | } else { | ||
515 | /* No data, and not complete */ | ||
516 | if (maj_status != GSS_S_COMPLETE) | ||
517 | fatal("Not complete, and no token output"); | ||
518 | } | ||
519 | } while (maj_status & GSS_S_CONTINUE_NEEDED); | ||
520 | |||
521 | /* | ||
522 | * We _must_ have received a COMPLETE message in reply from the | ||
523 | * server, which will have set dh_server_pub and msg_tok | ||
524 | */ | ||
525 | |||
526 | if (type != SSH2_MSG_KEXGSS_COMPLETE) | ||
527 | fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); | ||
528 | |||
529 | /* 7. C verifies that the key Q_S is valid */ | ||
530 | /* 8. C computes shared secret */ | ||
531 | if ((buf = sshbuf_new()) == NULL || | ||
532 | (r = sshbuf_put_stringb(buf, server_blob)) != 0 || | ||
533 | (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0) | ||
534 | goto out; | ||
535 | sshbuf_free(buf); | ||
536 | buf = NULL; | ||
537 | |||
538 | if ((shared_secret = sshbuf_new()) == NULL) { | ||
539 | r = SSH_ERR_ALLOC_FAIL; | ||
540 | goto out; | ||
541 | } | ||
542 | |||
543 | if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0) | ||
544 | goto out; | ||
545 | if ((empty = sshbuf_new()) == NULL) { | ||
546 | r = SSH_ERR_ALLOC_FAIL; | ||
547 | goto out; | ||
548 | } | ||
549 | |||
550 | DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g); | ||
551 | hashlen = sizeof(hash); | ||
552 | if ((r = kexgex_hash( | ||
553 | kex->hash_alg, | ||
554 | kex->client_version, | ||
555 | kex->server_version, | ||
556 | kex->my, | ||
557 | kex->peer, | ||
558 | (server_host_key_blob ? server_host_key_blob : empty), | ||
559 | kex->min, kex->nbits, kex->max, | ||
560 | dh_p, dh_g, | ||
561 | pub_key, | ||
562 | dh_server_pub, | ||
563 | sshbuf_ptr(shared_secret), sshbuf_len(shared_secret), | ||
564 | hash, &hashlen)) != 0) | ||
565 | fatal("Failed to calculate hash: %s", ssh_err(r)); | ||
566 | |||
567 | gssbuf.value = hash; | ||
568 | gssbuf.length = hashlen; | ||
569 | |||
570 | /* Verify that the hash matches the MIC we just got. */ | ||
571 | if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) | ||
572 | sshpkt_disconnect(ssh, "Hash's MIC didn't verify"); | ||
573 | |||
574 | gss_release_buffer(&min_status, &msg_tok); | ||
320 | 575 | ||
321 | /* save session id */ | 576 | /* save session id */ |
322 | if (ssh->kex->session_id == NULL) { | 577 | if (kex->session_id == NULL) { |
323 | ssh->kex->session_id_len = hashlen; | 578 | kex->session_id_len = hashlen; |
324 | ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); | 579 | kex->session_id = xmalloc(kex->session_id_len); |
325 | memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); | 580 | memcpy(kex->session_id, hash, kex->session_id_len); |
326 | } | 581 | } |
327 | 582 | ||
328 | if (ssh->kex->gss_deleg_creds) | 583 | if (kex->gss_deleg_creds) |
329 | ssh_gssapi_credentials_updated(ctxt); | 584 | ssh_gssapi_credentials_updated(ctxt); |
330 | 585 | ||
331 | if (gss_kex_context == NULL) | 586 | if (gss_kex_context == NULL) |
@@ -333,9 +588,19 @@ kexgss_client(struct ssh *ssh) { | |||
333 | else | 588 | else |
334 | ssh_gssapi_delete_ctx(&ctxt); | 589 | ssh_gssapi_delete_ctx(&ctxt); |
335 | 590 | ||
336 | kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); | 591 | /* Finally derive the keys and send them */ |
337 | BN_clear_free(shared_secret); | 592 | if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) |
338 | return kex_send_newkeys(ssh); | 593 | r = kex_send_newkeys(ssh); |
594 | out: | ||
595 | sshbuf_free(buf); | ||
596 | sshbuf_free(server_blob); | ||
597 | sshbuf_free(empty); | ||
598 | explicit_bzero(hash, sizeof(hash)); | ||
599 | DH_free(kex->dh); | ||
600 | kex->dh = NULL; | ||
601 | BN_clear_free(dh_server_pub); | ||
602 | sshbuf_free(shared_secret); | ||
603 | sshbuf_free(server_host_key_blob); | ||
604 | return r; | ||
339 | } | 605 | } |
340 | 606 | #endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */ | |
341 | #endif /* GSSAPI */ | ||