diff options
-rw-r--r-- | ChangeLog.gssapi | 75 | ||||
-rw-r--r-- | Makefile.in | 4 | ||||
-rw-r--r-- | auth-krb5.c | 17 | ||||
-rw-r--r-- | auth.h | 1 | ||||
-rw-r--r-- | auth2-gss.c | 41 | ||||
-rw-r--r-- | auth2.c | 3 | ||||
-rw-r--r-- | configure.ac | 24 | ||||
-rw-r--r-- | gss-genr.c | 170 | ||||
-rw-r--r-- | gss-serv-krb5.c | 14 | ||||
-rw-r--r-- | gss-serv.c | 73 | ||||
-rw-r--r-- | kex.c | 18 | ||||
-rw-r--r-- | kex.h | 13 | ||||
-rw-r--r-- | kexgssc.c | 319 | ||||
-rw-r--r-- | kexgsss.c | 271 | ||||
-rw-r--r-- | key.c | 2 | ||||
-rw-r--r-- | key.h | 1 | ||||
-rw-r--r-- | monitor.c | 59 | ||||
-rw-r--r-- | monitor.h | 1 | ||||
-rw-r--r-- | monitor_wrap.c | 23 | ||||
-rw-r--r-- | monitor_wrap.h | 1 | ||||
-rw-r--r-- | readconf.c | 20 | ||||
-rw-r--r-- | readconf.h | 2 | ||||
-rw-r--r-- | servconf.c | 22 | ||||
-rw-r--r-- | servconf.h | 2 | ||||
-rw-r--r-- | ssh-gss.h | 19 | ||||
-rw-r--r-- | ssh_config | 2 | ||||
-rw-r--r-- | ssh_config.5 | 17 | ||||
-rw-r--r-- | sshconnect2.c | 105 | ||||
-rw-r--r-- | sshd.c | 108 | ||||
-rw-r--r-- | sshd_config | 2 | ||||
-rw-r--r-- | sshd_config.5 | 23 |
31 files changed, 1414 insertions, 38 deletions
diff --git a/ChangeLog.gssapi b/ChangeLog.gssapi new file mode 100644 index 000000000..5c110d0d8 --- /dev/null +++ b/ChangeLog.gssapi | |||
@@ -0,0 +1,75 @@ | |||
1 | 20080404 | ||
2 | - [ gss-serv.c ] | ||
3 | Add code to actually implement GSSAPIStrictAcceptCheck, which had somehow | ||
4 | been omitted from a previous version of this patch. Reported by Borislav | ||
5 | Stoichkov | ||
6 | |||
7 | 20070317 | ||
8 | - [ gss-serv-krb5.c ] | ||
9 | Remove C99ism, where new_ccname was being declared in the middle of a | ||
10 | function | ||
11 | |||
12 | 20061220 | ||
13 | - [ servconf.c ] | ||
14 | Make default for GSSAPIStrictAcceptorCheck be Yes, to match previous, and | ||
15 | documented, behaviour. Reported by Dan Watson. | ||
16 | |||
17 | 20060910 | ||
18 | - [ gss-genr.c kexgssc.c kexgsss.c kex.h monitor.c sshconnect2.c sshd.c | ||
19 | ssh-gss.h ] | ||
20 | add support for gss-group14-sha1 key exchange mechanisms | ||
21 | - [ gss-serv.c servconf.c servconf.h sshd_config sshd_config.5 ] | ||
22 | Add GSSAPIStrictAcceptorCheck option to allow the disabling of | ||
23 | acceptor principal checking on multi-homed machines. | ||
24 | <Bugzilla #928> | ||
25 | - [ sshd_config ssh_config ] | ||
26 | Add settings for GSSAPIKeyExchange and GSSAPITrustDNS to the sample | ||
27 | configuration files | ||
28 | - [ kexgss.c kegsss.c sshconnect2.c sshd.c ] | ||
29 | Code cleanup. Replace strlen/xmalloc/snprintf sequences with xasprintf() | ||
30 | Limit length of error messages displayed by client | ||
31 | |||
32 | 20060909 | ||
33 | - [ gss-genr.c gss-serv.c ] | ||
34 | move ssh_gssapi_acquire_cred() and ssh_gssapi_server_ctx to be server | ||
35 | only, where they belong | ||
36 | <Bugzilla #1225> | ||
37 | |||
38 | 20060829 | ||
39 | - [ gss-serv-krb5.c ] | ||
40 | Fix CCAPI credentials cache name when creating KRB5CCNAME environment | ||
41 | variable | ||
42 | |||
43 | 20060828 | ||
44 | - [ gss-genr.c ] | ||
45 | Avoid Heimdal context freeing problem | ||
46 | <Fixed upstream 20060829> | ||
47 | |||
48 | 20060818 | ||
49 | - [ gss-genr.c ssh-gss.h sshconnect2.c ] | ||
50 | Make sure that SPENGO is disabled | ||
51 | <Bugzilla #1218 - Fixed upstream 20060818> | ||
52 | |||
53 | 20060421 | ||
54 | - [ gssgenr.c, sshconnect2.c ] | ||
55 | a few type changes (signed versus unsigned, int versus size_t) to | ||
56 | fix compiler errors/warnings | ||
57 | (from jbasney AT ncsa.uiuc.edu) | ||
58 | - [ kexgssc.c, sshconnect2.c ] | ||
59 | fix uninitialized variable warnings | ||
60 | (from jbasney AT ncsa.uiuc.edu) | ||
61 | - [ gssgenr.c ] | ||
62 | pass oid to gss_display_status (helpful when using GSSAPI mechglue) | ||
63 | (from jbasney AT ncsa.uiuc.edu) | ||
64 | <Bugzilla #1220 > | ||
65 | - [ gss-serv-krb5.c ] | ||
66 | #ifdef HAVE_GSSAPI_KRB5 should be #ifdef HAVE_GSSAPI_KRB5_H | ||
67 | (from jbasney AT ncsa.uiuc.edu) | ||
68 | <Fixed upstream 20060304> | ||
69 | - [ readconf.c, readconf.h, ssh_config.5, sshconnect2.c | ||
70 | add client-side GssapiKeyExchange option | ||
71 | (from jbasney AT ncsa.uiuc.edu) | ||
72 | - [ sshconnect2.c ] | ||
73 | add support for GssapiTrustDns option for gssapi-with-mic | ||
74 | (from jbasney AT ncsa.uiuc.edu) | ||
75 | <gssapi-with-mic support is Bugzilla #1008> | ||
diff --git a/Makefile.in b/Makefile.in index c1b7ab5a6..a81693d91 100644 --- a/Makefile.in +++ b/Makefile.in | |||
@@ -71,7 +71,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \ | |||
71 | atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ | 71 | atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ |
72 | monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \ | 72 | monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \ |
73 | kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \ | 73 | kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \ |
74 | entropy.o scard-opensc.o gss-genr.o umac.o | 74 | entropy.o scard-opensc.o gss-genr.o umac.o kexgssc.o |
75 | 75 | ||
76 | SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ | 76 | SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ |
77 | sshconnect.o sshconnect1.o sshconnect2.o mux.o | 77 | sshconnect.o sshconnect1.o sshconnect2.o mux.o |
@@ -84,7 +84,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ | |||
84 | auth2-none.o auth2-passwd.o auth2-pubkey.o \ | 84 | auth2-none.o auth2-passwd.o auth2-pubkey.o \ |
85 | monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \ | 85 | monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \ |
86 | auth-krb5.o \ | 86 | auth-krb5.o \ |
87 | auth2-gss.o gss-serv.o gss-serv-krb5.o \ | 87 | auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o\ |
88 | loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ | 88 | loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ |
89 | audit.o audit-bsm.o platform.o sftp-server.o sftp-common.o | 89 | audit.o audit-bsm.o platform.o sftp-server.o sftp-common.o |
90 | 90 | ||
diff --git a/auth-krb5.c b/auth-krb5.c index 868288126..38164fda8 100644 --- a/auth-krb5.c +++ b/auth-krb5.c | |||
@@ -166,8 +166,13 @@ auth_krb5_password(Authctxt *authctxt, const char *password) | |||
166 | 166 | ||
167 | len = strlen(authctxt->krb5_ticket_file) + 6; | 167 | len = strlen(authctxt->krb5_ticket_file) + 6; |
168 | authctxt->krb5_ccname = xmalloc(len); | 168 | authctxt->krb5_ccname = xmalloc(len); |
169 | #ifdef USE_CCAPI | ||
170 | snprintf(authctxt->krb5_ccname, len, "API:%s", | ||
171 | authctxt->krb5_ticket_file); | ||
172 | #else | ||
169 | snprintf(authctxt->krb5_ccname, len, "FILE:%s", | 173 | snprintf(authctxt->krb5_ccname, len, "FILE:%s", |
170 | authctxt->krb5_ticket_file); | 174 | authctxt->krb5_ticket_file); |
175 | #endif | ||
171 | 176 | ||
172 | #ifdef USE_PAM | 177 | #ifdef USE_PAM |
173 | if (options.use_pam) | 178 | if (options.use_pam) |
@@ -219,15 +224,22 @@ krb5_cleanup_proc(Authctxt *authctxt) | |||
219 | #ifndef HEIMDAL | 224 | #ifndef HEIMDAL |
220 | krb5_error_code | 225 | krb5_error_code |
221 | ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { | 226 | ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { |
222 | int tmpfd, ret; | 227 | int ret; |
223 | char ccname[40]; | 228 | char ccname[40]; |
224 | mode_t old_umask; | 229 | mode_t old_umask; |
230 | #ifdef USE_CCAPI | ||
231 | char cctemplate[] = "API:krb5cc_%d"; | ||
232 | #else | ||
233 | char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX"; | ||
234 | int tmpfd; | ||
235 | #endif | ||
225 | 236 | ||
226 | ret = snprintf(ccname, sizeof(ccname), | 237 | ret = snprintf(ccname, sizeof(ccname), |
227 | "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); | 238 | cctemplate, geteuid()); |
228 | if (ret < 0 || (size_t)ret >= sizeof(ccname)) | 239 | if (ret < 0 || (size_t)ret >= sizeof(ccname)) |
229 | return ENOMEM; | 240 | return ENOMEM; |
230 | 241 | ||
242 | #ifndef USE_CCAPI | ||
231 | old_umask = umask(0177); | 243 | old_umask = umask(0177); |
232 | tmpfd = mkstemp(ccname + strlen("FILE:")); | 244 | tmpfd = mkstemp(ccname + strlen("FILE:")); |
233 | umask(old_umask); | 245 | umask(old_umask); |
@@ -242,6 +254,7 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { | |||
242 | return errno; | 254 | return errno; |
243 | } | 255 | } |
244 | close(tmpfd); | 256 | close(tmpfd); |
257 | #endif | ||
245 | 258 | ||
246 | return (krb5_cc_resolve(ctx, ccname, ccache)); | 259 | return (krb5_cc_resolve(ctx, ccname, ccache)); |
247 | } | 260 | } |
@@ -53,6 +53,7 @@ struct Authctxt { | |||
53 | int valid; /* user exists and is allowed to login */ | 53 | int valid; /* user exists and is allowed to login */ |
54 | int attempt; | 54 | int attempt; |
55 | int failures; | 55 | int failures; |
56 | int server_caused_failure; | ||
56 | int force_pwchange; | 57 | int force_pwchange; |
57 | char *user; /* username sent by the client */ | 58 | char *user; /* username sent by the client */ |
58 | char *service; | 59 | char *service; |
diff --git a/auth2-gss.c b/auth2-gss.c index 0e08d889c..9f76f59bd 100644 --- a/auth2-gss.c +++ b/auth2-gss.c | |||
@@ -52,6 +52,39 @@ static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt); | |||
52 | static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); | 52 | static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); |
53 | static void input_gssapi_errtok(int, u_int32_t, void *); | 53 | static void input_gssapi_errtok(int, u_int32_t, void *); |
54 | 54 | ||
55 | /* | ||
56 | * The 'gssapi_keyex' userauth mechanism. | ||
57 | */ | ||
58 | static int | ||
59 | userauth_gsskeyex(Authctxt *authctxt) | ||
60 | { | ||
61 | int authenticated = 0; | ||
62 | Buffer b; | ||
63 | gss_buffer_desc mic, gssbuf; | ||
64 | u_int len; | ||
65 | |||
66 | mic.value = packet_get_string(&len); | ||
67 | mic.length = len; | ||
68 | |||
69 | packet_check_eom(); | ||
70 | |||
71 | ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service, | ||
72 | "gssapi-keyex"); | ||
73 | |||
74 | gssbuf.value = buffer_ptr(&b); | ||
75 | gssbuf.length = buffer_len(&b); | ||
76 | |||
77 | /* gss_kex_context is NULL with privsep, so we can't check it here */ | ||
78 | if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, | ||
79 | &gssbuf, &mic)))) | ||
80 | authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); | ||
81 | |||
82 | buffer_free(&b); | ||
83 | xfree(mic.value); | ||
84 | |||
85 | return (authenticated); | ||
86 | } | ||
87 | |||
55 | /* | 88 | /* |
56 | * We only support those mechanisms that we know about (ie ones that we know | 89 | * We only support those mechanisms that we know about (ie ones that we know |
57 | * how to check local user kuserok and the like) | 90 | * how to check local user kuserok and the like) |
@@ -102,6 +135,7 @@ userauth_gssapi(Authctxt *authctxt) | |||
102 | 135 | ||
103 | if (!present) { | 136 | if (!present) { |
104 | xfree(doid); | 137 | xfree(doid); |
138 | authctxt->server_caused_failure = 1; | ||
105 | return (0); | 139 | return (0); |
106 | } | 140 | } |
107 | 141 | ||
@@ -109,6 +143,7 @@ userauth_gssapi(Authctxt *authctxt) | |||
109 | if (ctxt != NULL) | 143 | if (ctxt != NULL) |
110 | ssh_gssapi_delete_ctx(&ctxt); | 144 | ssh_gssapi_delete_ctx(&ctxt); |
111 | xfree(doid); | 145 | xfree(doid); |
146 | authctxt->server_caused_failure = 1; | ||
112 | return (0); | 147 | return (0); |
113 | } | 148 | } |
114 | 149 | ||
@@ -292,6 +327,12 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt) | |||
292 | userauth_finish(authctxt, authenticated, "gssapi-with-mic"); | 327 | userauth_finish(authctxt, authenticated, "gssapi-with-mic"); |
293 | } | 328 | } |
294 | 329 | ||
330 | Authmethod method_gsskeyex = { | ||
331 | "gssapi-keyex", | ||
332 | userauth_gsskeyex, | ||
333 | &options.gss_authentication | ||
334 | }; | ||
335 | |||
295 | Authmethod method_gssapi = { | 336 | Authmethod method_gssapi = { |
296 | "gssapi-with-mic", | 337 | "gssapi-with-mic", |
297 | userauth_gssapi, | 338 | userauth_gssapi, |
@@ -69,6 +69,7 @@ extern Authmethod method_passwd; | |||
69 | extern Authmethod method_kbdint; | 69 | extern Authmethod method_kbdint; |
70 | extern Authmethod method_hostbased; | 70 | extern Authmethod method_hostbased; |
71 | #ifdef GSSAPI | 71 | #ifdef GSSAPI |
72 | extern Authmethod method_gsskeyex; | ||
72 | extern Authmethod method_gssapi; | 73 | extern Authmethod method_gssapi; |
73 | #endif | 74 | #endif |
74 | 75 | ||
@@ -76,6 +77,7 @@ Authmethod *authmethods[] = { | |||
76 | &method_none, | 77 | &method_none, |
77 | &method_pubkey, | 78 | &method_pubkey, |
78 | #ifdef GSSAPI | 79 | #ifdef GSSAPI |
80 | &method_gsskeyex, | ||
79 | &method_gssapi, | 81 | &method_gssapi, |
80 | #endif | 82 | #endif |
81 | &method_passwd, | 83 | &method_passwd, |
@@ -264,6 +266,7 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) | |||
264 | #endif | 266 | #endif |
265 | 267 | ||
266 | authctxt->postponed = 0; | 268 | authctxt->postponed = 0; |
269 | authctxt->server_caused_failure = 0; | ||
267 | 270 | ||
268 | /* try to authenticate user */ | 271 | /* try to authenticate user */ |
269 | m = authmethod_lookup(method); | 272 | m = authmethod_lookup(method); |
diff --git a/configure.ac b/configure.ac index fcf7e416b..ba9d9a37a 100644 --- a/configure.ac +++ b/configure.ac | |||
@@ -473,6 +473,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) | |||
473 | [Use tunnel device compatibility to OpenBSD]) | 473 | [Use tunnel device compatibility to OpenBSD]) |
474 | AC_DEFINE(SSH_TUN_PREPEND_AF, 1, | 474 | AC_DEFINE(SSH_TUN_PREPEND_AF, 1, |
475 | [Prepend the address family to IP tunnel traffic]) | 475 | [Prepend the address family to IP tunnel traffic]) |
476 | AC_MSG_CHECKING(if we have the Security Authorization Session API) | ||
477 | AC_TRY_COMPILE([#include <Security/AuthSession.h>], | ||
478 | [SessionCreate(0, 0);], | ||
479 | [ac_cv_use_security_session_api="yes" | ||
480 | AC_DEFINE(USE_SECURITY_SESSION_API, 1, | ||
481 | [platform has the Security Authorization Session API]) | ||
482 | LIBS="$LIBS -framework Security" | ||
483 | AC_MSG_RESULT(yes)], | ||
484 | [ac_cv_use_security_session_api="no" | ||
485 | AC_MSG_RESULT(no)]) | ||
486 | AC_MSG_CHECKING(if we have an in-memory credentials cache) | ||
487 | AC_TRY_COMPILE( | ||
488 | [#include <Kerberos/Kerberos.h>], | ||
489 | [cc_context_t c; | ||
490 | (void) cc_initialize (&c, 0, NULL, NULL);], | ||
491 | [AC_DEFINE(USE_CCAPI, 1, | ||
492 | [platform uses an in-memory credentials cache]) | ||
493 | LIBS="$LIBS -framework Security" | ||
494 | AC_MSG_RESULT(yes) | ||
495 | if test "x$ac_cv_use_security_session_api" = "xno"; then | ||
496 | AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***) | ||
497 | fi], | ||
498 | [AC_MSG_RESULT(no)] | ||
499 | ) | ||
476 | m4_pattern_allow(AU_IPv) | 500 | m4_pattern_allow(AU_IPv) |
477 | AC_CHECK_DECL(AU_IPv4, [], | 501 | AC_CHECK_DECL(AU_IPv4, [], |
478 | AC_DEFINE(AU_IPv4, 0, [System only supports IPv4 audit records]) | 502 | AC_DEFINE(AU_IPv4, 0, [System only supports IPv4 audit records]) |
diff --git a/gss-genr.c b/gss-genr.c index e9190575d..822a08212 100644 --- a/gss-genr.c +++ b/gss-genr.c | |||
@@ -39,12 +39,160 @@ | |||
39 | #include "buffer.h" | 39 | #include "buffer.h" |
40 | #include "log.h" | 40 | #include "log.h" |
41 | #include "ssh2.h" | 41 | #include "ssh2.h" |
42 | #include "cipher.h" | ||
43 | #include "key.h" | ||
44 | #include "kex.h" | ||
45 | #include <openssl/evp.h> | ||
42 | 46 | ||
43 | #include "ssh-gss.h" | 47 | #include "ssh-gss.h" |
44 | 48 | ||
45 | extern u_char *session_id2; | 49 | extern u_char *session_id2; |
46 | extern u_int session_id2_len; | 50 | extern u_int session_id2_len; |
47 | 51 | ||
52 | typedef struct { | ||
53 | char *encoded; | ||
54 | gss_OID oid; | ||
55 | } ssh_gss_kex_mapping; | ||
56 | |||
57 | /* | ||
58 | * XXX - It would be nice to find a more elegant way of handling the | ||
59 | * XXX passing of the key exchange context to the userauth routines | ||
60 | */ | ||
61 | |||
62 | Gssctxt *gss_kex_context = NULL; | ||
63 | |||
64 | static ssh_gss_kex_mapping *gss_enc2oid = NULL; | ||
65 | |||
66 | int | ||
67 | ssh_gssapi_oid_table_ok() { | ||
68 | return (gss_enc2oid != NULL); | ||
69 | } | ||
70 | |||
71 | /* | ||
72 | * Return a list of the gss-group1-sha1 mechanisms supported by this program | ||
73 | * | ||
74 | * We test mechanisms to ensure that we can use them, to avoid starting | ||
75 | * a key exchange with a bad mechanism | ||
76 | */ | ||
77 | |||
78 | char * | ||
79 | ssh_gssapi_client_mechanisms(const char *host) { | ||
80 | gss_OID_set gss_supported; | ||
81 | OM_uint32 min_status; | ||
82 | |||
83 | gss_indicate_mechs(&min_status, &gss_supported); | ||
84 | |||
85 | return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, | ||
86 | host)); | ||
87 | } | ||
88 | |||
89 | char * | ||
90 | ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, | ||
91 | const char *data) { | ||
92 | Buffer buf; | ||
93 | size_t i; | ||
94 | int oidpos, enclen; | ||
95 | char *mechs, *encoded; | ||
96 | u_char digest[EVP_MAX_MD_SIZE]; | ||
97 | char deroid[2]; | ||
98 | const EVP_MD *evp_md = EVP_md5(); | ||
99 | EVP_MD_CTX md; | ||
100 | |||
101 | if (gss_enc2oid != NULL) { | ||
102 | for (i = 0; gss_enc2oid[i].encoded != NULL; i++) | ||
103 | xfree(gss_enc2oid[i].encoded); | ||
104 | xfree(gss_enc2oid); | ||
105 | } | ||
106 | |||
107 | gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * | ||
108 | (gss_supported->count + 1)); | ||
109 | |||
110 | buffer_init(&buf); | ||
111 | |||
112 | oidpos = 0; | ||
113 | for (i = 0; i < gss_supported->count; i++) { | ||
114 | if (gss_supported->elements[i].length < 128 && | ||
115 | (*check)(NULL, &(gss_supported->elements[i]), data)) { | ||
116 | |||
117 | deroid[0] = SSH_GSS_OIDTYPE; | ||
118 | deroid[1] = gss_supported->elements[i].length; | ||
119 | |||
120 | EVP_DigestInit(&md, evp_md); | ||
121 | EVP_DigestUpdate(&md, deroid, 2); | ||
122 | EVP_DigestUpdate(&md, | ||
123 | gss_supported->elements[i].elements, | ||
124 | gss_supported->elements[i].length); | ||
125 | EVP_DigestFinal(&md, digest, NULL); | ||
126 | |||
127 | encoded = xmalloc(EVP_MD_size(evp_md) * 2); | ||
128 | enclen = __b64_ntop(digest, EVP_MD_size(evp_md), | ||
129 | encoded, EVP_MD_size(evp_md) * 2); | ||
130 | |||
131 | if (oidpos != 0) | ||
132 | buffer_put_char(&buf, ','); | ||
133 | |||
134 | buffer_append(&buf, KEX_GSS_GEX_SHA1_ID, | ||
135 | sizeof(KEX_GSS_GEX_SHA1_ID) - 1); | ||
136 | buffer_append(&buf, encoded, enclen); | ||
137 | buffer_put_char(&buf, ','); | ||
138 | buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, | ||
139 | sizeof(KEX_GSS_GRP1_SHA1_ID) - 1); | ||
140 | buffer_append(&buf, encoded, enclen); | ||
141 | buffer_put_char(&buf, ','); | ||
142 | buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID, | ||
143 | sizeof(KEX_GSS_GRP14_SHA1_ID) - 1); | ||
144 | buffer_append(&buf, encoded, enclen); | ||
145 | |||
146 | gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); | ||
147 | gss_enc2oid[oidpos].encoded = encoded; | ||
148 | oidpos++; | ||
149 | } | ||
150 | } | ||
151 | gss_enc2oid[oidpos].oid = NULL; | ||
152 | gss_enc2oid[oidpos].encoded = NULL; | ||
153 | |||
154 | buffer_put_char(&buf, '\0'); | ||
155 | |||
156 | mechs = xmalloc(buffer_len(&buf)); | ||
157 | buffer_get(&buf, mechs, buffer_len(&buf)); | ||
158 | buffer_free(&buf); | ||
159 | |||
160 | if (strlen(mechs) == 0) { | ||
161 | xfree(mechs); | ||
162 | mechs = NULL; | ||
163 | } | ||
164 | |||
165 | return (mechs); | ||
166 | } | ||
167 | |||
168 | gss_OID | ||
169 | ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { | ||
170 | int i = 0; | ||
171 | |||
172 | switch (kex_type) { | ||
173 | case KEX_GSS_GRP1_SHA1: | ||
174 | name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; | ||
175 | break; | ||
176 | case KEX_GSS_GRP14_SHA1: | ||
177 | name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; | ||
178 | break; | ||
179 | case KEX_GSS_GEX_SHA1: | ||
180 | name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; | ||
181 | break; | ||
182 | default: | ||
183 | return GSS_C_NO_OID; | ||
184 | } | ||
185 | |||
186 | while (gss_enc2oid[i].encoded != NULL && | ||
187 | strcmp(name, gss_enc2oid[i].encoded) != 0) | ||
188 | i++; | ||
189 | |||
190 | if (gss_enc2oid[i].oid != NULL && ctx != NULL) | ||
191 | ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); | ||
192 | |||
193 | return gss_enc2oid[i].oid; | ||
194 | } | ||
195 | |||
48 | /* Check that the OID in a data stream matches that in the context */ | 196 | /* Check that the OID in a data stream matches that in the context */ |
49 | int | 197 | int |
50 | ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) | 198 | ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) |
@@ -229,6 +377,9 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host) | |||
229 | OM_uint32 | 377 | OM_uint32 |
230 | ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) | 378 | ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) |
231 | { | 379 | { |
380 | if (ctx == NULL) | ||
381 | return -1; | ||
382 | |||
232 | if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, | 383 | if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, |
233 | GSS_C_QOP_DEFAULT, buffer, hash))) | 384 | GSS_C_QOP_DEFAULT, buffer, hash))) |
234 | ssh_gssapi_error(ctx); | 385 | ssh_gssapi_error(ctx); |
@@ -236,6 +387,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) | |||
236 | return (ctx->major); | 387 | return (ctx->major); |
237 | } | 388 | } |
238 | 389 | ||
390 | /* Priviledged when used by server */ | ||
391 | OM_uint32 | ||
392 | ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) | ||
393 | { | ||
394 | if (ctx == NULL) | ||
395 | return -1; | ||
396 | |||
397 | ctx->major = gss_verify_mic(&ctx->minor, ctx->context, | ||
398 | gssbuf, gssmic, NULL); | ||
399 | |||
400 | return (ctx->major); | ||
401 | } | ||
402 | |||
239 | void | 403 | void |
240 | ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, | 404 | ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, |
241 | const char *context) | 405 | const char *context) |
@@ -254,6 +418,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) | |||
254 | gss_buffer_desc token = GSS_C_EMPTY_BUFFER; | 418 | gss_buffer_desc token = GSS_C_EMPTY_BUFFER; |
255 | OM_uint32 major, minor; | 419 | OM_uint32 major, minor; |
256 | gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; | 420 | gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; |
421 | Gssctxt *intctx = NULL; | ||
422 | |||
423 | if (ctx == NULL) | ||
424 | ctx = &intctx; | ||
257 | 425 | ||
258 | /* RFC 4462 says we MUST NOT do SPNEGO */ | 426 | /* RFC 4462 says we MUST NOT do SPNEGO */ |
259 | if (oid->length == spnego_oid.length && | 427 | if (oid->length == spnego_oid.length && |
@@ -272,7 +440,7 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) | |||
272 | GSS_C_NO_BUFFER); | 440 | GSS_C_NO_BUFFER); |
273 | } | 441 | } |
274 | 442 | ||
275 | if (GSS_ERROR(major)) | 443 | if (GSS_ERROR(major) || intctx != NULL) |
276 | ssh_gssapi_delete_ctx(ctx); | 444 | ssh_gssapi_delete_ctx(ctx); |
277 | 445 | ||
278 | return (!GSS_ERROR(major)); | 446 | return (!GSS_ERROR(major)); |
diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c index 5a625acb8..b400081f6 100644 --- a/gss-serv-krb5.c +++ b/gss-serv-krb5.c | |||
@@ -120,6 +120,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) | |||
120 | krb5_principal princ; | 120 | krb5_principal princ; |
121 | OM_uint32 maj_status, min_status; | 121 | OM_uint32 maj_status, min_status; |
122 | int len; | 122 | int len; |
123 | const char *new_ccname; | ||
123 | 124 | ||
124 | if (client->creds == NULL) { | 125 | if (client->creds == NULL) { |
125 | debug("No credentials stored"); | 126 | debug("No credentials stored"); |
@@ -168,11 +169,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) | |||
168 | return; | 169 | return; |
169 | } | 170 | } |
170 | 171 | ||
171 | client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); | 172 | new_ccname = krb5_cc_get_name(krb_context, ccache); |
173 | |||
172 | client->store.envvar = "KRB5CCNAME"; | 174 | client->store.envvar = "KRB5CCNAME"; |
173 | len = strlen(client->store.filename) + 6; | 175 | #ifdef USE_CCAPI |
174 | client->store.envval = xmalloc(len); | 176 | xasprintf(&client->store.envval, "API:%s", new_ccname); |
175 | snprintf(client->store.envval, len, "FILE:%s", client->store.filename); | 177 | client->store.filename = NULL; |
178 | #else | ||
179 | xasprintf(&client->store.envval, "FILE:%s", new_ccname); | ||
180 | client->store.filename = xstrdup(new_ccname); | ||
181 | #endif | ||
176 | 182 | ||
177 | #ifdef USE_PAM | 183 | #ifdef USE_PAM |
178 | if (options.use_pam) | 184 | if (options.use_pam) |
diff --git a/gss-serv.c b/gss-serv.c index 2ec7ea19c..9227b797c 100644 --- a/gss-serv.c +++ b/gss-serv.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* $OpenBSD: gss-serv.c,v 1.22 2008/05/08 12:02:23 djm Exp $ */ | 1 | /* $OpenBSD: gss-serv.c,v 1.22 2008/05/08 12:02:23 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. | 4 | * Copyright (c) 2001-2008 Simon Wilkinson. All rights reserved. |
5 | * | 5 | * |
6 | * Redistribution and use in source and binary forms, with or without | 6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions | 7 | * modification, are permitted provided that the following conditions |
@@ -45,8 +45,12 @@ | |||
45 | #include "channels.h" | 45 | #include "channels.h" |
46 | #include "session.h" | 46 | #include "session.h" |
47 | #include "misc.h" | 47 | #include "misc.h" |
48 | #include "servconf.h" | ||
48 | 49 | ||
49 | #include "ssh-gss.h" | 50 | #include "ssh-gss.h" |
51 | #include "monitor_wrap.h" | ||
52 | |||
53 | extern ServerOptions options; | ||
50 | 54 | ||
51 | static ssh_gssapi_client gssapi_client = | 55 | static ssh_gssapi_client gssapi_client = |
52 | { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, | 56 | { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, |
@@ -81,25 +85,32 @@ ssh_gssapi_acquire_cred(Gssctxt *ctx) | |||
81 | char lname[MAXHOSTNAMELEN]; | 85 | char lname[MAXHOSTNAMELEN]; |
82 | gss_OID_set oidset; | 86 | gss_OID_set oidset; |
83 | 87 | ||
84 | gss_create_empty_oid_set(&status, &oidset); | 88 | if (options.gss_strict_acceptor) { |
85 | gss_add_oid_set_member(&status, ctx->oid, &oidset); | 89 | gss_create_empty_oid_set(&status, &oidset); |
90 | gss_add_oid_set_member(&status, ctx->oid, &oidset); | ||
86 | 91 | ||
87 | if (gethostname(lname, MAXHOSTNAMELEN)) { | 92 | if (gethostname(lname, MAXHOSTNAMELEN)) { |
88 | gss_release_oid_set(&status, &oidset); | 93 | gss_release_oid_set(&status, &oidset); |
89 | return (-1); | 94 | return (-1); |
90 | } | 95 | } |
96 | |||
97 | if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { | ||
98 | gss_release_oid_set(&status, &oidset); | ||
99 | return (ctx->major); | ||
100 | } | ||
101 | |||
102 | if ((ctx->major = gss_acquire_cred(&ctx->minor, | ||
103 | ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, | ||
104 | NULL, NULL))) | ||
105 | ssh_gssapi_error(ctx); | ||
91 | 106 | ||
92 | if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { | ||
93 | gss_release_oid_set(&status, &oidset); | 107 | gss_release_oid_set(&status, &oidset); |
94 | return (ctx->major); | 108 | return (ctx->major); |
109 | } else { | ||
110 | ctx->name = GSS_C_NO_NAME; | ||
111 | ctx->creds = GSS_C_NO_CREDENTIAL; | ||
95 | } | 112 | } |
96 | 113 | return GSS_S_COMPLETE; | |
97 | if ((ctx->major = gss_acquire_cred(&ctx->minor, | ||
98 | ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL))) | ||
99 | ssh_gssapi_error(ctx); | ||
100 | |||
101 | gss_release_oid_set(&status, &oidset); | ||
102 | return (ctx->major); | ||
103 | } | 114 | } |
104 | 115 | ||
105 | /* Privileged */ | 116 | /* Privileged */ |
@@ -114,6 +125,28 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) | |||
114 | } | 125 | } |
115 | 126 | ||
116 | /* Unprivileged */ | 127 | /* Unprivileged */ |
128 | char * | ||
129 | ssh_gssapi_server_mechanisms() { | ||
130 | gss_OID_set supported; | ||
131 | |||
132 | ssh_gssapi_supported_oids(&supported); | ||
133 | return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech, | ||
134 | NULL)); | ||
135 | } | ||
136 | |||
137 | /* Unprivileged */ | ||
138 | int | ||
139 | ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data) { | ||
140 | Gssctxt *ctx = NULL; | ||
141 | int res; | ||
142 | |||
143 | res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); | ||
144 | ssh_gssapi_delete_ctx(&ctx); | ||
145 | |||
146 | return (res); | ||
147 | } | ||
148 | |||
149 | /* Unprivileged */ | ||
117 | void | 150 | void |
118 | ssh_gssapi_supported_oids(gss_OID_set *oidset) | 151 | ssh_gssapi_supported_oids(gss_OID_set *oidset) |
119 | { | 152 | { |
@@ -352,14 +385,4 @@ ssh_gssapi_userok(char *user) | |||
352 | return (0); | 385 | return (0); |
353 | } | 386 | } |
354 | 387 | ||
355 | /* Privileged */ | ||
356 | OM_uint32 | ||
357 | ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) | ||
358 | { | ||
359 | ctx->major = gss_verify_mic(&ctx->minor, ctx->context, | ||
360 | gssbuf, gssmic, NULL); | ||
361 | |||
362 | return (ctx->major); | ||
363 | } | ||
364 | |||
365 | #endif | 388 | #endif |
@@ -49,6 +49,10 @@ | |||
49 | #include "dispatch.h" | 49 | #include "dispatch.h" |
50 | #include "monitor.h" | 50 | #include "monitor.h" |
51 | 51 | ||
52 | #ifdef GSSAPI | ||
53 | #include "ssh-gss.h" | ||
54 | #endif | ||
55 | |||
52 | #define KEX_COOKIE_LEN 16 | 56 | #define KEX_COOKIE_LEN 16 |
53 | 57 | ||
54 | #if OPENSSL_VERSION_NUMBER >= 0x00907000L | 58 | #if OPENSSL_VERSION_NUMBER >= 0x00907000L |
@@ -327,6 +331,20 @@ choose_kex(Kex *k, char *client, char *server) | |||
327 | k->kex_type = KEX_DH_GEX_SHA256; | 331 | k->kex_type = KEX_DH_GEX_SHA256; |
328 | k->evp_md = evp_ssh_sha256(); | 332 | k->evp_md = evp_ssh_sha256(); |
329 | #endif | 333 | #endif |
334 | #ifdef GSSAPI | ||
335 | } else if (strncmp(k->name, KEX_GSS_GEX_SHA1_ID, | ||
336 | sizeof(KEX_GSS_GEX_SHA1_ID) - 1) == 0) { | ||
337 | k->kex_type = KEX_GSS_GEX_SHA1; | ||
338 | k->evp_md = EVP_sha1(); | ||
339 | } else if (strncmp(k->name, KEX_GSS_GRP1_SHA1_ID, | ||
340 | sizeof(KEX_GSS_GRP1_SHA1_ID) - 1) == 0) { | ||
341 | k->kex_type = KEX_GSS_GRP1_SHA1; | ||
342 | k->evp_md = EVP_sha1(); | ||
343 | } else if (strncmp(k->name, KEX_GSS_GRP14_SHA1_ID, | ||
344 | sizeof(KEX_GSS_GRP14_SHA1_ID) - 1) == 0) { | ||
345 | k->kex_type = KEX_GSS_GRP14_SHA1; | ||
346 | k->evp_md = EVP_sha1(); | ||
347 | #endif | ||
330 | } else | 348 | } else |
331 | fatal("bad kex alg %s", k->name); | 349 | fatal("bad kex alg %s", k->name); |
332 | } | 350 | } |
@@ -64,6 +64,9 @@ enum kex_exchange { | |||
64 | KEX_DH_GRP14_SHA1, | 64 | KEX_DH_GRP14_SHA1, |
65 | KEX_DH_GEX_SHA1, | 65 | KEX_DH_GEX_SHA1, |
66 | KEX_DH_GEX_SHA256, | 66 | KEX_DH_GEX_SHA256, |
67 | KEX_GSS_GRP1_SHA1, | ||
68 | KEX_GSS_GRP14_SHA1, | ||
69 | KEX_GSS_GEX_SHA1, | ||
67 | KEX_MAX | 70 | KEX_MAX |
68 | }; | 71 | }; |
69 | 72 | ||
@@ -119,6 +122,11 @@ struct Kex { | |||
119 | sig_atomic_t done; | 122 | sig_atomic_t done; |
120 | int flags; | 123 | int flags; |
121 | const EVP_MD *evp_md; | 124 | const EVP_MD *evp_md; |
125 | #ifdef GSSAPI | ||
126 | int gss_deleg_creds; | ||
127 | int gss_trust_dns; | ||
128 | char *gss_host; | ||
129 | #endif | ||
122 | char *client_version_string; | 130 | char *client_version_string; |
123 | char *server_version_string; | 131 | char *server_version_string; |
124 | int (*verify_host_key)(Key *); | 132 | int (*verify_host_key)(Key *); |
@@ -141,6 +149,11 @@ void kexdh_server(Kex *); | |||
141 | void kexgex_client(Kex *); | 149 | void kexgex_client(Kex *); |
142 | void kexgex_server(Kex *); | 150 | void kexgex_server(Kex *); |
143 | 151 | ||
152 | #ifdef GSSAPI | ||
153 | void kexgss_client(Kex *); | ||
154 | void kexgss_server(Kex *); | ||
155 | #endif | ||
156 | |||
144 | void | 157 | void |
145 | kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int, | 158 | kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int, |
146 | BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *); | 159 | BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *); |
diff --git a/kexgssc.c b/kexgssc.c new file mode 100644 index 000000000..7c4a56f45 --- /dev/null +++ b/kexgssc.c | |||
@@ -0,0 +1,319 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001-2006 Simon Wilkinson. All rights reserved. | ||
3 | * | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * 1. Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * 2. Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * | ||
13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR | ||
14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
23 | */ | ||
24 | |||
25 | #include "includes.h" | ||
26 | |||
27 | #ifdef GSSAPI | ||
28 | |||
29 | #include "includes.h" | ||
30 | |||
31 | #include <openssl/crypto.h> | ||
32 | #include <openssl/bn.h> | ||
33 | |||
34 | #include <string.h> | ||
35 | |||
36 | #include "xmalloc.h" | ||
37 | #include "buffer.h" | ||
38 | #include "ssh2.h" | ||
39 | #include "key.h" | ||
40 | #include "cipher.h" | ||
41 | #include "kex.h" | ||
42 | #include "log.h" | ||
43 | #include "packet.h" | ||
44 | #include "dh.h" | ||
45 | |||
46 | #include "ssh-gss.h" | ||
47 | |||
48 | void | ||
49 | kexgss_client(Kex *kex) { | ||
50 | gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; | ||
51 | gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; | ||
52 | Gssctxt *ctxt; | ||
53 | OM_uint32 maj_status, min_status, ret_flags; | ||
54 | u_int klen, kout, slen = 0, hashlen, strlen; | ||
55 | DH *dh; | ||
56 | BIGNUM *dh_server_pub = NULL; | ||
57 | BIGNUM *shared_secret = NULL; | ||
58 | BIGNUM *p = NULL; | ||
59 | BIGNUM *g = NULL; | ||
60 | u_char *kbuf, *hash; | ||
61 | u_char *serverhostkey = NULL; | ||
62 | char *msg; | ||
63 | char *lang; | ||
64 | int type = 0; | ||
65 | int first = 1; | ||
66 | int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; | ||
67 | |||
68 | /* Initialise our GSSAPI world */ | ||
69 | ssh_gssapi_build_ctx(&ctxt); | ||
70 | if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) | ||
71 | == GSS_C_NO_OID) | ||
72 | fatal("Couldn't identify host exchange"); | ||
73 | |||
74 | if (ssh_gssapi_import_name(ctxt, kex->gss_host)) | ||
75 | fatal("Couldn't import hostname"); | ||
76 | |||
77 | switch (kex->kex_type) { | ||
78 | case KEX_GSS_GRP1_SHA1: | ||
79 | dh = dh_new_group1(); | ||
80 | break; | ||
81 | case KEX_GSS_GRP14_SHA1: | ||
82 | dh = dh_new_group14(); | ||
83 | break; | ||
84 | case KEX_GSS_GEX_SHA1: | ||
85 | debug("Doing group exchange\n"); | ||
86 | nbits = dh_estimate(kex->we_need * 8); | ||
87 | packet_start(SSH2_MSG_KEXGSS_GROUPREQ); | ||
88 | packet_put_int(min); | ||
89 | packet_put_int(nbits); | ||
90 | packet_put_int(max); | ||
91 | |||
92 | packet_send(); | ||
93 | |||
94 | packet_read_expect(SSH2_MSG_KEXGSS_GROUP); | ||
95 | |||
96 | if ((p = BN_new()) == NULL) | ||
97 | fatal("BN_new() failed"); | ||
98 | packet_get_bignum2(p); | ||
99 | if ((g = BN_new()) == NULL) | ||
100 | fatal("BN_new() failed"); | ||
101 | packet_get_bignum2(g); | ||
102 | packet_check_eom(); | ||
103 | |||
104 | if (BN_num_bits(p) < min || BN_num_bits(p) > max) | ||
105 | fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", | ||
106 | min, BN_num_bits(p), max); | ||
107 | |||
108 | dh = dh_new_group(g, p); | ||
109 | break; | ||
110 | default: | ||
111 | fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); | ||
112 | } | ||
113 | |||
114 | /* Step 1 - e is dh->pub_key */ | ||
115 | dh_gen_key(dh, kex->we_need * 8); | ||
116 | |||
117 | /* This is f, we initialise it now to make life easier */ | ||
118 | dh_server_pub = BN_new(); | ||
119 | if (dh_server_pub == NULL) | ||
120 | fatal("dh_server_pub == NULL"); | ||
121 | |||
122 | token_ptr = GSS_C_NO_BUFFER; | ||
123 | |||
124 | do { | ||
125 | debug("Calling gss_init_sec_context"); | ||
126 | |||
127 | maj_status = ssh_gssapi_init_ctx(ctxt, | ||
128 | kex->gss_deleg_creds, token_ptr, &send_tok, | ||
129 | &ret_flags); | ||
130 | |||
131 | if (GSS_ERROR(maj_status)) { | ||
132 | if (send_tok.length != 0) { | ||
133 | packet_start(SSH2_MSG_KEXGSS_CONTINUE); | ||
134 | packet_put_string(send_tok.value, | ||
135 | send_tok.length); | ||
136 | } | ||
137 | fatal("gss_init_context failed"); | ||
138 | } | ||
139 | |||
140 | /* If we've got an old receive buffer get rid of it */ | ||
141 | if (token_ptr != GSS_C_NO_BUFFER) | ||
142 | xfree(recv_tok.value); | ||
143 | |||
144 | if (maj_status == GSS_S_COMPLETE) { | ||
145 | /* If mutual state flag is not true, kex fails */ | ||
146 | if (!(ret_flags & GSS_C_MUTUAL_FLAG)) | ||
147 | fatal("Mutual authentication failed"); | ||
148 | |||
149 | /* If integ avail flag is not true kex fails */ | ||
150 | if (!(ret_flags & GSS_C_INTEG_FLAG)) | ||
151 | fatal("Integrity check failed"); | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * If we have data to send, then the last message that we | ||
156 | * received cannot have been a 'complete'. | ||
157 | */ | ||
158 | if (send_tok.length != 0) { | ||
159 | if (first) { | ||
160 | packet_start(SSH2_MSG_KEXGSS_INIT); | ||
161 | packet_put_string(send_tok.value, | ||
162 | send_tok.length); | ||
163 | packet_put_bignum2(dh->pub_key); | ||
164 | first = 0; | ||
165 | } else { | ||
166 | packet_start(SSH2_MSG_KEXGSS_CONTINUE); | ||
167 | packet_put_string(send_tok.value, | ||
168 | send_tok.length); | ||
169 | } | ||
170 | packet_send(); | ||
171 | gss_release_buffer(&min_status, &send_tok); | ||
172 | |||
173 | /* If we've sent them data, they should reply */ | ||
174 | do { | ||
175 | type = packet_read(); | ||
176 | if (type == SSH2_MSG_KEXGSS_HOSTKEY) { | ||
177 | debug("Received KEXGSS_HOSTKEY"); | ||
178 | if (serverhostkey) | ||
179 | fatal("Server host key received more than once"); | ||
180 | serverhostkey = | ||
181 | packet_get_string(&slen); | ||
182 | } | ||
183 | } while (type == SSH2_MSG_KEXGSS_HOSTKEY); | ||
184 | |||
185 | switch (type) { | ||
186 | case SSH2_MSG_KEXGSS_CONTINUE: | ||
187 | debug("Received GSSAPI_CONTINUE"); | ||
188 | if (maj_status == GSS_S_COMPLETE) | ||
189 | fatal("GSSAPI Continue received from server when complete"); | ||
190 | recv_tok.value = packet_get_string(&strlen); | ||
191 | recv_tok.length = strlen; | ||
192 | break; | ||
193 | case SSH2_MSG_KEXGSS_COMPLETE: | ||
194 | debug("Received GSSAPI_COMPLETE"); | ||
195 | packet_get_bignum2(dh_server_pub); | ||
196 | msg_tok.value = packet_get_string(&strlen); | ||
197 | msg_tok.length = strlen; | ||
198 | |||
199 | /* Is there a token included? */ | ||
200 | if (packet_get_char()) { | ||
201 | recv_tok.value= | ||
202 | packet_get_string(&strlen); | ||
203 | recv_tok.length = strlen; | ||
204 | /* If we're already complete - protocol error */ | ||
205 | if (maj_status == GSS_S_COMPLETE) | ||
206 | packet_disconnect("Protocol error: received token when complete"); | ||
207 | } else { | ||
208 | /* No token included */ | ||
209 | if (maj_status != GSS_S_COMPLETE) | ||
210 | packet_disconnect("Protocol error: did not receive final token"); | ||
211 | } | ||
212 | break; | ||
213 | case SSH2_MSG_KEXGSS_ERROR: | ||
214 | debug("Received Error"); | ||
215 | maj_status = packet_get_int(); | ||
216 | min_status = packet_get_int(); | ||
217 | msg = packet_get_string(NULL); | ||
218 | lang = packet_get_string(NULL); | ||
219 | fatal("GSSAPI Error: \n%.400s",msg); | ||
220 | default: | ||
221 | packet_disconnect("Protocol error: didn't expect packet type %d", | ||
222 | type); | ||
223 | } | ||
224 | token_ptr = &recv_tok; | ||
225 | } else { | ||
226 | /* No data, and not complete */ | ||
227 | if (maj_status != GSS_S_COMPLETE) | ||
228 | fatal("Not complete, and no token output"); | ||
229 | } | ||
230 | } while (maj_status & GSS_S_CONTINUE_NEEDED); | ||
231 | |||
232 | /* | ||
233 | * We _must_ have received a COMPLETE message in reply from the | ||
234 | * server, which will have set dh_server_pub and msg_tok | ||
235 | */ | ||
236 | |||
237 | if (type != SSH2_MSG_KEXGSS_COMPLETE) | ||
238 | fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); | ||
239 | |||
240 | /* Check f in range [1, p-1] */ | ||
241 | if (!dh_pub_is_valid(dh, dh_server_pub)) | ||
242 | packet_disconnect("bad server public DH value"); | ||
243 | |||
244 | /* compute K=f^x mod p */ | ||
245 | klen = DH_size(dh); | ||
246 | kbuf = xmalloc(klen); | ||
247 | kout = DH_compute_key(kbuf, dh_server_pub, dh); | ||
248 | |||
249 | shared_secret = BN_new(); | ||
250 | BN_bin2bn(kbuf,kout, shared_secret); | ||
251 | memset(kbuf, 0, klen); | ||
252 | xfree(kbuf); | ||
253 | |||
254 | switch (kex->kex_type) { | ||
255 | case KEX_GSS_GRP1_SHA1: | ||
256 | case KEX_GSS_GRP14_SHA1: | ||
257 | kex_dh_hash( kex->client_version_string, | ||
258 | kex->server_version_string, | ||
259 | buffer_ptr(&kex->my), buffer_len(&kex->my), | ||
260 | buffer_ptr(&kex->peer), buffer_len(&kex->peer), | ||
261 | serverhostkey, slen, /* server host key */ | ||
262 | dh->pub_key, /* e */ | ||
263 | dh_server_pub, /* f */ | ||
264 | shared_secret, /* K */ | ||
265 | &hash, &hashlen | ||
266 | ); | ||
267 | break; | ||
268 | case KEX_GSS_GEX_SHA1: | ||
269 | kexgex_hash( | ||
270 | kex->evp_md, | ||
271 | kex->client_version_string, | ||
272 | kex->server_version_string, | ||
273 | buffer_ptr(&kex->my), buffer_len(&kex->my), | ||
274 | buffer_ptr(&kex->peer), buffer_len(&kex->peer), | ||
275 | serverhostkey, slen, | ||
276 | min, nbits, max, | ||
277 | dh->p, dh->g, | ||
278 | dh->pub_key, | ||
279 | dh_server_pub, | ||
280 | shared_secret, | ||
281 | &hash, &hashlen | ||
282 | ); | ||
283 | break; | ||
284 | default: | ||
285 | fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); | ||
286 | } | ||
287 | |||
288 | gssbuf.value = hash; | ||
289 | gssbuf.length = hashlen; | ||
290 | |||
291 | /* Verify that the hash matches the MIC we just got. */ | ||
292 | if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) | ||
293 | packet_disconnect("Hash's MIC didn't verify"); | ||
294 | |||
295 | xfree(msg_tok.value); | ||
296 | |||
297 | DH_free(dh); | ||
298 | if (serverhostkey) | ||
299 | xfree(serverhostkey); | ||
300 | BN_clear_free(dh_server_pub); | ||
301 | |||
302 | /* save session id */ | ||
303 | if (kex->session_id == NULL) { | ||
304 | kex->session_id_len = hashlen; | ||
305 | kex->session_id = xmalloc(kex->session_id_len); | ||
306 | memcpy(kex->session_id, hash, kex->session_id_len); | ||
307 | } | ||
308 | |||
309 | if (gss_kex_context == NULL) | ||
310 | gss_kex_context = ctxt; | ||
311 | else | ||
312 | ssh_gssapi_delete_ctx(&ctxt); | ||
313 | |||
314 | kex_derive_keys(kex, hash, hashlen, shared_secret); | ||
315 | BN_clear_free(shared_secret); | ||
316 | kex_finish(kex); | ||
317 | } | ||
318 | |||
319 | #endif /* GSSAPI */ | ||
diff --git a/kexgsss.c b/kexgsss.c new file mode 100644 index 000000000..3ca23bbb2 --- /dev/null +++ b/kexgsss.c | |||
@@ -0,0 +1,271 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001-2006 Simon Wilkinson. All rights reserved. | ||
3 | * | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * 1. Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * 2. Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * | ||
13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR | ||
14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
23 | */ | ||
24 | |||
25 | #include "includes.h" | ||
26 | |||
27 | #ifdef GSSAPI | ||
28 | |||
29 | #include <string.h> | ||
30 | |||
31 | #include <openssl/crypto.h> | ||
32 | #include <openssl/bn.h> | ||
33 | |||
34 | #include "xmalloc.h" | ||
35 | #include "buffer.h" | ||
36 | #include "ssh2.h" | ||
37 | #include "key.h" | ||
38 | #include "cipher.h" | ||
39 | #include "kex.h" | ||
40 | #include "log.h" | ||
41 | #include "packet.h" | ||
42 | #include "dh.h" | ||
43 | #include "ssh-gss.h" | ||
44 | #include "monitor_wrap.h" | ||
45 | |||
46 | void | ||
47 | kexgss_server(Kex *kex) | ||
48 | { | ||
49 | OM_uint32 maj_status, min_status; | ||
50 | |||
51 | /* | ||
52 | * Some GSSAPI implementations use the input value of ret_flags (an | ||
53 | * output variable) as a means of triggering mechanism specific | ||
54 | * features. Initializing it to zero avoids inadvertently | ||
55 | * activating this non-standard behaviour. | ||
56 | */ | ||
57 | |||
58 | OM_uint32 ret_flags = 0; | ||
59 | gss_buffer_desc gssbuf, recv_tok, msg_tok; | ||
60 | gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; | ||
61 | Gssctxt *ctxt = NULL; | ||
62 | u_int slen, klen, kout, hashlen; | ||
63 | u_char *kbuf, *hash; | ||
64 | DH *dh; | ||
65 | int min = -1, max = -1, nbits = -1; | ||
66 | BIGNUM *shared_secret = NULL; | ||
67 | BIGNUM *dh_client_pub = NULL; | ||
68 | int type = 0; | ||
69 | gss_OID oid; | ||
70 | |||
71 | /* Initialise GSSAPI */ | ||
72 | |||
73 | /* If we're rekeying, privsep means that some of the private structures | ||
74 | * in the GSSAPI code are no longer available. This kludges them back | ||
75 | * into life | ||
76 | */ | ||
77 | if (!ssh_gssapi_oid_table_ok()) | ||
78 | ssh_gssapi_server_mechanisms(); | ||
79 | |||
80 | debug2("%s: Identifying %s", __func__, kex->name); | ||
81 | oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); | ||
82 | if (oid == GSS_C_NO_OID) | ||
83 | fatal("Unknown gssapi mechanism"); | ||
84 | |||
85 | debug2("%s: Acquiring credentials", __func__); | ||
86 | |||
87 | if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) | ||
88 | fatal("Unable to acquire credentials for the server"); | ||
89 | |||
90 | switch (kex->kex_type) { | ||
91 | case KEX_GSS_GRP1_SHA1: | ||
92 | dh = dh_new_group1(); | ||
93 | break; | ||
94 | case KEX_GSS_GRP14_SHA1: | ||
95 | dh = dh_new_group14(); | ||
96 | break; | ||
97 | case KEX_GSS_GEX_SHA1: | ||
98 | debug("Doing group exchange"); | ||
99 | packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); | ||
100 | min = packet_get_int(); | ||
101 | nbits = packet_get_int(); | ||
102 | max = packet_get_int(); | ||
103 | min = MAX(DH_GRP_MIN, min); | ||
104 | max = MIN(DH_GRP_MAX, max); | ||
105 | packet_check_eom(); | ||
106 | if (max < min || nbits < min || max < nbits) | ||
107 | fatal("GSS_GEX, bad parameters: %d !< %d !< %d", | ||
108 | min, nbits, max); | ||
109 | dh = PRIVSEP(choose_dh(min, nbits, max)); | ||
110 | if (dh == NULL) | ||
111 | packet_disconnect("Protocol error: no matching group found"); | ||
112 | |||
113 | packet_start(SSH2_MSG_KEXGSS_GROUP); | ||
114 | packet_put_bignum2(dh->p); | ||
115 | packet_put_bignum2(dh->g); | ||
116 | packet_send(); | ||
117 | |||
118 | packet_write_wait(); | ||
119 | break; | ||
120 | default: | ||
121 | fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); | ||
122 | } | ||
123 | |||
124 | dh_gen_key(dh, kex->we_need * 8); | ||
125 | |||
126 | do { | ||
127 | debug("Wait SSH2_MSG_GSSAPI_INIT"); | ||
128 | type = packet_read(); | ||
129 | switch(type) { | ||
130 | case SSH2_MSG_KEXGSS_INIT: | ||
131 | if (dh_client_pub != NULL) | ||
132 | fatal("Received KEXGSS_INIT after initialising"); | ||
133 | recv_tok.value = packet_get_string(&slen); | ||
134 | recv_tok.length = slen; | ||
135 | |||
136 | if ((dh_client_pub = BN_new()) == NULL) | ||
137 | fatal("dh_client_pub == NULL"); | ||
138 | |||
139 | packet_get_bignum2(dh_client_pub); | ||
140 | |||
141 | /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ | ||
142 | break; | ||
143 | case SSH2_MSG_KEXGSS_CONTINUE: | ||
144 | recv_tok.value = packet_get_string(&slen); | ||
145 | recv_tok.length = slen; | ||
146 | break; | ||
147 | default: | ||
148 | packet_disconnect( | ||
149 | "Protocol error: didn't expect packet type %d", | ||
150 | type); | ||
151 | } | ||
152 | |||
153 | maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, | ||
154 | &send_tok, &ret_flags)); | ||
155 | |||
156 | xfree(recv_tok.value); | ||
157 | |||
158 | if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) | ||
159 | fatal("Zero length token output when incomplete"); | ||
160 | |||
161 | if (dh_client_pub == NULL) | ||
162 | fatal("No client public key"); | ||
163 | |||
164 | if (maj_status & GSS_S_CONTINUE_NEEDED) { | ||
165 | debug("Sending GSSAPI_CONTINUE"); | ||
166 | packet_start(SSH2_MSG_KEXGSS_CONTINUE); | ||
167 | packet_put_string(send_tok.value, send_tok.length); | ||
168 | packet_send(); | ||
169 | gss_release_buffer(&min_status, &send_tok); | ||
170 | } | ||
171 | } while (maj_status & GSS_S_CONTINUE_NEEDED); | ||
172 | |||
173 | if (GSS_ERROR(maj_status)) { | ||
174 | if (send_tok.length > 0) { | ||
175 | packet_start(SSH2_MSG_KEXGSS_CONTINUE); | ||
176 | packet_put_string(send_tok.value, send_tok.length); | ||
177 | packet_send(); | ||
178 | } | ||
179 | fatal("accept_ctx died"); | ||
180 | } | ||
181 | |||
182 | if (!(ret_flags & GSS_C_MUTUAL_FLAG)) | ||
183 | fatal("Mutual Authentication flag wasn't set"); | ||
184 | |||
185 | if (!(ret_flags & GSS_C_INTEG_FLAG)) | ||
186 | fatal("Integrity flag wasn't set"); | ||
187 | |||
188 | if (!dh_pub_is_valid(dh, dh_client_pub)) | ||
189 | packet_disconnect("bad client public DH value"); | ||
190 | |||
191 | klen = DH_size(dh); | ||
192 | kbuf = xmalloc(klen); | ||
193 | kout = DH_compute_key(kbuf, dh_client_pub, dh); | ||
194 | |||
195 | shared_secret = BN_new(); | ||
196 | BN_bin2bn(kbuf, kout, shared_secret); | ||
197 | memset(kbuf, 0, klen); | ||
198 | xfree(kbuf); | ||
199 | |||
200 | switch (kex->kex_type) { | ||
201 | case KEX_GSS_GRP1_SHA1: | ||
202 | case KEX_GSS_GRP14_SHA1: | ||
203 | kex_dh_hash( | ||
204 | kex->client_version_string, kex->server_version_string, | ||
205 | buffer_ptr(&kex->peer), buffer_len(&kex->peer), | ||
206 | buffer_ptr(&kex->my), buffer_len(&kex->my), | ||
207 | NULL, 0, /* Change this if we start sending host keys */ | ||
208 | dh_client_pub, dh->pub_key, shared_secret, | ||
209 | &hash, &hashlen | ||
210 | ); | ||
211 | break; | ||
212 | case KEX_GSS_GEX_SHA1: | ||
213 | kexgex_hash( | ||
214 | kex->evp_md, | ||
215 | kex->client_version_string, kex->server_version_string, | ||
216 | buffer_ptr(&kex->peer), buffer_len(&kex->peer), | ||
217 | buffer_ptr(&kex->my), buffer_len(&kex->my), | ||
218 | NULL, 0, | ||
219 | min, nbits, max, | ||
220 | dh->p, dh->g, | ||
221 | dh_client_pub, | ||
222 | dh->pub_key, | ||
223 | shared_secret, | ||
224 | &hash, &hashlen | ||
225 | ); | ||
226 | break; | ||
227 | default: | ||
228 | fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); | ||
229 | } | ||
230 | |||
231 | BN_free(dh_client_pub); | ||
232 | |||
233 | if (kex->session_id == NULL) { | ||
234 | kex->session_id_len = hashlen; | ||
235 | kex->session_id = xmalloc(kex->session_id_len); | ||
236 | memcpy(kex->session_id, hash, kex->session_id_len); | ||
237 | } | ||
238 | |||
239 | gssbuf.value = hash; | ||
240 | gssbuf.length = hashlen; | ||
241 | |||
242 | if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) | ||
243 | fatal("Couldn't get MIC"); | ||
244 | |||
245 | packet_start(SSH2_MSG_KEXGSS_COMPLETE); | ||
246 | packet_put_bignum2(dh->pub_key); | ||
247 | packet_put_string(msg_tok.value,msg_tok.length); | ||
248 | |||
249 | if (send_tok.length != 0) { | ||
250 | packet_put_char(1); /* true */ | ||
251 | packet_put_string(send_tok.value, send_tok.length); | ||
252 | } else { | ||
253 | packet_put_char(0); /* false */ | ||
254 | } | ||
255 | packet_send(); | ||
256 | |||
257 | gss_release_buffer(&min_status, &send_tok); | ||
258 | gss_release_buffer(&min_status, &msg_tok); | ||
259 | |||
260 | if (gss_kex_context == NULL) | ||
261 | gss_kex_context = ctxt; | ||
262 | else | ||
263 | ssh_gssapi_delete_ctx(&ctxt); | ||
264 | |||
265 | DH_free(dh); | ||
266 | |||
267 | kex_derive_keys(kex, hash, hashlen, shared_secret); | ||
268 | BN_clear_free(shared_secret); | ||
269 | kex_finish(kex); | ||
270 | } | ||
271 | #endif /* GSSAPI */ | ||
@@ -763,6 +763,8 @@ key_type_from_name(char *name) | |||
763 | return KEY_RSA; | 763 | return KEY_RSA; |
764 | } else if (strcmp(name, "ssh-dss") == 0) { | 764 | } else if (strcmp(name, "ssh-dss") == 0) { |
765 | return KEY_DSA; | 765 | return KEY_DSA; |
766 | } else if (strcmp(name, "null") == 0) { | ||
767 | return KEY_NULL; | ||
766 | } | 768 | } |
767 | debug2("key_type_from_name: unknown key type '%s'", name); | 769 | debug2("key_type_from_name: unknown key type '%s'", name); |
768 | return KEY_UNSPEC; | 770 | return KEY_UNSPEC; |
@@ -34,6 +34,7 @@ enum types { | |||
34 | KEY_RSA1, | 34 | KEY_RSA1, |
35 | KEY_RSA, | 35 | KEY_RSA, |
36 | KEY_DSA, | 36 | KEY_DSA, |
37 | KEY_NULL, | ||
37 | KEY_UNSPEC | 38 | KEY_UNSPEC |
38 | }; | 39 | }; |
39 | enum fp_type { | 40 | enum fp_type { |
@@ -164,6 +164,7 @@ int mm_answer_gss_setup_ctx(int, Buffer *); | |||
164 | int mm_answer_gss_accept_ctx(int, Buffer *); | 164 | int mm_answer_gss_accept_ctx(int, Buffer *); |
165 | int mm_answer_gss_userok(int, Buffer *); | 165 | int mm_answer_gss_userok(int, Buffer *); |
166 | int mm_answer_gss_checkmic(int, Buffer *); | 166 | int mm_answer_gss_checkmic(int, Buffer *); |
167 | int mm_answer_gss_sign(int, Buffer *); | ||
167 | #endif | 168 | #endif |
168 | 169 | ||
169 | #ifdef SSH_AUDIT_EVENTS | 170 | #ifdef SSH_AUDIT_EVENTS |
@@ -233,11 +234,17 @@ struct mon_table mon_dispatch_proto20[] = { | |||
233 | {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, | 234 | {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, |
234 | {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, | 235 | {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, |
235 | {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, | 236 | {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, |
237 | {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign}, | ||
236 | #endif | 238 | #endif |
237 | {0, 0, NULL} | 239 | {0, 0, NULL} |
238 | }; | 240 | }; |
239 | 241 | ||
240 | struct mon_table mon_dispatch_postauth20[] = { | 242 | struct mon_table mon_dispatch_postauth20[] = { |
243 | #ifdef GSSAPI | ||
244 | {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, | ||
245 | {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, | ||
246 | {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign}, | ||
247 | #endif | ||
241 | {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, | 248 | {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, |
242 | {MONITOR_REQ_SIGN, 0, mm_answer_sign}, | 249 | {MONITOR_REQ_SIGN, 0, mm_answer_sign}, |
243 | {MONITOR_REQ_PTY, 0, mm_answer_pty}, | 250 | {MONITOR_REQ_PTY, 0, mm_answer_pty}, |
@@ -342,6 +349,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) | |||
342 | /* Permit requests for moduli and signatures */ | 349 | /* Permit requests for moduli and signatures */ |
343 | monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); | 350 | monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); |
344 | monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); | 351 | monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); |
352 | #ifdef GSSAPI | ||
353 | /* and for the GSSAPI key exchange */ | ||
354 | monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); | ||
355 | #endif | ||
345 | } else { | 356 | } else { |
346 | mon_dispatch = mon_dispatch_proto15; | 357 | mon_dispatch = mon_dispatch_proto15; |
347 | 358 | ||
@@ -419,6 +430,10 @@ monitor_child_postauth(struct monitor *pmonitor) | |||
419 | monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); | 430 | monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); |
420 | monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); | 431 | monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); |
421 | monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); | 432 | monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); |
433 | #ifdef GSSAPI | ||
434 | /* and for the GSSAPI key exchange */ | ||
435 | monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); | ||
436 | #endif | ||
422 | } else { | 437 | } else { |
423 | mon_dispatch = mon_dispatch_postauth15; | 438 | mon_dispatch = mon_dispatch_postauth15; |
424 | monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); | 439 | monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); |
@@ -1675,6 +1690,11 @@ mm_get_kex(Buffer *m) | |||
1675 | kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; | 1690 | kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; |
1676 | kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; | 1691 | kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; |
1677 | kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; | 1692 | kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; |
1693 | #ifdef GSSAPI | ||
1694 | kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; | ||
1695 | kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; | ||
1696 | kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; | ||
1697 | #endif | ||
1678 | kex->server = 1; | 1698 | kex->server = 1; |
1679 | kex->hostkey_type = buffer_get_int(m); | 1699 | kex->hostkey_type = buffer_get_int(m); |
1680 | kex->kex_type = buffer_get_int(m); | 1700 | kex->kex_type = buffer_get_int(m); |
@@ -1918,6 +1938,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m) | |||
1918 | monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); | 1938 | monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); |
1919 | monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); | 1939 | monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); |
1920 | monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); | 1940 | monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); |
1941 | monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); | ||
1921 | } | 1942 | } |
1922 | return (0); | 1943 | return (0); |
1923 | } | 1944 | } |
@@ -1968,4 +1989,42 @@ mm_answer_gss_userok(int sock, Buffer *m) | |||
1968 | /* Monitor loop will terminate if authenticated */ | 1989 | /* Monitor loop will terminate if authenticated */ |
1969 | return (authenticated); | 1990 | return (authenticated); |
1970 | } | 1991 | } |
1992 | |||
1993 | int | ||
1994 | mm_answer_gss_sign(int socket, Buffer *m) | ||
1995 | { | ||
1996 | gss_buffer_desc data; | ||
1997 | gss_buffer_desc hash = GSS_C_EMPTY_BUFFER; | ||
1998 | OM_uint32 major, minor; | ||
1999 | u_int len; | ||
2000 | |||
2001 | data.value = buffer_get_string(m, &len); | ||
2002 | data.length = len; | ||
2003 | if (data.length != 20) | ||
2004 | fatal("%s: data length incorrect: %d", __func__, data.length); | ||
2005 | |||
2006 | /* Save the session ID on the first time around */ | ||
2007 | if (session_id2_len == 0) { | ||
2008 | session_id2_len = data.length; | ||
2009 | session_id2 = xmalloc(session_id2_len); | ||
2010 | memcpy(session_id2, data.value, session_id2_len); | ||
2011 | } | ||
2012 | major = ssh_gssapi_sign(gsscontext, &data, &hash); | ||
2013 | |||
2014 | xfree(data.value); | ||
2015 | |||
2016 | buffer_clear(m); | ||
2017 | buffer_put_int(m, major); | ||
2018 | buffer_put_string(m, hash.value, hash.length); | ||
2019 | |||
2020 | mm_request_send(socket, MONITOR_ANS_GSSSIGN, m); | ||
2021 | |||
2022 | gss_release_buffer(&minor, &hash); | ||
2023 | |||
2024 | /* Turn on getpwnam permissions */ | ||
2025 | monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); | ||
2026 | |||
2027 | return (0); | ||
2028 | } | ||
2029 | |||
1971 | #endif /* GSSAPI */ | 2030 | #endif /* GSSAPI */ |
@@ -53,6 +53,7 @@ enum monitor_reqtype { | |||
53 | MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP, | 53 | MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP, |
54 | MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK, | 54 | MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK, |
55 | MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC, | 55 | MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC, |
56 | MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN, | ||
56 | MONITOR_REQ_PAM_START, | 57 | MONITOR_REQ_PAM_START, |
57 | MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT, | 58 | MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT, |
58 | MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX, | 59 | MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX, |
diff --git a/monitor_wrap.c b/monitor_wrap.c index 40463d078..b559c77bf 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c | |||
@@ -1255,4 +1255,27 @@ mm_ssh_gssapi_userok(char *user) | |||
1255 | debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); | 1255 | debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); |
1256 | return (authenticated); | 1256 | return (authenticated); |
1257 | } | 1257 | } |
1258 | |||
1259 | OM_uint32 | ||
1260 | mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) | ||
1261 | { | ||
1262 | Buffer m; | ||
1263 | OM_uint32 major; | ||
1264 | u_int len; | ||
1265 | |||
1266 | buffer_init(&m); | ||
1267 | buffer_put_string(&m, data->value, data->length); | ||
1268 | |||
1269 | mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m); | ||
1270 | mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m); | ||
1271 | |||
1272 | major = buffer_get_int(&m); | ||
1273 | hash->value = buffer_get_string(&m, &len); | ||
1274 | hash->length = len; | ||
1275 | |||
1276 | buffer_free(&m); | ||
1277 | |||
1278 | return(major); | ||
1279 | } | ||
1280 | |||
1258 | #endif /* GSSAPI */ | 1281 | #endif /* GSSAPI */ |
diff --git a/monitor_wrap.h b/monitor_wrap.h index 329189c2a..2a7bf7c87 100644 --- a/monitor_wrap.h +++ b/monitor_wrap.h | |||
@@ -59,6 +59,7 @@ OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, | |||
59 | gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); | 59 | gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); |
60 | int mm_ssh_gssapi_userok(char *user); | 60 | int mm_ssh_gssapi_userok(char *user); |
61 | OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); | 61 | OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); |
62 | OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); | ||
62 | #endif | 63 | #endif |
63 | 64 | ||
64 | #ifdef USE_PAM | 65 | #ifdef USE_PAM |
diff --git a/readconf.c b/readconf.c index 73f6eb361..3aedd6f5a 100644 --- a/readconf.c +++ b/readconf.c | |||
@@ -127,6 +127,8 @@ typedef enum { | |||
127 | oClearAllForwardings, oNoHostAuthenticationForLocalhost, | 127 | oClearAllForwardings, oNoHostAuthenticationForLocalhost, |
128 | oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, | 128 | oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, |
129 | oAddressFamily, oGssAuthentication, oGssDelegateCreds, | 129 | oAddressFamily, oGssAuthentication, oGssDelegateCreds, |
130 | oGssKeyEx, | ||
131 | oGssTrustDns, | ||
130 | oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, | 132 | oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, |
131 | oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, | 133 | oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, |
132 | oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, | 134 | oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, |
@@ -164,10 +166,14 @@ static struct { | |||
164 | { "afstokenpassing", oUnsupported }, | 166 | { "afstokenpassing", oUnsupported }, |
165 | #if defined(GSSAPI) | 167 | #if defined(GSSAPI) |
166 | { "gssapiauthentication", oGssAuthentication }, | 168 | { "gssapiauthentication", oGssAuthentication }, |
169 | { "gssapikeyexchange", oGssKeyEx }, | ||
167 | { "gssapidelegatecredentials", oGssDelegateCreds }, | 170 | { "gssapidelegatecredentials", oGssDelegateCreds }, |
171 | { "gssapitrustdns", oGssTrustDns }, | ||
168 | #else | 172 | #else |
169 | { "gssapiauthentication", oUnsupported }, | 173 | { "gssapiauthentication", oUnsupported }, |
174 | { "gssapikeyexchange", oUnsupported }, | ||
170 | { "gssapidelegatecredentials", oUnsupported }, | 175 | { "gssapidelegatecredentials", oUnsupported }, |
176 | { "gssapitrustdns", oUnsupported }, | ||
171 | #endif | 177 | #endif |
172 | { "fallbacktorsh", oDeprecated }, | 178 | { "fallbacktorsh", oDeprecated }, |
173 | { "usersh", oDeprecated }, | 179 | { "usersh", oDeprecated }, |
@@ -444,10 +450,18 @@ parse_flag: | |||
444 | intptr = &options->gss_authentication; | 450 | intptr = &options->gss_authentication; |
445 | goto parse_flag; | 451 | goto parse_flag; |
446 | 452 | ||
453 | case oGssKeyEx: | ||
454 | intptr = &options->gss_keyex; | ||
455 | goto parse_flag; | ||
456 | |||
447 | case oGssDelegateCreds: | 457 | case oGssDelegateCreds: |
448 | intptr = &options->gss_deleg_creds; | 458 | intptr = &options->gss_deleg_creds; |
449 | goto parse_flag; | 459 | goto parse_flag; |
450 | 460 | ||
461 | case oGssTrustDns: | ||
462 | intptr = &options->gss_trust_dns; | ||
463 | goto parse_flag; | ||
464 | |||
451 | case oBatchMode: | 465 | case oBatchMode: |
452 | intptr = &options->batch_mode; | 466 | intptr = &options->batch_mode; |
453 | goto parse_flag; | 467 | goto parse_flag; |
@@ -1016,7 +1030,9 @@ initialize_options(Options * options) | |||
1016 | options->pubkey_authentication = -1; | 1030 | options->pubkey_authentication = -1; |
1017 | options->challenge_response_authentication = -1; | 1031 | options->challenge_response_authentication = -1; |
1018 | options->gss_authentication = -1; | 1032 | options->gss_authentication = -1; |
1033 | options->gss_keyex = -1; | ||
1019 | options->gss_deleg_creds = -1; | 1034 | options->gss_deleg_creds = -1; |
1035 | options->gss_trust_dns = -1; | ||
1020 | options->password_authentication = -1; | 1036 | options->password_authentication = -1; |
1021 | options->kbd_interactive_authentication = -1; | 1037 | options->kbd_interactive_authentication = -1; |
1022 | options->kbd_interactive_devices = NULL; | 1038 | options->kbd_interactive_devices = NULL; |
@@ -1106,8 +1122,12 @@ fill_default_options(Options * options) | |||
1106 | options->challenge_response_authentication = 1; | 1122 | options->challenge_response_authentication = 1; |
1107 | if (options->gss_authentication == -1) | 1123 | if (options->gss_authentication == -1) |
1108 | options->gss_authentication = 0; | 1124 | options->gss_authentication = 0; |
1125 | if (options->gss_keyex == -1) | ||
1126 | options->gss_keyex = 0; | ||
1109 | if (options->gss_deleg_creds == -1) | 1127 | if (options->gss_deleg_creds == -1) |
1110 | options->gss_deleg_creds = 0; | 1128 | options->gss_deleg_creds = 0; |
1129 | if (options->gss_trust_dns == -1) | ||
1130 | options->gss_trust_dns = 0; | ||
1111 | if (options->password_authentication == -1) | 1131 | if (options->password_authentication == -1) |
1112 | options->password_authentication = 1; | 1132 | options->password_authentication = 1; |
1113 | if (options->kbd_interactive_authentication == -1) | 1133 | if (options->kbd_interactive_authentication == -1) |
diff --git a/readconf.h b/readconf.h index 47c7aef4e..10067f117 100644 --- a/readconf.h +++ b/readconf.h | |||
@@ -44,7 +44,9 @@ typedef struct { | |||
44 | int challenge_response_authentication; | 44 | int challenge_response_authentication; |
45 | /* Try S/Key or TIS, authentication. */ | 45 | /* Try S/Key or TIS, authentication. */ |
46 | int gss_authentication; /* Try GSS authentication */ | 46 | int gss_authentication; /* Try GSS authentication */ |
47 | int gss_keyex; /* Try GSS key exchange */ | ||
47 | int gss_deleg_creds; /* Delegate GSS credentials */ | 48 | int gss_deleg_creds; /* Delegate GSS credentials */ |
49 | int gss_trust_dns; /* Trust DNS for GSS canonicalization */ | ||
48 | int password_authentication; /* Try password | 50 | int password_authentication; /* Try password |
49 | * authentication. */ | 51 | * authentication. */ |
50 | int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ | 52 | int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ |
diff --git a/servconf.c b/servconf.c index 66e22979f..ede032567 100644 --- a/servconf.c +++ b/servconf.c | |||
@@ -92,7 +92,9 @@ initialize_server_options(ServerOptions *options) | |||
92 | options->kerberos_ticket_cleanup = -1; | 92 | options->kerberos_ticket_cleanup = -1; |
93 | options->kerberos_get_afs_token = -1; | 93 | options->kerberos_get_afs_token = -1; |
94 | options->gss_authentication=-1; | 94 | options->gss_authentication=-1; |
95 | options->gss_keyex = -1; | ||
95 | options->gss_cleanup_creds = -1; | 96 | options->gss_cleanup_creds = -1; |
97 | options->gss_strict_acceptor = -1; | ||
96 | options->password_authentication = -1; | 98 | options->password_authentication = -1; |
97 | options->kbd_interactive_authentication = -1; | 99 | options->kbd_interactive_authentication = -1; |
98 | options->challenge_response_authentication = -1; | 100 | options->challenge_response_authentication = -1; |
@@ -209,8 +211,12 @@ fill_default_server_options(ServerOptions *options) | |||
209 | options->kerberos_get_afs_token = 0; | 211 | options->kerberos_get_afs_token = 0; |
210 | if (options->gss_authentication == -1) | 212 | if (options->gss_authentication == -1) |
211 | options->gss_authentication = 0; | 213 | options->gss_authentication = 0; |
214 | if (options->gss_keyex == -1) | ||
215 | options->gss_keyex = 0; | ||
212 | if (options->gss_cleanup_creds == -1) | 216 | if (options->gss_cleanup_creds == -1) |
213 | options->gss_cleanup_creds = 1; | 217 | options->gss_cleanup_creds = 1; |
218 | if (options->gss_strict_acceptor == -1) | ||
219 | options->gss_strict_acceptor = 1; | ||
214 | if (options->password_authentication == -1) | 220 | if (options->password_authentication == -1) |
215 | options->password_authentication = 1; | 221 | options->password_authentication = 1; |
216 | if (options->kbd_interactive_authentication == -1) | 222 | if (options->kbd_interactive_authentication == -1) |
@@ -299,7 +305,9 @@ typedef enum { | |||
299 | sBanner, sUseDNS, sHostbasedAuthentication, | 305 | sBanner, sUseDNS, sHostbasedAuthentication, |
300 | sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, | 306 | sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, |
301 | sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, | 307 | sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, |
302 | sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, | 308 | sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, |
309 | sGssKeyEx, | ||
310 | sAcceptEnv, sPermitTunnel, | ||
303 | sMatch, sPermitOpen, sForceCommand, sChrootDirectory, | 311 | sMatch, sPermitOpen, sForceCommand, sChrootDirectory, |
304 | sUsePrivilegeSeparation, sAllowAgentForwarding, | 312 | sUsePrivilegeSeparation, sAllowAgentForwarding, |
305 | sDeprecated, sUnsupported | 313 | sDeprecated, sUnsupported |
@@ -360,9 +368,13 @@ static struct { | |||
360 | #ifdef GSSAPI | 368 | #ifdef GSSAPI |
361 | { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, | 369 | { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, |
362 | { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, | 370 | { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, |
371 | { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, | ||
372 | { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, | ||
363 | #else | 373 | #else |
364 | { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, | 374 | { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, |
365 | { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, | 375 | { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, |
376 | { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, | ||
377 | { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, | ||
366 | #endif | 378 | #endif |
367 | { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, | 379 | { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, |
368 | { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, | 380 | { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, |
@@ -882,10 +894,18 @@ process_server_config_line(ServerOptions *options, char *line, | |||
882 | intptr = &options->gss_authentication; | 894 | intptr = &options->gss_authentication; |
883 | goto parse_flag; | 895 | goto parse_flag; |
884 | 896 | ||
897 | case sGssKeyEx: | ||
898 | intptr = &options->gss_keyex; | ||
899 | goto parse_flag; | ||
900 | |||
885 | case sGssCleanupCreds: | 901 | case sGssCleanupCreds: |
886 | intptr = &options->gss_cleanup_creds; | 902 | intptr = &options->gss_cleanup_creds; |
887 | goto parse_flag; | 903 | goto parse_flag; |
888 | 904 | ||
905 | case sGssStrictAcceptor: | ||
906 | intptr = &options->gss_strict_acceptor; | ||
907 | goto parse_flag; | ||
908 | |||
889 | case sPasswordAuthentication: | 909 | case sPasswordAuthentication: |
890 | intptr = &options->password_authentication; | 910 | intptr = &options->password_authentication; |
891 | goto parse_flag; | 911 | goto parse_flag; |
diff --git a/servconf.h b/servconf.h index 40ac64f13..bad73a510 100644 --- a/servconf.h +++ b/servconf.h | |||
@@ -91,7 +91,9 @@ typedef struct { | |||
91 | int kerberos_get_afs_token; /* If true, try to get AFS token if | 91 | int kerberos_get_afs_token; /* If true, try to get AFS token if |
92 | * authenticated with Kerberos. */ | 92 | * authenticated with Kerberos. */ |
93 | int gss_authentication; /* If true, permit GSSAPI authentication */ | 93 | int gss_authentication; /* If true, permit GSSAPI authentication */ |
94 | int gss_keyex; /* If true, permit GSSAPI key exchange */ | ||
94 | int gss_cleanup_creds; /* If true, destroy cred cache on logout */ | 95 | int gss_cleanup_creds; /* If true, destroy cred cache on logout */ |
96 | int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ | ||
95 | int password_authentication; /* If true, permit password | 97 | int password_authentication; /* If true, permit password |
96 | * authentication. */ | 98 | * authentication. */ |
97 | int kbd_interactive_authentication; /* If true, permit */ | 99 | int kbd_interactive_authentication; /* If true, permit */ |
@@ -60,6 +60,17 @@ | |||
60 | 60 | ||
61 | #define SSH_GSS_OIDTYPE 0x06 | 61 | #define SSH_GSS_OIDTYPE 0x06 |
62 | 62 | ||
63 | #define SSH2_MSG_KEXGSS_INIT 30 | ||
64 | #define SSH2_MSG_KEXGSS_CONTINUE 31 | ||
65 | #define SSH2_MSG_KEXGSS_COMPLETE 32 | ||
66 | #define SSH2_MSG_KEXGSS_HOSTKEY 33 | ||
67 | #define SSH2_MSG_KEXGSS_ERROR 34 | ||
68 | #define SSH2_MSG_KEXGSS_GROUPREQ 40 | ||
69 | #define SSH2_MSG_KEXGSS_GROUP 41 | ||
70 | #define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" | ||
71 | #define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" | ||
72 | #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" | ||
73 | |||
63 | typedef struct { | 74 | typedef struct { |
64 | char *filename; | 75 | char *filename; |
65 | char *envvar; | 76 | char *envvar; |
@@ -97,6 +108,7 @@ typedef struct { | |||
97 | } Gssctxt; | 108 | } Gssctxt; |
98 | 109 | ||
99 | extern ssh_gssapi_mech *supported_mechs[]; | 110 | extern ssh_gssapi_mech *supported_mechs[]; |
111 | extern Gssctxt *gss_kex_context; | ||
100 | 112 | ||
101 | int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); | 113 | int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); |
102 | void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); | 114 | void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); |
@@ -119,6 +131,11 @@ void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *); | |||
119 | int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); | 131 | int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); |
120 | 132 | ||
121 | /* In the server */ | 133 | /* In the server */ |
134 | typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *); | ||
135 | char *ssh_gssapi_client_mechanisms(const char *host); | ||
136 | char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *); | ||
137 | gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); | ||
138 | int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *); | ||
122 | OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); | 139 | OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); |
123 | int ssh_gssapi_userok(char *name); | 140 | int ssh_gssapi_userok(char *name); |
124 | OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); | 141 | OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); |
@@ -126,6 +143,8 @@ void ssh_gssapi_do_child(char ***, u_int *); | |||
126 | void ssh_gssapi_cleanup_creds(void); | 143 | void ssh_gssapi_cleanup_creds(void); |
127 | void ssh_gssapi_storecreds(void); | 144 | void ssh_gssapi_storecreds(void); |
128 | 145 | ||
146 | char *ssh_gssapi_server_mechanisms(void); | ||
147 | int ssh_gssapi_oid_table_ok(); | ||
129 | #endif /* GSSAPI */ | 148 | #endif /* GSSAPI */ |
130 | 149 | ||
131 | #endif /* _SSH_GSS_H */ | 150 | #endif /* _SSH_GSS_H */ |
diff --git a/ssh_config b/ssh_config index 8cb0698a8..5e3cd7426 100644 --- a/ssh_config +++ b/ssh_config | |||
@@ -26,6 +26,8 @@ | |||
26 | # HostbasedAuthentication no | 26 | # HostbasedAuthentication no |
27 | # GSSAPIAuthentication no | 27 | # GSSAPIAuthentication no |
28 | # GSSAPIDelegateCredentials no | 28 | # GSSAPIDelegateCredentials no |
29 | # GSSAPIKeyExchange no | ||
30 | # GSSAPITrustDNS no | ||
29 | # BatchMode no | 31 | # BatchMode no |
30 | # CheckHostIP yes | 32 | # CheckHostIP yes |
31 | # AddressFamily any | 33 | # AddressFamily any |
diff --git a/ssh_config.5 b/ssh_config.5 index 85e7ba06d..15eecb6ff 100644 --- a/ssh_config.5 +++ b/ssh_config.5 | |||
@@ -478,11 +478,28 @@ Specifies whether user authentication based on GSSAPI is allowed. | |||
478 | The default is | 478 | The default is |
479 | .Dq no . | 479 | .Dq no . |
480 | Note that this option applies to protocol version 2 only. | 480 | Note that this option applies to protocol version 2 only. |
481 | .It Cm GSSAPIKeyExchange | ||
482 | Specifies whether key exchange based on GSSAPI may be used. When using | ||
483 | GSSAPI key exchange the server need not have a host key. | ||
484 | The default is | ||
485 | .Dq no . | ||
486 | Note that this option applies to protocol version 2 only. | ||
481 | .It Cm GSSAPIDelegateCredentials | 487 | .It Cm GSSAPIDelegateCredentials |
482 | Forward (delegate) credentials to the server. | 488 | Forward (delegate) credentials to the server. |
483 | The default is | 489 | The default is |
484 | .Dq no . | 490 | .Dq no . |
485 | Note that this option applies to protocol version 2 only. | 491 | Note that this option applies to protocol version 2 only. |
492 | .It Cm GSSAPITrustDns | ||
493 | Set to | ||
494 | .Dq yes | ||
495 | to indicate that the DNS is trusted to securely canonicalize | ||
496 | the name of the host being connected to. If | ||
497 | .Dq no , | ||
498 | the hostname entered on the | ||
499 | command line will be passed untouched to the GSSAPI library. | ||
500 | The default is | ||
501 | .Dq no . | ||
502 | This option only applies to protocol version 2 connections using GSSAPI. | ||
486 | .It Cm HashKnownHosts | 503 | .It Cm HashKnownHosts |
487 | Indicates that | 504 | Indicates that |
488 | .Xr ssh 1 | 505 | .Xr ssh 1 |
diff --git a/sshconnect2.c b/sshconnect2.c index 389bec9e4..f7d34bf02 100644 --- a/sshconnect2.c +++ b/sshconnect2.c | |||
@@ -102,9 +102,34 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) | |||
102 | { | 102 | { |
103 | Kex *kex; | 103 | Kex *kex; |
104 | 104 | ||
105 | #ifdef GSSAPI | ||
106 | char *orig = NULL, *gss = NULL; | ||
107 | char *gss_host = NULL; | ||
108 | #endif | ||
109 | |||
105 | xxx_host = host; | 110 | xxx_host = host; |
106 | xxx_hostaddr = hostaddr; | 111 | xxx_hostaddr = hostaddr; |
107 | 112 | ||
113 | #ifdef GSSAPI | ||
114 | if (options.gss_keyex) { | ||
115 | /* Add the GSSAPI mechanisms currently supported on this | ||
116 | * client to the key exchange algorithm proposal */ | ||
117 | orig = myproposal[PROPOSAL_KEX_ALGS]; | ||
118 | |||
119 | if (options.gss_trust_dns) | ||
120 | gss_host = (char *)get_canonical_hostname(1); | ||
121 | else | ||
122 | gss_host = host; | ||
123 | |||
124 | gss = ssh_gssapi_client_mechanisms(gss_host); | ||
125 | if (gss) { | ||
126 | debug("Offering GSSAPI proposal: %s", gss); | ||
127 | xasprintf(&myproposal[PROPOSAL_KEX_ALGS], | ||
128 | "%s,%s", gss, orig); | ||
129 | } | ||
130 | } | ||
131 | #endif | ||
132 | |||
108 | if (options.ciphers == (char *)-1) { | 133 | if (options.ciphers == (char *)-1) { |
109 | logit("No valid ciphers for protocol version 2 given, using defaults."); | 134 | logit("No valid ciphers for protocol version 2 given, using defaults."); |
110 | options.ciphers = NULL; | 135 | options.ciphers = NULL; |
@@ -132,6 +157,16 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) | |||
132 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = | 157 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = |
133 | options.hostkeyalgorithms; | 158 | options.hostkeyalgorithms; |
134 | 159 | ||
160 | #ifdef GSSAPI | ||
161 | /* If we've got GSSAPI algorithms, then we also support the | ||
162 | * 'null' hostkey, as a last resort */ | ||
163 | if (options.gss_keyex && gss) { | ||
164 | orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; | ||
165 | xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], | ||
166 | "%s,null", orig); | ||
167 | } | ||
168 | #endif | ||
169 | |||
135 | if (options.rekey_limit) | 170 | if (options.rekey_limit) |
136 | packet_set_rekey_limit((u_int32_t)options.rekey_limit); | 171 | packet_set_rekey_limit((u_int32_t)options.rekey_limit); |
137 | 172 | ||
@@ -141,10 +176,21 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) | |||
141 | kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; | 176 | kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; |
142 | kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; | 177 | kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; |
143 | kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; | 178 | kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; |
179 | #ifdef GSSAPI | ||
180 | kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; | ||
181 | kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; | ||
182 | kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; | ||
183 | #endif | ||
144 | kex->client_version_string=client_version_string; | 184 | kex->client_version_string=client_version_string; |
145 | kex->server_version_string=server_version_string; | 185 | kex->server_version_string=server_version_string; |
146 | kex->verify_host_key=&verify_host_key_callback; | 186 | kex->verify_host_key=&verify_host_key_callback; |
147 | 187 | ||
188 | #ifdef GSSAPI | ||
189 | kex->gss_deleg_creds = options.gss_deleg_creds; | ||
190 | kex->gss_trust_dns = options.gss_trust_dns; | ||
191 | kex->gss_host = gss_host; | ||
192 | #endif | ||
193 | |||
148 | xxx_kex = kex; | 194 | xxx_kex = kex; |
149 | 195 | ||
150 | dispatch_run(DISPATCH_BLOCK, &kex->done, kex); | 196 | dispatch_run(DISPATCH_BLOCK, &kex->done, kex); |
@@ -227,6 +273,7 @@ void input_gssapi_token(int type, u_int32_t, void *); | |||
227 | void input_gssapi_hash(int type, u_int32_t, void *); | 273 | void input_gssapi_hash(int type, u_int32_t, void *); |
228 | void input_gssapi_error(int, u_int32_t, void *); | 274 | void input_gssapi_error(int, u_int32_t, void *); |
229 | void input_gssapi_errtok(int, u_int32_t, void *); | 275 | void input_gssapi_errtok(int, u_int32_t, void *); |
276 | int userauth_gsskeyex(Authctxt *authctxt); | ||
230 | #endif | 277 | #endif |
231 | 278 | ||
232 | void userauth(Authctxt *, char *); | 279 | void userauth(Authctxt *, char *); |
@@ -242,6 +289,10 @@ static char *authmethods_get(void); | |||
242 | 289 | ||
243 | Authmethod authmethods[] = { | 290 | Authmethod authmethods[] = { |
244 | #ifdef GSSAPI | 291 | #ifdef GSSAPI |
292 | {"gssapi-keyex", | ||
293 | userauth_gsskeyex, | ||
294 | &options.gss_authentication, | ||
295 | NULL}, | ||
245 | {"gssapi-with-mic", | 296 | {"gssapi-with-mic", |
246 | userauth_gssapi, | 297 | userauth_gssapi, |
247 | &options.gss_authentication, | 298 | &options.gss_authentication, |
@@ -511,6 +562,12 @@ userauth_gssapi(Authctxt *authctxt) | |||
511 | static u_int mech = 0; | 562 | static u_int mech = 0; |
512 | OM_uint32 min; | 563 | OM_uint32 min; |
513 | int ok = 0; | 564 | int ok = 0; |
565 | char *gss_host = NULL; | ||
566 | |||
567 | if (options.gss_trust_dns) | ||
568 | gss_host = (char *)get_canonical_hostname(1); | ||
569 | else | ||
570 | gss_host = (char *)authctxt->host; | ||
514 | 571 | ||
515 | /* Try one GSSAPI method at a time, rather than sending them all at | 572 | /* Try one GSSAPI method at a time, rather than sending them all at |
516 | * once. */ | 573 | * once. */ |
@@ -523,7 +580,7 @@ userauth_gssapi(Authctxt *authctxt) | |||
523 | /* My DER encoding requires length<128 */ | 580 | /* My DER encoding requires length<128 */ |
524 | if (gss_supported->elements[mech].length < 128 && | 581 | if (gss_supported->elements[mech].length < 128 && |
525 | ssh_gssapi_check_mechanism(&gssctxt, | 582 | ssh_gssapi_check_mechanism(&gssctxt, |
526 | &gss_supported->elements[mech], authctxt->host)) { | 583 | &gss_supported->elements[mech], gss_host)) { |
527 | ok = 1; /* Mechanism works */ | 584 | ok = 1; /* Mechanism works */ |
528 | } else { | 585 | } else { |
529 | mech++; | 586 | mech++; |
@@ -619,8 +676,8 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt) | |||
619 | { | 676 | { |
620 | Authctxt *authctxt = ctxt; | 677 | Authctxt *authctxt = ctxt; |
621 | Gssctxt *gssctxt; | 678 | Gssctxt *gssctxt; |
622 | int oidlen; | 679 | u_int oidlen; |
623 | char *oidv; | 680 | u_char *oidv; |
624 | 681 | ||
625 | if (authctxt == NULL) | 682 | if (authctxt == NULL) |
626 | fatal("input_gssapi_response: no authentication context"); | 683 | fatal("input_gssapi_response: no authentication context"); |
@@ -727,6 +784,48 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt) | |||
727 | xfree(msg); | 784 | xfree(msg); |
728 | xfree(lang); | 785 | xfree(lang); |
729 | } | 786 | } |
787 | |||
788 | int | ||
789 | userauth_gsskeyex(Authctxt *authctxt) | ||
790 | { | ||
791 | Buffer b; | ||
792 | gss_buffer_desc gssbuf; | ||
793 | gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; | ||
794 | OM_uint32 ms; | ||
795 | |||
796 | static int attempt = 0; | ||
797 | if (attempt++ >= 1) | ||
798 | return (0); | ||
799 | |||
800 | if (gss_kex_context == NULL) { | ||
801 | debug("No valid Key exchange context"); | ||
802 | return (0); | ||
803 | } | ||
804 | |||
805 | ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service, | ||
806 | "gssapi-keyex"); | ||
807 | |||
808 | gssbuf.value = buffer_ptr(&b); | ||
809 | gssbuf.length = buffer_len(&b); | ||
810 | |||
811 | if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { | ||
812 | buffer_free(&b); | ||
813 | return (0); | ||
814 | } | ||
815 | |||
816 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
817 | packet_put_cstring(authctxt->server_user); | ||
818 | packet_put_cstring(authctxt->service); | ||
819 | packet_put_cstring(authctxt->method->name); | ||
820 | packet_put_string(mic.value, mic.length); | ||
821 | packet_send(); | ||
822 | |||
823 | buffer_free(&b); | ||
824 | gss_release_buffer(&ms, &mic); | ||
825 | |||
826 | return (1); | ||
827 | } | ||
828 | |||
730 | #endif /* GSSAPI */ | 829 | #endif /* GSSAPI */ |
731 | 830 | ||
732 | int | 831 | int |
@@ -120,6 +120,10 @@ | |||
120 | #include "monitor_fdpass.h" | 120 | #include "monitor_fdpass.h" |
121 | #include "version.h" | 121 | #include "version.h" |
122 | 122 | ||
123 | #ifdef USE_SECURITY_SESSION_API | ||
124 | #include <Security/AuthSession.h> | ||
125 | #endif | ||
126 | |||
123 | #ifdef LIBWRAP | 127 | #ifdef LIBWRAP |
124 | #include <tcpd.h> | 128 | #include <tcpd.h> |
125 | #include <syslog.h> | 129 | #include <syslog.h> |
@@ -1531,10 +1535,13 @@ main(int ac, char **av) | |||
1531 | logit("Disabling protocol version 1. Could not load host key"); | 1535 | logit("Disabling protocol version 1. Could not load host key"); |
1532 | options.protocol &= ~SSH_PROTO_1; | 1536 | options.protocol &= ~SSH_PROTO_1; |
1533 | } | 1537 | } |
1538 | #ifndef GSSAPI | ||
1539 | /* The GSSAPI key exchange can run without a host key */ | ||
1534 | if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { | 1540 | if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { |
1535 | logit("Disabling protocol version 2. Could not load host key"); | 1541 | logit("Disabling protocol version 2. Could not load host key"); |
1536 | options.protocol &= ~SSH_PROTO_2; | 1542 | options.protocol &= ~SSH_PROTO_2; |
1537 | } | 1543 | } |
1544 | #endif | ||
1538 | if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { | 1545 | if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { |
1539 | logit("sshd: no hostkeys available -- exiting."); | 1546 | logit("sshd: no hostkeys available -- exiting."); |
1540 | exit(1); | 1547 | exit(1); |
@@ -1818,6 +1825,60 @@ main(int ac, char **av) | |||
1818 | /* Log the connection. */ | 1825 | /* Log the connection. */ |
1819 | verbose("Connection from %.500s port %d", remote_ip, remote_port); | 1826 | verbose("Connection from %.500s port %d", remote_ip, remote_port); |
1820 | 1827 | ||
1828 | #ifdef USE_SECURITY_SESSION_API | ||
1829 | /* | ||
1830 | * Create a new security session for use by the new user login if | ||
1831 | * the current session is the root session or we are not launched | ||
1832 | * by inetd (eg: debugging mode or server mode). We do not | ||
1833 | * necessarily need to create a session if we are launched from | ||
1834 | * inetd because Panther xinetd will create a session for us. | ||
1835 | * | ||
1836 | * The only case where this logic will fail is if there is an | ||
1837 | * inetd running in a non-root session which is not creating | ||
1838 | * new sessions for us. Then all the users will end up in the | ||
1839 | * same session (bad). | ||
1840 | * | ||
1841 | * When the client exits, the session will be destroyed for us | ||
1842 | * automatically. | ||
1843 | * | ||
1844 | * We must create the session before any credentials are stored | ||
1845 | * (including AFS pags, which happens a few lines below). | ||
1846 | */ | ||
1847 | { | ||
1848 | OSStatus err = 0; | ||
1849 | SecuritySessionId sid = 0; | ||
1850 | SessionAttributeBits sattrs = 0; | ||
1851 | |||
1852 | err = SessionGetInfo(callerSecuritySession, &sid, &sattrs); | ||
1853 | if (err) | ||
1854 | error("SessionGetInfo() failed with error %.8X", | ||
1855 | (unsigned) err); | ||
1856 | else | ||
1857 | debug("Current Session ID is %.8X / Session Attributes are %.8X", | ||
1858 | (unsigned) sid, (unsigned) sattrs); | ||
1859 | |||
1860 | if (inetd_flag && !(sattrs & sessionIsRoot)) | ||
1861 | debug("Running in inetd mode in a non-root session... " | ||
1862 | "assuming inetd created the session for us."); | ||
1863 | else { | ||
1864 | debug("Creating new security session..."); | ||
1865 | err = SessionCreate(0, sessionHasTTY | sessionIsRemote); | ||
1866 | if (err) | ||
1867 | error("SessionCreate() failed with error %.8X", | ||
1868 | (unsigned) err); | ||
1869 | |||
1870 | err = SessionGetInfo(callerSecuritySession, &sid, | ||
1871 | &sattrs); | ||
1872 | if (err) | ||
1873 | error("SessionGetInfo() failed with error %.8X", | ||
1874 | (unsigned) err); | ||
1875 | else | ||
1876 | debug("New Session ID is %.8X / Session Attributes are %.8X", | ||
1877 | (unsigned) sid, (unsigned) sattrs); | ||
1878 | } | ||
1879 | } | ||
1880 | #endif | ||
1881 | |||
1821 | /* | 1882 | /* |
1822 | * We don't want to listen forever unless the other side | 1883 | * We don't want to listen forever unless the other side |
1823 | * successfully authenticates itself. So we set up an alarm which is | 1884 | * successfully authenticates itself. So we set up an alarm which is |
@@ -2195,12 +2256,59 @@ do_ssh2_kex(void) | |||
2195 | 2256 | ||
2196 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); | 2257 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); |
2197 | 2258 | ||
2259 | #ifdef GSSAPI | ||
2260 | { | ||
2261 | char *orig; | ||
2262 | char *gss = NULL; | ||
2263 | char *newstr = NULL; | ||
2264 | orig = myproposal[PROPOSAL_KEX_ALGS]; | ||
2265 | |||
2266 | /* | ||
2267 | * If we don't have a host key, then there's no point advertising | ||
2268 | * the other key exchange algorithms | ||
2269 | */ | ||
2270 | |||
2271 | if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) | ||
2272 | orig = NULL; | ||
2273 | |||
2274 | if (options.gss_keyex) | ||
2275 | gss = ssh_gssapi_server_mechanisms(); | ||
2276 | else | ||
2277 | gss = NULL; | ||
2278 | |||
2279 | if (gss && orig) | ||
2280 | xasprintf(&newstr, "%s,%s", gss, orig); | ||
2281 | else if (gss) | ||
2282 | newstr = gss; | ||
2283 | else if (orig) | ||
2284 | newstr = orig; | ||
2285 | |||
2286 | /* | ||
2287 | * If we've got GSSAPI mechanisms, then we've got the 'null' host | ||
2288 | * key alg, but we can't tell people about it unless its the only | ||
2289 | * host key algorithm we support | ||
2290 | */ | ||
2291 | if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) | ||
2292 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; | ||
2293 | |||
2294 | if (newstr) | ||
2295 | myproposal[PROPOSAL_KEX_ALGS] = newstr; | ||
2296 | else | ||
2297 | fatal("No supported key exchange algorithms"); | ||
2298 | } | ||
2299 | #endif | ||
2300 | |||
2198 | /* start key exchange */ | 2301 | /* start key exchange */ |
2199 | kex = kex_setup(myproposal); | 2302 | kex = kex_setup(myproposal); |
2200 | kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; | 2303 | kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; |
2201 | kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; | 2304 | kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; |
2202 | kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; | 2305 | kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; |
2203 | kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; | 2306 | kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; |
2307 | #ifdef GSSAPI | ||
2308 | kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; | ||
2309 | kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; | ||
2310 | kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; | ||
2311 | #endif | ||
2204 | kex->server = 1; | 2312 | kex->server = 1; |
2205 | kex->client_version_string=client_version_string; | 2313 | kex->client_version_string=client_version_string; |
2206 | kex->server_version_string=server_version_string; | 2314 | kex->server_version_string=server_version_string; |
diff --git a/sshd_config b/sshd_config index 1b53a0efb..e03b3b15f 100644 --- a/sshd_config +++ b/sshd_config | |||
@@ -73,6 +73,8 @@ Protocol 2 | |||
73 | # GSSAPI options | 73 | # GSSAPI options |
74 | #GSSAPIAuthentication no | 74 | #GSSAPIAuthentication no |
75 | #GSSAPICleanupCredentials yes | 75 | #GSSAPICleanupCredentials yes |
76 | #GSSAPIStrictAcceptorCheck yes | ||
77 | #GSSAPIKeyExchange no | ||
76 | 78 | ||
77 | # Set this to 'yes' to enable PAM authentication, account processing, | 79 | # Set this to 'yes' to enable PAM authentication, account processing, |
78 | # and session processing. If this is enabled, PAM authentication will | 80 | # and session processing. If this is enabled, PAM authentication will |
diff --git a/sshd_config.5 b/sshd_config.5 index 7255b1c22..e58b7cfc7 100644 --- a/sshd_config.5 +++ b/sshd_config.5 | |||
@@ -374,12 +374,35 @@ Specifies whether user authentication based on GSSAPI is allowed. | |||
374 | The default is | 374 | The default is |
375 | .Dq no . | 375 | .Dq no . |
376 | Note that this option applies to protocol version 2 only. | 376 | Note that this option applies to protocol version 2 only. |
377 | .It Cm GSSAPIKeyExchange | ||
378 | Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange | ||
379 | doesn't rely on ssh keys to verify host identity. | ||
380 | The default is | ||
381 | .Dq no . | ||
382 | Note that this option applies to protocol version 2 only. | ||
377 | .It Cm GSSAPICleanupCredentials | 383 | .It Cm GSSAPICleanupCredentials |
378 | Specifies whether to automatically destroy the user's credentials cache | 384 | Specifies whether to automatically destroy the user's credentials cache |
379 | on logout. | 385 | on logout. |
380 | The default is | 386 | The default is |
381 | .Dq yes . | 387 | .Dq yes . |
382 | Note that this option applies to protocol version 2 only. | 388 | Note that this option applies to protocol version 2 only. |
389 | .It Cm GSSAPIStrictAcceptorCheck | ||
390 | Determines whether to be strict about the identity of the GSSAPI acceptor | ||
391 | a client authenticates against. If | ||
392 | .Dq yes | ||
393 | then the client must authenticate against the | ||
394 | .Pa host | ||
395 | service on the current hostname. If | ||
396 | .Dq no | ||
397 | then the client may authenticate against any service key stored in the | ||
398 | machine's default store. This facility is provided to assist with operation | ||
399 | on multi homed machines. | ||
400 | The default is | ||
401 | .Dq yes . | ||
402 | Note that this option applies only to protocol version 2 GSSAPI connections, | ||
403 | and setting it to | ||
404 | .Dq no | ||
405 | may only work with recent Kerberos GSSAPI libraries. | ||
383 | .It Cm HostbasedAuthentication | 406 | .It Cm HostbasedAuthentication |
384 | Specifies whether rhosts or /etc/hosts.equiv authentication together | 407 | Specifies whether rhosts or /etc/hosts.equiv authentication together |
385 | with successful public key client host authentication is allowed | 408 | with successful public key client host authentication is allowed |