diff options
Diffstat (limited to 'sshconnect2.c')
-rw-r--r-- | sshconnect2.c | 154 |
1 files changed, 149 insertions, 5 deletions
diff --git a/sshconnect2.c b/sshconnect2.c index f64aae66a..c47fc31a6 100644 --- a/sshconnect2.c +++ b/sshconnect2.c | |||
@@ -80,8 +80,6 @@ | |||
80 | #endif | 80 | #endif |
81 | 81 | ||
82 | /* import */ | 82 | /* import */ |
83 | extern char *client_version_string; | ||
84 | extern char *server_version_string; | ||
85 | extern Options options; | 83 | extern Options options; |
86 | 84 | ||
87 | /* | 85 | /* |
@@ -210,6 +208,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) | |||
210 | char *s, *all_key; | 208 | char *s, *all_key; |
211 | int r, use_known_hosts_order = 0; | 209 | int r, use_known_hosts_order = 0; |
212 | 210 | ||
211 | #if defined(GSSAPI) && defined(WITH_OPENSSL) | ||
212 | char *orig = NULL, *gss = NULL; | ||
213 | char *gss_host = NULL; | ||
214 | #endif | ||
215 | |||
213 | xxx_host = host; | 216 | xxx_host = host; |
214 | xxx_hostaddr = hostaddr; | 217 | xxx_hostaddr = hostaddr; |
215 | 218 | ||
@@ -253,6 +256,41 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) | |||
253 | compat_pkalg_proposal(options.hostkeyalgorithms); | 256 | compat_pkalg_proposal(options.hostkeyalgorithms); |
254 | } | 257 | } |
255 | 258 | ||
259 | #if defined(GSSAPI) && defined(WITH_OPENSSL) | ||
260 | if (options.gss_keyex) { | ||
261 | /* Add the GSSAPI mechanisms currently supported on this | ||
262 | * client to the key exchange algorithm proposal */ | ||
263 | orig = myproposal[PROPOSAL_KEX_ALGS]; | ||
264 | |||
265 | if (options.gss_server_identity) { | ||
266 | gss_host = xstrdup(options.gss_server_identity); | ||
267 | } else if (options.gss_trust_dns) { | ||
268 | gss_host = remote_hostname(ssh); | ||
269 | /* Fall back to specified host if we are using proxy command | ||
270 | * and can not use DNS on that socket */ | ||
271 | if (strcmp(gss_host, "UNKNOWN") == 0) { | ||
272 | gss_host = xstrdup(host); | ||
273 | } | ||
274 | } else { | ||
275 | gss_host = xstrdup(host); | ||
276 | } | ||
277 | |||
278 | gss = ssh_gssapi_client_mechanisms(gss_host, | ||
279 | options.gss_client_identity, options.gss_kex_algorithms); | ||
280 | if (gss) { | ||
281 | debug("Offering GSSAPI proposal: %s", gss); | ||
282 | xasprintf(&myproposal[PROPOSAL_KEX_ALGS], | ||
283 | "%s,%s", gss, orig); | ||
284 | |||
285 | /* If we've got GSSAPI algorithms, then we also support the | ||
286 | * 'null' hostkey, as a last resort */ | ||
287 | orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; | ||
288 | xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], | ||
289 | "%s,null", orig); | ||
290 | } | ||
291 | } | ||
292 | #endif | ||
293 | |||
256 | if (options.rekey_limit || options.rekey_interval) | 294 | if (options.rekey_limit || options.rekey_interval) |
257 | ssh_packet_set_rekey_limits(ssh, options.rekey_limit, | 295 | ssh_packet_set_rekey_limits(ssh, options.rekey_limit, |
258 | options.rekey_interval); | 296 | options.rekey_interval); |
@@ -271,16 +309,46 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) | |||
271 | # ifdef OPENSSL_HAS_ECC | 309 | # ifdef OPENSSL_HAS_ECC |
272 | ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; | 310 | ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; |
273 | # endif | 311 | # endif |
274 | #endif | 312 | # ifdef GSSAPI |
313 | if (options.gss_keyex) { | ||
314 | ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; | ||
315 | ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; | ||
316 | ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client; | ||
317 | ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client; | ||
318 | ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client; | ||
319 | ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client; | ||
320 | ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client; | ||
321 | } | ||
322 | # endif | ||
323 | #endif /* WITH_OPENSSL */ | ||
275 | ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; | 324 | ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; |
276 | ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client; | 325 | ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client; |
277 | ssh->kex->verify_host_key=&verify_host_key_callback; | 326 | ssh->kex->verify_host_key=&verify_host_key_callback; |
278 | 327 | ||
328 | #if defined(GSSAPI) && defined(WITH_OPENSSL) | ||
329 | if (options.gss_keyex) { | ||
330 | ssh->kex->gss_deleg_creds = options.gss_deleg_creds; | ||
331 | ssh->kex->gss_trust_dns = options.gss_trust_dns; | ||
332 | ssh->kex->gss_client = options.gss_client_identity; | ||
333 | ssh->kex->gss_host = gss_host; | ||
334 | } | ||
335 | #endif | ||
336 | |||
279 | ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done); | 337 | ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done); |
280 | 338 | ||
281 | /* remove ext-info from the KEX proposals for rekeying */ | 339 | /* remove ext-info from the KEX proposals for rekeying */ |
282 | myproposal[PROPOSAL_KEX_ALGS] = | 340 | myproposal[PROPOSAL_KEX_ALGS] = |
283 | compat_kex_proposal(options.kex_algorithms); | 341 | compat_kex_proposal(options.kex_algorithms); |
342 | #if defined(GSSAPI) && defined(WITH_OPENSSL) | ||
343 | /* repair myproposal after it was crumpled by the */ | ||
344 | /* ext-info removal above */ | ||
345 | if (gss) { | ||
346 | orig = myproposal[PROPOSAL_KEX_ALGS]; | ||
347 | xasprintf(&myproposal[PROPOSAL_KEX_ALGS], | ||
348 | "%s,%s", gss, orig); | ||
349 | free(gss); | ||
350 | } | ||
351 | #endif | ||
284 | if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0) | 352 | if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0) |
285 | fatal("kex_prop2buf: %s", ssh_err(r)); | 353 | fatal("kex_prop2buf: %s", ssh_err(r)); |
286 | 354 | ||
@@ -377,6 +445,7 @@ static int input_gssapi_response(int type, u_int32_t, struct ssh *); | |||
377 | static int input_gssapi_token(int type, u_int32_t, struct ssh *); | 445 | static int input_gssapi_token(int type, u_int32_t, struct ssh *); |
378 | static int input_gssapi_error(int, u_int32_t, struct ssh *); | 446 | static int input_gssapi_error(int, u_int32_t, struct ssh *); |
379 | static int input_gssapi_errtok(int, u_int32_t, struct ssh *); | 447 | static int input_gssapi_errtok(int, u_int32_t, struct ssh *); |
448 | static int userauth_gsskeyex(struct ssh *); | ||
380 | #endif | 449 | #endif |
381 | 450 | ||
382 | void userauth(struct ssh *, char *); | 451 | void userauth(struct ssh *, char *); |
@@ -393,6 +462,11 @@ static char *authmethods_get(void); | |||
393 | 462 | ||
394 | Authmethod authmethods[] = { | 463 | Authmethod authmethods[] = { |
395 | #ifdef GSSAPI | 464 | #ifdef GSSAPI |
465 | {"gssapi-keyex", | ||
466 | userauth_gsskeyex, | ||
467 | NULL, | ||
468 | &options.gss_keyex, | ||
469 | NULL}, | ||
396 | {"gssapi-with-mic", | 470 | {"gssapi-with-mic", |
397 | userauth_gssapi, | 471 | userauth_gssapi, |
398 | userauth_gssapi_cleanup, | 472 | userauth_gssapi_cleanup, |
@@ -763,12 +837,31 @@ userauth_gssapi(struct ssh *ssh) | |||
763 | OM_uint32 min; | 837 | OM_uint32 min; |
764 | int r, ok = 0; | 838 | int r, ok = 0; |
765 | gss_OID mech = NULL; | 839 | gss_OID mech = NULL; |
840 | char *gss_host; | ||
841 | |||
842 | if (options.gss_server_identity) { | ||
843 | gss_host = xstrdup(options.gss_server_identity); | ||
844 | } else if (options.gss_trust_dns) { | ||
845 | gss_host = remote_hostname(ssh); | ||
846 | /* Fall back to specified host if we are using proxy command | ||
847 | * and can not use DNS on that socket */ | ||
848 | if (strcmp(gss_host, "UNKNOWN") == 0) { | ||
849 | gss_host = authctxt->host; | ||
850 | } | ||
851 | } else { | ||
852 | gss_host = xstrdup(authctxt->host); | ||
853 | } | ||
766 | 854 | ||
767 | /* Try one GSSAPI method at a time, rather than sending them all at | 855 | /* Try one GSSAPI method at a time, rather than sending them all at |
768 | * once. */ | 856 | * once. */ |
769 | 857 | ||
770 | if (authctxt->gss_supported_mechs == NULL) | 858 | if (authctxt->gss_supported_mechs == NULL) |
771 | gss_indicate_mechs(&min, &authctxt->gss_supported_mechs); | 859 | if (GSS_ERROR(gss_indicate_mechs(&min, |
860 | &authctxt->gss_supported_mechs))) { | ||
861 | authctxt->gss_supported_mechs = NULL; | ||
862 | free(gss_host); | ||
863 | return 0; | ||
864 | } | ||
772 | 865 | ||
773 | /* Check to see whether the mechanism is usable before we offer it */ | 866 | /* Check to see whether the mechanism is usable before we offer it */ |
774 | while (authctxt->mech_tried < authctxt->gss_supported_mechs->count && | 867 | while (authctxt->mech_tried < authctxt->gss_supported_mechs->count && |
@@ -777,13 +870,15 @@ userauth_gssapi(struct ssh *ssh) | |||
777 | elements[authctxt->mech_tried]; | 870 | elements[authctxt->mech_tried]; |
778 | /* My DER encoding requires length<128 */ | 871 | /* My DER encoding requires length<128 */ |
779 | if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt, | 872 | if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt, |
780 | mech, authctxt->host)) { | 873 | mech, gss_host, options.gss_client_identity)) { |
781 | ok = 1; /* Mechanism works */ | 874 | ok = 1; /* Mechanism works */ |
782 | } else { | 875 | } else { |
783 | authctxt->mech_tried++; | 876 | authctxt->mech_tried++; |
784 | } | 877 | } |
785 | } | 878 | } |
786 | 879 | ||
880 | free(gss_host); | ||
881 | |||
787 | if (!ok || mech == NULL) | 882 | if (!ok || mech == NULL) |
788 | return 0; | 883 | return 0; |
789 | 884 | ||
@@ -1023,6 +1118,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh) | |||
1023 | free(lang); | 1118 | free(lang); |
1024 | return r; | 1119 | return r; |
1025 | } | 1120 | } |
1121 | |||
1122 | int | ||
1123 | userauth_gsskeyex(struct ssh *ssh) | ||
1124 | { | ||
1125 | struct sshbuf *b = NULL; | ||
1126 | Authctxt *authctxt = ssh->authctxt; | ||
1127 | gss_buffer_desc gssbuf; | ||
1128 | gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; | ||
1129 | OM_uint32 ms; | ||
1130 | int r; | ||
1131 | |||
1132 | static int attempt = 0; | ||
1133 | if (attempt++ >= 1) | ||
1134 | return (0); | ||
1135 | |||
1136 | if (gss_kex_context == NULL) { | ||
1137 | debug("No valid Key exchange context"); | ||
1138 | return (0); | ||
1139 | } | ||
1140 | |||
1141 | if ((b = sshbuf_new()) == NULL) | ||
1142 | fatal("%s: sshbuf_new failed", __func__); | ||
1143 | |||
1144 | ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service, | ||
1145 | "gssapi-keyex"); | ||
1146 | |||
1147 | if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) | ||
1148 | fatal("%s: sshbuf_mutable_ptr failed", __func__); | ||
1149 | gssbuf.length = sshbuf_len(b); | ||
1150 | |||
1151 | if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { | ||
1152 | sshbuf_free(b); | ||
1153 | return (0); | ||
1154 | } | ||
1155 | |||
1156 | if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || | ||
1157 | (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || | ||
1158 | (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || | ||
1159 | (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || | ||
1160 | (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 || | ||
1161 | (r = sshpkt_send(ssh)) != 0) | ||
1162 | fatal("%s: %s", __func__, ssh_err(r)); | ||
1163 | |||
1164 | sshbuf_free(b); | ||
1165 | gss_release_buffer(&ms, &mic); | ||
1166 | |||
1167 | return (1); | ||
1168 | } | ||
1169 | |||
1026 | #endif /* GSSAPI */ | 1170 | #endif /* GSSAPI */ |
1027 | 1171 | ||
1028 | static int | 1172 | static int |