13 /* Windows code to set up the GSSAPI library list. */
15 const int ngsslibs
= 3;
16 const char *const gsslibnames
[3] = {
17 "MIT Kerberos GSSAPI32.DLL",
18 "Microsoft SSPI SECUR32.DLL",
19 "User-specified GSSAPI DLL",
21 const struct keyvalwhere gsslibkeywords
[] = {
22 { "gssapi32", 0, -1, -1 },
23 { "sspi", 1, -1, -1 },
24 { "custom", 2, -1, -1 },
27 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS
,
28 AcquireCredentialsHandleA
,
29 (SEC_CHAR
*, SEC_CHAR
*, ULONG
, PLUID
,
30 PVOID
, SEC_GET_KEY_FN
, PVOID
, PCredHandle
, PTimeStamp
));
31 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS
,
32 InitializeSecurityContextA
,
33 (PCredHandle
, PCtxtHandle
, SEC_CHAR
*, ULONG
, ULONG
,
34 ULONG
, PSecBufferDesc
, ULONG
, PCtxtHandle
,
35 PSecBufferDesc
, PULONG
, PTimeStamp
));
36 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS
,
39 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS
,
40 FreeCredentialsHandle
,
42 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS
,
43 DeleteSecurityContext
,
45 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS
,
46 QueryContextAttributesA
,
47 (PCtxtHandle
, ULONG
, PVOID
));
48 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS
,
50 (PCtxtHandle
, ULONG
, PSecBufferDesc
, ULONG
));
52 typedef struct winSsh_gss_ctx
{
53 unsigned long maj_stat
;
54 unsigned long min_stat
;
55 CredHandle cred_handle
;
57 PCtxtHandle context_handle
;
62 const Ssh_gss_buf gss_mech_krb5
={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};
64 const char *gsslogmsg
= NULL
;
66 static void ssh_sspi_bind_fns(struct ssh_gss_library
*lib
);
68 struct ssh_gss_liblist
*ssh_gss_setup(const Config
*cfg
)
72 struct ssh_gss_liblist
*list
= snew(struct ssh_gss_liblist
);
74 list
->libraries
= snewn(3, struct ssh_gss_library
);
77 /* MIT Kerberos GSSAPI implementation */
78 /* TODO: For 64-bit builds, check for gssapi64.dll */
80 if (RegOpenKey(HKEY_LOCAL_MACHINE
, "SOFTWARE\\MIT\\Kerberos", ®key
)
86 /* Find out the string length */
87 ret
= RegQueryValueEx(regkey
, "InstallDir", NULL
, &type
, NULL
, &size
);
89 if (ret
== ERROR_SUCCESS
&& type
== REG_SZ
) {
90 buffer
= snewn(size
+ 20, char);
91 ret
= RegQueryValueEx(regkey
, "InstallDir", NULL
,
92 &type
, buffer
, &size
);
93 if (ret
== ERROR_SUCCESS
&& type
== REG_SZ
) {
95 strcat(buffer
, "\\bin\\gssapi64.dll");
97 strcat(buffer
, "\\bin\\gssapi32.dll");
99 module
= LoadLibrary(buffer
);
106 struct ssh_gss_library
*lib
=
107 &list
->libraries
[list
->nlibraries
++];
110 lib
->gsslogmsg
= "Using GSSAPI from GSSAPI32.DLL";
111 lib
->handle
= (void *)module
;
113 #define BIND_GSS_FN(name) \
114 lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name)
116 BIND_GSS_FN(delete_sec_context
);
117 BIND_GSS_FN(display_status
);
118 BIND_GSS_FN(get_mic
);
119 BIND_GSS_FN(import_name
);
120 BIND_GSS_FN(init_sec_context
);
121 BIND_GSS_FN(release_buffer
);
122 BIND_GSS_FN(release_cred
);
123 BIND_GSS_FN(release_name
);
127 ssh_gssapi_bind_fns(lib
);
130 /* Microsoft SSPI Implementation */
131 module
= load_system32_dll("secur32.dll");
133 struct ssh_gss_library
*lib
=
134 &list
->libraries
[list
->nlibraries
++];
137 lib
->gsslogmsg
= "Using SSPI from SECUR32.DLL";
138 lib
->handle
= (void *)module
;
140 GET_WINDOWS_FUNCTION(module
, AcquireCredentialsHandleA
);
141 GET_WINDOWS_FUNCTION(module
, InitializeSecurityContextA
);
142 GET_WINDOWS_FUNCTION(module
, FreeContextBuffer
);
143 GET_WINDOWS_FUNCTION(module
, FreeCredentialsHandle
);
144 GET_WINDOWS_FUNCTION(module
, DeleteSecurityContext
);
145 GET_WINDOWS_FUNCTION(module
, QueryContextAttributesA
);
146 GET_WINDOWS_FUNCTION(module
, MakeSignature
);
148 ssh_sspi_bind_fns(lib
);
155 if (cfg
->ssh_gss_custom
.path
[0]) {
156 module
= LoadLibrary(cfg
->ssh_gss_custom
.path
);
159 struct ssh_gss_library
*lib
=
160 &list
->libraries
[list
->nlibraries
++];
163 lib
->gsslogmsg
= dupprintf("Using GSSAPI from user-specified"
164 " library '%s'", cfg
->ssh_gss_custom
.path
);
165 lib
->handle
= (void *)module
;
167 #define BIND_GSS_FN(name) \
168 lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name)
170 BIND_GSS_FN(delete_sec_context
);
171 BIND_GSS_FN(display_status
);
172 BIND_GSS_FN(get_mic
);
173 BIND_GSS_FN(import_name
);
174 BIND_GSS_FN(init_sec_context
);
175 BIND_GSS_FN(release_buffer
);
176 BIND_GSS_FN(release_cred
);
177 BIND_GSS_FN(release_name
);
181 ssh_gssapi_bind_fns(lib
);
188 void ssh_gss_cleanup(struct ssh_gss_liblist
*list
)
193 * LoadLibrary and FreeLibrary are defined to employ reference
194 * counting in the case where the same library is repeatedly
195 * loaded, so even in a multiple-sessions-per-process context
196 * (not that we currently expect ever to have such a thing on
197 * Windows) it's safe to naively FreeLibrary everything here
198 * without worrying about destroying it under the feet of
199 * another SSH instance still using it.
201 for (i
= 0; i
< list
->nlibraries
; i
++) {
202 FreeLibrary((HMODULE
)list
->libraries
[i
].handle
);
203 if (list
->libraries
[i
].id
== 2) {
204 /* The 'custom' id involves a dynamically allocated message.
205 * Note that we must cast away the 'const' to free it. */
206 sfree((char *)list
->libraries
[i
].gsslogmsg
);
209 sfree(list
->libraries
);
213 static Ssh_gss_stat
ssh_sspi_indicate_mech(struct ssh_gss_library
*lib
,
216 *mech
= gss_mech_krb5
;
221 static Ssh_gss_stat
ssh_sspi_import_name(struct ssh_gss_library
*lib
,
222 char *host
, Ssh_gss_name
*srv_name
)
227 if (host
== NULL
) return SSH_GSS_FAILURE
;
229 /* copy it into form host/FQDN */
230 pStr
= dupcat("host/", host
, NULL
);
232 *srv_name
= (Ssh_gss_name
) pStr
;
237 static Ssh_gss_stat
ssh_sspi_acquire_cred(struct ssh_gss_library
*lib
,
240 winSsh_gss_ctx
*winctx
= snew(winSsh_gss_ctx
);
241 memset(winctx
, 0, sizeof(winSsh_gss_ctx
));
243 /* prepare our "wrapper" structure */
244 winctx
->maj_stat
= winctx
->min_stat
= SEC_E_OK
;
245 winctx
->context_handle
= NULL
;
247 /* Specifying no principal name here means use the credentials of
248 the current logged-in user */
250 winctx
->maj_stat
= p_AcquireCredentialsHandleA(NULL
,
252 SECPKG_CRED_OUTBOUND
,
257 &winctx
->cred_handle
,
260 if (winctx
->maj_stat
!= SEC_E_OK
) return SSH_GSS_FAILURE
;
262 *ctx
= (Ssh_gss_ctx
) winctx
;
267 static Ssh_gss_stat
ssh_sspi_init_sec_context(struct ssh_gss_library
*lib
,
269 Ssh_gss_name srv_name
,
271 Ssh_gss_buf
*recv_tok
,
272 Ssh_gss_buf
*send_tok
)
274 winSsh_gss_ctx
*winctx
= (winSsh_gss_ctx
*) *ctx
;
275 SecBuffer wsend_tok
= {send_tok
->length
,SECBUFFER_TOKEN
,send_tok
->value
};
276 SecBuffer wrecv_tok
= {recv_tok
->length
,SECBUFFER_TOKEN
,recv_tok
->value
};
277 SecBufferDesc output_desc
={SECBUFFER_VERSION
,1,&wsend_tok
};
278 SecBufferDesc input_desc
={SECBUFFER_VERSION
,1,&wrecv_tok
};
279 unsigned long flags
=ISC_REQ_MUTUAL_AUTH
|ISC_REQ_REPLAY_DETECT
|
280 ISC_REQ_CONFIDENTIALITY
|ISC_REQ_ALLOCATE_MEMORY
;
281 unsigned long ret_flags
=0;
283 /* check if we have to delegate ... */
284 if (to_deleg
) flags
|= ISC_REQ_DELEGATE
;
285 winctx
->maj_stat
= p_InitializeSecurityContextA(&winctx
->cred_handle
,
286 winctx
->context_handle
,
290 SECURITY_NATIVE_DREP
,
298 /* prepare for the next round */
299 winctx
->context_handle
= &winctx
->context
;
300 send_tok
->value
= wsend_tok
.pvBuffer
;
301 send_tok
->length
= wsend_tok
.cbBuffer
;
303 /* check & return our status */
304 if (winctx
->maj_stat
==SEC_E_OK
) return SSH_GSS_S_COMPLETE
;
305 if (winctx
->maj_stat
==SEC_I_CONTINUE_NEEDED
) return SSH_GSS_S_CONTINUE_NEEDED
;
307 return SSH_GSS_FAILURE
;
310 static Ssh_gss_stat
ssh_sspi_free_tok(struct ssh_gss_library
*lib
,
311 Ssh_gss_buf
*send_tok
)
314 if (send_tok
== NULL
) return SSH_GSS_FAILURE
;
316 /* free Windows buffer */
317 p_FreeContextBuffer(send_tok
->value
);
318 SSH_GSS_CLEAR_BUF(send_tok
);
323 static Ssh_gss_stat
ssh_sspi_release_cred(struct ssh_gss_library
*lib
,
326 winSsh_gss_ctx
*winctx
= (winSsh_gss_ctx
*) *ctx
;
329 if (winctx
== NULL
) return SSH_GSS_FAILURE
;
331 /* free Windows data */
332 p_FreeCredentialsHandle(&winctx
->cred_handle
);
333 p_DeleteSecurityContext(&winctx
->context
);
335 /* delete our "wrapper" structure */
337 *ctx
= (Ssh_gss_ctx
) NULL
;
343 static Ssh_gss_stat
ssh_sspi_release_name(struct ssh_gss_library
*lib
,
344 Ssh_gss_name
*srv_name
)
346 char *pStr
= (char *) *srv_name
;
348 if (pStr
== NULL
) return SSH_GSS_FAILURE
;
350 *srv_name
= (Ssh_gss_name
) NULL
;
355 static Ssh_gss_stat
ssh_sspi_display_status(struct ssh_gss_library
*lib
,
356 Ssh_gss_ctx ctx
, Ssh_gss_buf
*buf
)
358 winSsh_gss_ctx
*winctx
= (winSsh_gss_ctx
*) ctx
;
361 if (winctx
== NULL
) return SSH_GSS_FAILURE
;
363 /* decode the error code */
364 switch (winctx
->maj_stat
) {
365 case SEC_E_OK
: msg
="SSPI status OK"; break;
366 case SEC_E_INVALID_HANDLE
: msg
="The handle passed to the function"
369 case SEC_E_TARGET_UNKNOWN
: msg
="The target was not recognized."; break;
370 case SEC_E_LOGON_DENIED
: msg
="The logon failed."; break;
371 case SEC_E_INTERNAL_ERROR
: msg
="The Local Security Authority cannot"
374 case SEC_E_NO_CREDENTIALS
: msg
="No credentials are available in the"
375 " security package.";
377 case SEC_E_NO_AUTHENTICATING_AUTHORITY
:
378 msg
="No authority could be contacted for authentication."
379 "The domain name of the authenticating party could be wrong,"
380 " the domain could be unreachable, or there might have been"
381 " a trust relationship failure.";
383 case SEC_E_INSUFFICIENT_MEMORY
:
384 msg
="One or more of the SecBufferDesc structures passed as"
385 " an OUT parameter has a buffer that is too small.";
387 case SEC_E_INVALID_TOKEN
:
388 msg
="The error is due to a malformed input token, such as a"
389 " token corrupted in transit, a token"
390 " of incorrect size, or a token passed into the wrong"
391 " security package. Passing a token to"
392 " the wrong package can happen if client and server did not"
393 " negotiate the proper security package.";
396 msg
= "Internal SSPI error";
400 buf
->value
= dupstr(msg
);
401 buf
->length
= strlen(buf
->value
);
406 static Ssh_gss_stat
ssh_sspi_get_mic(struct ssh_gss_library
*lib
,
407 Ssh_gss_ctx ctx
, Ssh_gss_buf
*buf
,
410 winSsh_gss_ctx
*winctx
= (winSsh_gss_ctx
*) ctx
;
411 SecPkgContext_Sizes ContextSizes
;
412 SecBufferDesc InputBufferDescriptor
;
413 SecBuffer InputSecurityToken
[2];
415 if (winctx
== NULL
) return SSH_GSS_FAILURE
;
417 winctx
->maj_stat
= 0;
419 memset(&ContextSizes
, 0, sizeof(ContextSizes
));
421 winctx
->maj_stat
= p_QueryContextAttributesA(&winctx
->context
,
425 if (winctx
->maj_stat
!= SEC_E_OK
||
426 ContextSizes
.cbMaxSignature
== 0)
427 return winctx
->maj_stat
;
429 InputBufferDescriptor
.cBuffers
= 2;
430 InputBufferDescriptor
.pBuffers
= InputSecurityToken
;
431 InputBufferDescriptor
.ulVersion
= SECBUFFER_VERSION
;
432 InputSecurityToken
[0].BufferType
= SECBUFFER_DATA
;
433 InputSecurityToken
[0].cbBuffer
= buf
->length
;
434 InputSecurityToken
[0].pvBuffer
= buf
->value
;
435 InputSecurityToken
[1].BufferType
= SECBUFFER_TOKEN
;
436 InputSecurityToken
[1].cbBuffer
= ContextSizes
.cbMaxSignature
;
437 InputSecurityToken
[1].pvBuffer
= snewn(ContextSizes
.cbMaxSignature
, char);
439 winctx
->maj_stat
= p_MakeSignature(&winctx
->context
,
441 &InputBufferDescriptor
,
444 if (winctx
->maj_stat
== SEC_E_OK
) {
445 hash
->length
= InputSecurityToken
[1].cbBuffer
;
446 hash
->value
= InputSecurityToken
[1].pvBuffer
;
449 return winctx
->maj_stat
;
452 static Ssh_gss_stat
ssh_sspi_free_mic(struct ssh_gss_library
*lib
,
459 static void ssh_sspi_bind_fns(struct ssh_gss_library
*lib
)
461 lib
->indicate_mech
= ssh_sspi_indicate_mech
;
462 lib
->import_name
= ssh_sspi_import_name
;
463 lib
->release_name
= ssh_sspi_release_name
;
464 lib
->init_sec_context
= ssh_sspi_init_sec_context
;
465 lib
->free_tok
= ssh_sspi_free_tok
;
466 lib
->acquire_cred
= ssh_sspi_acquire_cred
;
467 lib
->release_cred
= ssh_sspi_release_cred
;
468 lib
->get_mic
= ssh_sspi_get_mic
;
469 lib
->free_mic
= ssh_sspi_free_mic
;
470 lib
->display_status
= ssh_sspi_display_status
;
475 /* Dummy function so this source file defines something if NO_GSSAPI
478 void ssh_gss_init(void)