summaryrefslogtreecommitdiff
path: root/sshconnect2.c
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2020-10-18 12:04:32 +0100
commitd1b7918f9bce6e997c7952ac795e18d09192b2a6 (patch)
tree897785ddb933a151a3d4b7f7d1b6542aee570b76 /sshconnect2.c
parent2b2c99658e3e8ed452e28f88f9cdbcdfb2a461cb (diff)
GSSAPI key exchange support
This patch has been rejected upstream: "None of the OpenSSH developers are in favour of adding this, and this situation has not changed for several years. This is not a slight on Simon's patch, which is of fine quality, but just that a) we don't trust GSSAPI implementations that much and b) we don't like adding new KEX since they are pre-auth attack surface. This one is particularly scary, since it requires hooks out to typically root-owned system resources." However, quite a lot of people rely on this in Debian, and it's better to have it merged into the main openssh package rather than having separate -krb5 packages (as we used to have). It seems to have a generally good security history. Author: Simon Wilkinson <simon@sxw.org.uk> Author: Colin Watson <cjwatson@debian.org> Author: Jakub Jelen <jjelen@redhat.com> Origin: other, https://github.com/openssh-gsskex/openssh-gsskex/commits/debian/master Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242 Last-Updated: 2020-06-07 Patch-Name: gssapi.patch
Diffstat (limited to 'sshconnect2.c')
-rw-r--r--sshconnect2.c154
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 */
83extern char *client_version_string;
84extern char *server_version_string;
85extern Options options; 83extern 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 *);
377static int input_gssapi_token(int type, u_int32_t, struct ssh *); 445static int input_gssapi_token(int type, u_int32_t, struct ssh *);
378static int input_gssapi_error(int, u_int32_t, struct ssh *); 446static int input_gssapi_error(int, u_int32_t, struct ssh *);
379static int input_gssapi_errtok(int, u_int32_t, struct ssh *); 447static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
448static int userauth_gsskeyex(struct ssh *);
380#endif 449#endif
381 450
382void userauth(struct ssh *, char *); 451void userauth(struct ssh *, char *);
@@ -393,6 +462,11 @@ static char *authmethods_get(void);
393 462
394Authmethod authmethods[] = { 463Authmethod 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
1122int
1123userauth_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
1028static int 1172static int