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-06-07 10:24:45 +0100
commit79f9d21b406c172878896ef41cdc2502fc2f84a7 (patch)
tree71507aaefd925223b1543b10f4342f2df9ea0ee3 /sshconnect2.c
parent202f5a676221c244cd450086c334c2b59f339e86 (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 1a6545edf..79a22e600 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/*
@@ -163,6 +161,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
163 char *s, *all_key; 161 char *s, *all_key;
164 int r, use_known_hosts_order = 0; 162 int r, use_known_hosts_order = 0;
165 163
164#if defined(GSSAPI) && defined(WITH_OPENSSL)
165 char *orig = NULL, *gss = NULL;
166 char *gss_host = NULL;
167#endif
168
166 xxx_host = host; 169 xxx_host = host;
167 xxx_hostaddr = hostaddr; 170 xxx_hostaddr = hostaddr;
168 171
@@ -206,6 +209,41 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
206 compat_pkalg_proposal(options.hostkeyalgorithms); 209 compat_pkalg_proposal(options.hostkeyalgorithms);
207 } 210 }
208 211
212#if defined(GSSAPI) && defined(WITH_OPENSSL)
213 if (options.gss_keyex) {
214 /* Add the GSSAPI mechanisms currently supported on this
215 * client to the key exchange algorithm proposal */
216 orig = myproposal[PROPOSAL_KEX_ALGS];
217
218 if (options.gss_server_identity) {
219 gss_host = xstrdup(options.gss_server_identity);
220 } else if (options.gss_trust_dns) {
221 gss_host = remote_hostname(ssh);
222 /* Fall back to specified host if we are using proxy command
223 * and can not use DNS on that socket */
224 if (strcmp(gss_host, "UNKNOWN") == 0) {
225 gss_host = xstrdup(host);
226 }
227 } else {
228 gss_host = xstrdup(host);
229 }
230
231 gss = ssh_gssapi_client_mechanisms(gss_host,
232 options.gss_client_identity, options.gss_kex_algorithms);
233 if (gss) {
234 debug("Offering GSSAPI proposal: %s", gss);
235 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
236 "%s,%s", gss, orig);
237
238 /* If we've got GSSAPI algorithms, then we also support the
239 * 'null' hostkey, as a last resort */
240 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
241 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
242 "%s,null", orig);
243 }
244 }
245#endif
246
209 if (options.rekey_limit || options.rekey_interval) 247 if (options.rekey_limit || options.rekey_interval)
210 ssh_packet_set_rekey_limits(ssh, options.rekey_limit, 248 ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
211 options.rekey_interval); 249 options.rekey_interval);
@@ -224,16 +262,46 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
224# ifdef OPENSSL_HAS_ECC 262# ifdef OPENSSL_HAS_ECC
225 ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; 263 ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
226# endif 264# endif
227#endif 265# ifdef GSSAPI
266 if (options.gss_keyex) {
267 ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
268 ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
269 ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client;
270 ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client;
271 ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client;
272 ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client;
273 ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client;
274 }
275# endif
276#endif /* WITH_OPENSSL */
228 ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; 277 ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
229 ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client; 278 ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client;
230 ssh->kex->verify_host_key=&verify_host_key_callback; 279 ssh->kex->verify_host_key=&verify_host_key_callback;
231 280
281#if defined(GSSAPI) && defined(WITH_OPENSSL)
282 if (options.gss_keyex) {
283 ssh->kex->gss_deleg_creds = options.gss_deleg_creds;
284 ssh->kex->gss_trust_dns = options.gss_trust_dns;
285 ssh->kex->gss_client = options.gss_client_identity;
286 ssh->kex->gss_host = gss_host;
287 }
288#endif
289
232 ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done); 290 ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
233 291
234 /* remove ext-info from the KEX proposals for rekeying */ 292 /* remove ext-info from the KEX proposals for rekeying */
235 myproposal[PROPOSAL_KEX_ALGS] = 293 myproposal[PROPOSAL_KEX_ALGS] =
236 compat_kex_proposal(options.kex_algorithms); 294 compat_kex_proposal(options.kex_algorithms);
295#if defined(GSSAPI) && defined(WITH_OPENSSL)
296 /* repair myproposal after it was crumpled by the */
297 /* ext-info removal above */
298 if (gss) {
299 orig = myproposal[PROPOSAL_KEX_ALGS];
300 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
301 "%s,%s", gss, orig);
302 free(gss);
303 }
304#endif
237 if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0) 305 if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0)
238 fatal("kex_prop2buf: %s", ssh_err(r)); 306 fatal("kex_prop2buf: %s", ssh_err(r));
239 307
@@ -330,6 +398,7 @@ static int input_gssapi_response(int type, u_int32_t, struct ssh *);
330static int input_gssapi_token(int type, u_int32_t, struct ssh *); 398static int input_gssapi_token(int type, u_int32_t, struct ssh *);
331static int input_gssapi_error(int, u_int32_t, struct ssh *); 399static int input_gssapi_error(int, u_int32_t, struct ssh *);
332static int input_gssapi_errtok(int, u_int32_t, struct ssh *); 400static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
401static int userauth_gsskeyex(struct ssh *);
333#endif 402#endif
334 403
335void userauth(struct ssh *, char *); 404void userauth(struct ssh *, char *);
@@ -346,6 +415,11 @@ static char *authmethods_get(void);
346 415
347Authmethod authmethods[] = { 416Authmethod authmethods[] = {
348#ifdef GSSAPI 417#ifdef GSSAPI
418 {"gssapi-keyex",
419 userauth_gsskeyex,
420 NULL,
421 &options.gss_keyex,
422 NULL},
349 {"gssapi-with-mic", 423 {"gssapi-with-mic",
350 userauth_gssapi, 424 userauth_gssapi,
351 userauth_gssapi_cleanup, 425 userauth_gssapi_cleanup,
@@ -716,12 +790,31 @@ userauth_gssapi(struct ssh *ssh)
716 OM_uint32 min; 790 OM_uint32 min;
717 int r, ok = 0; 791 int r, ok = 0;
718 gss_OID mech = NULL; 792 gss_OID mech = NULL;
793 char *gss_host;
794
795 if (options.gss_server_identity) {
796 gss_host = xstrdup(options.gss_server_identity);
797 } else if (options.gss_trust_dns) {
798 gss_host = remote_hostname(ssh);
799 /* Fall back to specified host if we are using proxy command
800 * and can not use DNS on that socket */
801 if (strcmp(gss_host, "UNKNOWN") == 0) {
802 gss_host = authctxt->host;
803 }
804 } else {
805 gss_host = xstrdup(authctxt->host);
806 }
719 807
720 /* Try one GSSAPI method at a time, rather than sending them all at 808 /* Try one GSSAPI method at a time, rather than sending them all at
721 * once. */ 809 * once. */
722 810
723 if (authctxt->gss_supported_mechs == NULL) 811 if (authctxt->gss_supported_mechs == NULL)
724 gss_indicate_mechs(&min, &authctxt->gss_supported_mechs); 812 if (GSS_ERROR(gss_indicate_mechs(&min,
813 &authctxt->gss_supported_mechs))) {
814 authctxt->gss_supported_mechs = NULL;
815 free(gss_host);
816 return 0;
817 }
725 818
726 /* Check to see whether the mechanism is usable before we offer it */ 819 /* Check to see whether the mechanism is usable before we offer it */
727 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count && 820 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
@@ -730,13 +823,15 @@ userauth_gssapi(struct ssh *ssh)
730 elements[authctxt->mech_tried]; 823 elements[authctxt->mech_tried];
731 /* My DER encoding requires length<128 */ 824 /* My DER encoding requires length<128 */
732 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt, 825 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
733 mech, authctxt->host)) { 826 mech, gss_host, options.gss_client_identity)) {
734 ok = 1; /* Mechanism works */ 827 ok = 1; /* Mechanism works */
735 } else { 828 } else {
736 authctxt->mech_tried++; 829 authctxt->mech_tried++;
737 } 830 }
738 } 831 }
739 832
833 free(gss_host);
834
740 if (!ok || mech == NULL) 835 if (!ok || mech == NULL)
741 return 0; 836 return 0;
742 837
@@ -976,6 +1071,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
976 free(lang); 1071 free(lang);
977 return r; 1072 return r;
978} 1073}
1074
1075int
1076userauth_gsskeyex(struct ssh *ssh)
1077{
1078 struct sshbuf *b = NULL;
1079 Authctxt *authctxt = ssh->authctxt;
1080 gss_buffer_desc gssbuf;
1081 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
1082 OM_uint32 ms;
1083 int r;
1084
1085 static int attempt = 0;
1086 if (attempt++ >= 1)
1087 return (0);
1088
1089 if (gss_kex_context == NULL) {
1090 debug("No valid Key exchange context");
1091 return (0);
1092 }
1093
1094 if ((b = sshbuf_new()) == NULL)
1095 fatal("%s: sshbuf_new failed", __func__);
1096
1097 ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
1098 "gssapi-keyex");
1099
1100 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
1101 fatal("%s: sshbuf_mutable_ptr failed", __func__);
1102 gssbuf.length = sshbuf_len(b);
1103
1104 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
1105 sshbuf_free(b);
1106 return (0);
1107 }
1108
1109 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1110 (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1111 (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1112 (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1113 (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
1114 (r = sshpkt_send(ssh)) != 0)
1115 fatal("%s: %s", __func__, ssh_err(r));
1116
1117 sshbuf_free(b);
1118 gss_release_buffer(&ms, &mic);
1119
1120 return (1);
1121}
1122
979#endif /* GSSAPI */ 1123#endif /* GSSAPI */
980 1124
981static int 1125static int