diff options
Diffstat (limited to 'sshconnect2.c')
-rw-r--r-- | sshconnect2.c | 133 |
1 files changed, 131 insertions, 2 deletions
diff --git a/sshconnect2.c b/sshconnect2.c index 10e4f0a08..c6a1b1271 100644 --- a/sshconnect2.c +++ b/sshconnect2.c | |||
@@ -162,6 +162,11 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) | |||
162 | struct kex *kex; | 162 | struct kex *kex; |
163 | int r; | 163 | int r; |
164 | 164 | ||
165 | #ifdef GSSAPI | ||
166 | char *orig = NULL, *gss = NULL; | ||
167 | char *gss_host = NULL; | ||
168 | #endif | ||
169 | |||
165 | xxx_host = host; | 170 | xxx_host = host; |
166 | xxx_hostaddr = hostaddr; | 171 | xxx_hostaddr = hostaddr; |
167 | 172 | ||
@@ -194,6 +199,35 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) | |||
194 | order_hostkeyalgs(host, hostaddr, port)); | 199 | order_hostkeyalgs(host, hostaddr, port)); |
195 | } | 200 | } |
196 | 201 | ||
202 | #ifdef GSSAPI | ||
203 | if (options.gss_keyex) { | ||
204 | /* Add the GSSAPI mechanisms currently supported on this | ||
205 | * client to the key exchange algorithm proposal */ | ||
206 | orig = myproposal[PROPOSAL_KEX_ALGS]; | ||
207 | |||
208 | if (options.gss_server_identity) | ||
209 | gss_host = xstrdup(options.gss_server_identity); | ||
210 | else if (options.gss_trust_dns) | ||
211 | gss_host = remote_hostname(active_state); | ||
212 | else | ||
213 | gss_host = xstrdup(host); | ||
214 | |||
215 | gss = ssh_gssapi_client_mechanisms(gss_host, | ||
216 | options.gss_client_identity); | ||
217 | if (gss) { | ||
218 | debug("Offering GSSAPI proposal: %s", gss); | ||
219 | xasprintf(&myproposal[PROPOSAL_KEX_ALGS], | ||
220 | "%s,%s", gss, orig); | ||
221 | |||
222 | /* If we've got GSSAPI algorithms, then we also | ||
223 | * support the 'null' hostkey, as a last resort */ | ||
224 | orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; | ||
225 | xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], | ||
226 | "%s,null", orig); | ||
227 | } | ||
228 | } | ||
229 | #endif | ||
230 | |||
197 | if (options.rekey_limit || options.rekey_interval) | 231 | if (options.rekey_limit || options.rekey_interval) |
198 | packet_set_rekey_limits(options.rekey_limit, | 232 | packet_set_rekey_limits(options.rekey_limit, |
199 | options.rekey_interval); | 233 | options.rekey_interval); |
@@ -215,15 +249,41 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) | |||
215 | # endif | 249 | # endif |
216 | #endif | 250 | #endif |
217 | kex->kex[KEX_C25519_SHA256] = kexc25519_client; | 251 | kex->kex[KEX_C25519_SHA256] = kexc25519_client; |
252 | #ifdef GSSAPI | ||
253 | if (options.gss_keyex) { | ||
254 | kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; | ||
255 | kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; | ||
256 | kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; | ||
257 | } | ||
258 | #endif | ||
218 | kex->client_version_string=client_version_string; | 259 | kex->client_version_string=client_version_string; |
219 | kex->server_version_string=server_version_string; | 260 | kex->server_version_string=server_version_string; |
220 | kex->verify_host_key=&verify_host_key_callback; | 261 | kex->verify_host_key=&verify_host_key_callback; |
221 | 262 | ||
263 | #ifdef GSSAPI | ||
264 | if (options.gss_keyex) { | ||
265 | kex->gss_deleg_creds = options.gss_deleg_creds; | ||
266 | kex->gss_trust_dns = options.gss_trust_dns; | ||
267 | kex->gss_client = options.gss_client_identity; | ||
268 | kex->gss_host = gss_host; | ||
269 | } | ||
270 | #endif | ||
271 | |||
222 | ssh_dispatch_run_fatal(active_state, DISPATCH_BLOCK, &kex->done); | 272 | ssh_dispatch_run_fatal(active_state, DISPATCH_BLOCK, &kex->done); |
223 | 273 | ||
224 | /* remove ext-info from the KEX proposals for rekeying */ | 274 | /* remove ext-info from the KEX proposals for rekeying */ |
225 | myproposal[PROPOSAL_KEX_ALGS] = | 275 | myproposal[PROPOSAL_KEX_ALGS] = |
226 | compat_kex_proposal(options.kex_algorithms); | 276 | compat_kex_proposal(options.kex_algorithms); |
277 | #ifdef GSSAPI | ||
278 | /* repair myproposal after it was crumpled by the */ | ||
279 | /* ext-info removal above */ | ||
280 | if (gss) { | ||
281 | orig = myproposal[PROPOSAL_KEX_ALGS]; | ||
282 | xasprintf(&myproposal[PROPOSAL_KEX_ALGS], | ||
283 | "%s,%s", gss, orig); | ||
284 | free(gss); | ||
285 | } | ||
286 | #endif | ||
227 | if ((r = kex_prop2buf(kex->my, myproposal)) != 0) | 287 | if ((r = kex_prop2buf(kex->my, myproposal)) != 0) |
228 | fatal("kex_prop2buf: %s", ssh_err(r)); | 288 | fatal("kex_prop2buf: %s", ssh_err(r)); |
229 | 289 | ||
@@ -314,6 +374,7 @@ int input_gssapi_token(int type, u_int32_t, struct ssh *); | |||
314 | int input_gssapi_hash(int type, u_int32_t, struct ssh *); | 374 | int input_gssapi_hash(int type, u_int32_t, struct ssh *); |
315 | int input_gssapi_error(int, u_int32_t, struct ssh *); | 375 | int input_gssapi_error(int, u_int32_t, struct ssh *); |
316 | int input_gssapi_errtok(int, u_int32_t, struct ssh *); | 376 | int input_gssapi_errtok(int, u_int32_t, struct ssh *); |
377 | int userauth_gsskeyex(Authctxt *authctxt); | ||
317 | #endif | 378 | #endif |
318 | 379 | ||
319 | void userauth(Authctxt *, char *); | 380 | void userauth(Authctxt *, char *); |
@@ -330,6 +391,11 @@ static char *authmethods_get(void); | |||
330 | 391 | ||
331 | Authmethod authmethods[] = { | 392 | Authmethod authmethods[] = { |
332 | #ifdef GSSAPI | 393 | #ifdef GSSAPI |
394 | {"gssapi-keyex", | ||
395 | userauth_gsskeyex, | ||
396 | NULL, | ||
397 | &options.gss_authentication, | ||
398 | NULL}, | ||
333 | {"gssapi-with-mic", | 399 | {"gssapi-with-mic", |
334 | userauth_gssapi, | 400 | userauth_gssapi, |
335 | NULL, | 401 | NULL, |
@@ -657,25 +723,40 @@ userauth_gssapi(Authctxt *authctxt) | |||
657 | static u_int mech = 0; | 723 | static u_int mech = 0; |
658 | OM_uint32 min; | 724 | OM_uint32 min; |
659 | int r, ok = 0; | 725 | int r, ok = 0; |
726 | char *gss_host; | ||
727 | |||
728 | if (options.gss_server_identity) | ||
729 | gss_host = xstrdup(options.gss_server_identity); | ||
730 | else if (options.gss_trust_dns) | ||
731 | gss_host = remote_hostname(active_state); | ||
732 | else | ||
733 | gss_host = xstrdup(authctxt->host); | ||
660 | 734 | ||
661 | /* Try one GSSAPI method at a time, rather than sending them all at | 735 | /* Try one GSSAPI method at a time, rather than sending them all at |
662 | * once. */ | 736 | * once. */ |
663 | 737 | ||
664 | if (gss_supported == NULL) | 738 | if (gss_supported == NULL) |
665 | gss_indicate_mechs(&min, &gss_supported); | 739 | if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) { |
740 | gss_supported = NULL; | ||
741 | free(gss_host); | ||
742 | return 0; | ||
743 | } | ||
666 | 744 | ||
667 | /* Check to see if the mechanism is usable before we offer it */ | 745 | /* Check to see if the mechanism is usable before we offer it */ |
668 | while (mech < gss_supported->count && !ok) { | 746 | while (mech < gss_supported->count && !ok) { |
669 | /* My DER encoding requires length<128 */ | 747 | /* My DER encoding requires length<128 */ |
670 | if (gss_supported->elements[mech].length < 128 && | 748 | if (gss_supported->elements[mech].length < 128 && |
671 | ssh_gssapi_check_mechanism(&gssctxt, | 749 | ssh_gssapi_check_mechanism(&gssctxt, |
672 | &gss_supported->elements[mech], authctxt->host)) { | 750 | &gss_supported->elements[mech], gss_host, |
751 | options.gss_client_identity)) { | ||
673 | ok = 1; /* Mechanism works */ | 752 | ok = 1; /* Mechanism works */ |
674 | } else { | 753 | } else { |
675 | mech++; | 754 | mech++; |
676 | } | 755 | } |
677 | } | 756 | } |
678 | 757 | ||
758 | free(gss_host); | ||
759 | |||
679 | if (!ok) | 760 | if (!ok) |
680 | return 0; | 761 | return 0; |
681 | 762 | ||
@@ -906,6 +987,54 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh) | |||
906 | free(lang); | 987 | free(lang); |
907 | return r; | 988 | return r; |
908 | } | 989 | } |
990 | |||
991 | int | ||
992 | userauth_gsskeyex(Authctxt *authctxt) | ||
993 | { | ||
994 | struct ssh *ssh = active_state; /* XXX */ | ||
995 | struct sshbuf *b; | ||
996 | gss_buffer_desc gssbuf; | ||
997 | gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; | ||
998 | OM_uint32 ms; | ||
999 | int r; | ||
1000 | |||
1001 | static int attempt = 0; | ||
1002 | if (attempt++ >= 1) | ||
1003 | return (0); | ||
1004 | |||
1005 | if (gss_kex_context == NULL) { | ||
1006 | debug("No valid Key exchange context"); | ||
1007 | return (0); | ||
1008 | } | ||
1009 | |||
1010 | if ((b = sshbuf_new()) == NULL) | ||
1011 | fatal("%s: sshbuf_new failed", __func__); | ||
1012 | ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service, | ||
1013 | "gssapi-keyex"); | ||
1014 | |||
1015 | if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) | ||
1016 | fatal("%s: sshbuf_mutable_ptr failed", __func__); | ||
1017 | gssbuf.length = sshbuf_len(b); | ||
1018 | |||
1019 | if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { | ||
1020 | sshbuf_free(b); | ||
1021 | return (0); | ||
1022 | } | ||
1023 | |||
1024 | if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || | ||
1025 | (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || | ||
1026 | (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || | ||
1027 | (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || | ||
1028 | (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 || | ||
1029 | (r = sshpkt_send(ssh)) != 0) | ||
1030 | fatal("%s: %s", __func__, ssh_err(r)); | ||
1031 | |||
1032 | sshbuf_free(b); | ||
1033 | gss_release_buffer(&ms, &mic); | ||
1034 | |||
1035 | return (1); | ||
1036 | } | ||
1037 | |||
909 | #endif /* GSSAPI */ | 1038 | #endif /* GSSAPI */ |
910 | 1039 | ||
911 | int | 1040 | int |