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 1675f3935..8c872a4fb 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, |
@@ -686,25 +752,40 @@ userauth_gssapi(Authctxt *authctxt) | |||
686 | static u_int mech = 0; | 752 | static u_int mech = 0; |
687 | OM_uint32 min; | 753 | OM_uint32 min; |
688 | int r, ok = 0; | 754 | int r, ok = 0; |
755 | char *gss_host; | ||
756 | |||
757 | if (options.gss_server_identity) | ||
758 | gss_host = xstrdup(options.gss_server_identity); | ||
759 | else if (options.gss_trust_dns) | ||
760 | gss_host = remote_hostname(active_state); | ||
761 | else | ||
762 | gss_host = xstrdup(authctxt->host); | ||
689 | 763 | ||
690 | /* Try one GSSAPI method at a time, rather than sending them all at | 764 | /* Try one GSSAPI method at a time, rather than sending them all at |
691 | * once. */ | 765 | * once. */ |
692 | 766 | ||
693 | if (gss_supported == NULL) | 767 | if (gss_supported == NULL) |
694 | gss_indicate_mechs(&min, &gss_supported); | 768 | if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) { |
769 | gss_supported = NULL; | ||
770 | free(gss_host); | ||
771 | return 0; | ||
772 | } | ||
695 | 773 | ||
696 | /* Check to see if the mechanism is usable before we offer it */ | 774 | /* Check to see if the mechanism is usable before we offer it */ |
697 | while (mech < gss_supported->count && !ok) { | 775 | while (mech < gss_supported->count && !ok) { |
698 | /* My DER encoding requires length<128 */ | 776 | /* My DER encoding requires length<128 */ |
699 | if (gss_supported->elements[mech].length < 128 && | 777 | if (gss_supported->elements[mech].length < 128 && |
700 | ssh_gssapi_check_mechanism(&gssctxt, | 778 | ssh_gssapi_check_mechanism(&gssctxt, |
701 | &gss_supported->elements[mech], authctxt->host)) { | 779 | &gss_supported->elements[mech], gss_host, |
780 | options.gss_client_identity)) { | ||
702 | ok = 1; /* Mechanism works */ | 781 | ok = 1; /* Mechanism works */ |
703 | } else { | 782 | } else { |
704 | mech++; | 783 | mech++; |
705 | } | 784 | } |
706 | } | 785 | } |
707 | 786 | ||
787 | free(gss_host); | ||
788 | |||
708 | if (!ok) | 789 | if (!ok) |
709 | return 0; | 790 | return 0; |
710 | 791 | ||
@@ -935,6 +1016,54 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh) | |||
935 | free(lang); | 1016 | free(lang); |
936 | return r; | 1017 | return r; |
937 | } | 1018 | } |
1019 | |||
1020 | int | ||
1021 | userauth_gsskeyex(Authctxt *authctxt) | ||
1022 | { | ||
1023 | struct ssh *ssh = active_state; /* XXX */ | ||
1024 | struct sshbuf *b; | ||
1025 | gss_buffer_desc gssbuf; | ||
1026 | gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; | ||
1027 | OM_uint32 ms; | ||
1028 | int r; | ||
1029 | |||
1030 | static int attempt = 0; | ||
1031 | if (attempt++ >= 1) | ||
1032 | return (0); | ||
1033 | |||
1034 | if (gss_kex_context == NULL) { | ||
1035 | debug("No valid Key exchange context"); | ||
1036 | return (0); | ||
1037 | } | ||
1038 | |||
1039 | if ((b = sshbuf_new()) == NULL) | ||
1040 | fatal("%s: sshbuf_new failed", __func__); | ||
1041 | ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service, | ||
1042 | "gssapi-keyex"); | ||
1043 | |||
1044 | if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) | ||
1045 | fatal("%s: sshbuf_mutable_ptr failed", __func__); | ||
1046 | gssbuf.length = sshbuf_len(b); | ||
1047 | |||
1048 | if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { | ||
1049 | sshbuf_free(b); | ||
1050 | return (0); | ||
1051 | } | ||
1052 | |||
1053 | if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || | ||
1054 | (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || | ||
1055 | (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || | ||
1056 | (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || | ||
1057 | (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 || | ||
1058 | (r = sshpkt_send(ssh)) != 0) | ||
1059 | fatal("%s: %s", __func__, ssh_err(r)); | ||
1060 | |||
1061 | sshbuf_free(b); | ||
1062 | gss_release_buffer(&ms, &mic); | ||
1063 | |||
1064 | return (1); | ||
1065 | } | ||
1066 | |||
938 | #endif /* GSSAPI */ | 1067 | #endif /* GSSAPI */ |
939 | 1068 | ||
940 | int | 1069 | int |