5 #include <security.h>
\r
13 /* Windows code to set up the GSSAPI library list. */
\r
15 const int ngsslibs = 3;
\r
16 const char *const gsslibnames[3] = {
\r
17 "MIT Kerberos GSSAPI32.DLL",
\r
18 "Microsoft SSPI SECUR32.DLL",
\r
19 "User-specified GSSAPI DLL",
\r
21 const struct keyvalwhere gsslibkeywords[] = {
\r
22 { "gssapi32", 0, -1, -1 },
\r
23 { "sspi", 1, -1, -1 },
\r
24 { "custom", 2, -1, -1 },
\r
27 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
\r
28 AcquireCredentialsHandleA,
\r
29 (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID,
\r
30 PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp));
\r
31 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
\r
32 InitializeSecurityContextA,
\r
33 (PCredHandle, PCtxtHandle, SEC_CHAR *, ULONG, ULONG,
\r
34 ULONG, PSecBufferDesc, ULONG, PCtxtHandle,
\r
35 PSecBufferDesc, PULONG, PTimeStamp));
\r
36 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
\r
39 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
\r
40 FreeCredentialsHandle,
\r
42 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
\r
43 DeleteSecurityContext,
\r
45 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
\r
46 QueryContextAttributesA,
\r
47 (PCtxtHandle, ULONG, PVOID));
\r
48 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
\r
50 (PCtxtHandle, ULONG, PSecBufferDesc, ULONG));
\r
52 typedef struct winSsh_gss_ctx {
\r
53 unsigned long maj_stat;
\r
54 unsigned long min_stat;
\r
55 CredHandle cred_handle;
\r
57 PCtxtHandle context_handle;
\r
62 const Ssh_gss_buf gss_mech_krb5={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};
\r
64 const char *gsslogmsg = NULL;
\r
66 static void ssh_sspi_bind_fns(struct ssh_gss_library *lib);
\r
68 struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
\r
72 struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist);
\r
74 list->libraries = snewn(3, struct ssh_gss_library);
\r
75 list->nlibraries = 0;
\r
77 /* MIT Kerberos GSSAPI implementation */
\r
78 /* TODO: For 64-bit builds, check for gssapi64.dll */
\r
80 if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MIT\\Kerberos", ®key)
\r
86 /* Find out the string length */
\r
87 ret = RegQueryValueEx(regkey, "InstallDir", NULL, &type, NULL, &size);
\r
89 if (ret == ERROR_SUCCESS && type == REG_SZ) {
\r
90 buffer = snewn(size + 20, char);
\r
91 ret = RegQueryValueEx(regkey, "InstallDir", NULL,
\r
92 &type, buffer, &size);
\r
93 if (ret == ERROR_SUCCESS && type == REG_SZ) {
\r
94 strcat(buffer, "\\bin\\gssapi32.dll");
\r
95 module = LoadLibrary(buffer);
\r
99 RegCloseKey(regkey);
\r
102 struct ssh_gss_library *lib =
\r
103 &list->libraries[list->nlibraries++];
\r
106 lib->gsslogmsg = "Using GSSAPI from GSSAPI32.DLL";
\r
107 lib->handle = (void *)module;
\r
109 #define BIND_GSS_FN(name) \
\r
110 lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name)
\r
112 BIND_GSS_FN(delete_sec_context);
\r
113 BIND_GSS_FN(display_status);
\r
114 BIND_GSS_FN(get_mic);
\r
115 BIND_GSS_FN(import_name);
\r
116 BIND_GSS_FN(init_sec_context);
\r
117 BIND_GSS_FN(release_buffer);
\r
118 BIND_GSS_FN(release_cred);
\r
119 BIND_GSS_FN(release_name);
\r
123 ssh_gssapi_bind_fns(lib);
\r
126 /* Microsoft SSPI Implementation */
\r
127 module = load_system32_dll("secur32.dll");
\r
129 struct ssh_gss_library *lib =
\r
130 &list->libraries[list->nlibraries++];
\r
133 lib->gsslogmsg = "Using SSPI from SECUR32.DLL";
\r
134 lib->handle = (void *)module;
\r
136 GET_WINDOWS_FUNCTION(module, AcquireCredentialsHandleA);
\r
137 GET_WINDOWS_FUNCTION(module, InitializeSecurityContextA);
\r
138 GET_WINDOWS_FUNCTION(module, FreeContextBuffer);
\r
139 GET_WINDOWS_FUNCTION(module, FreeCredentialsHandle);
\r
140 GET_WINDOWS_FUNCTION(module, DeleteSecurityContext);
\r
141 GET_WINDOWS_FUNCTION(module, QueryContextAttributesA);
\r
142 GET_WINDOWS_FUNCTION(module, MakeSignature);
\r
144 ssh_sspi_bind_fns(lib);
\r
148 * Custom GSSAPI DLL.
\r
151 if (cfg->ssh_gss_custom.path[0]) {
\r
152 module = LoadLibrary(cfg->ssh_gss_custom.path);
\r
155 struct ssh_gss_library *lib =
\r
156 &list->libraries[list->nlibraries++];
\r
159 lib->gsslogmsg = dupprintf("Using GSSAPI from user-specified"
\r
160 " library '%s'", cfg->ssh_gss_custom.path);
\r
161 lib->handle = (void *)module;
\r
163 #define BIND_GSS_FN(name) \
\r
164 lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name)
\r
166 BIND_GSS_FN(delete_sec_context);
\r
167 BIND_GSS_FN(display_status);
\r
168 BIND_GSS_FN(get_mic);
\r
169 BIND_GSS_FN(import_name);
\r
170 BIND_GSS_FN(init_sec_context);
\r
171 BIND_GSS_FN(release_buffer);
\r
172 BIND_GSS_FN(release_cred);
\r
173 BIND_GSS_FN(release_name);
\r
177 ssh_gssapi_bind_fns(lib);
\r
184 void ssh_gss_cleanup(struct ssh_gss_liblist *list)
\r
189 * LoadLibrary and FreeLibrary are defined to employ reference
\r
190 * counting in the case where the same library is repeatedly
\r
191 * loaded, so even in a multiple-sessions-per-process context
\r
192 * (not that we currently expect ever to have such a thing on
\r
193 * Windows) it's safe to naively FreeLibrary everything here
\r
194 * without worrying about destroying it under the feet of
\r
195 * another SSH instance still using it.
\r
197 for (i = 0; i < list->nlibraries; i++) {
\r
198 FreeLibrary((HMODULE)list->libraries[i].handle);
\r
199 if (list->libraries[i].id == 2) {
\r
200 /* The 'custom' id involves a dynamically allocated message.
\r
201 * Note that we must cast away the 'const' to free it. */
\r
202 sfree((char *)list->libraries[i].gsslogmsg);
\r
205 sfree(list->libraries);
\r
209 static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib,
\r
212 *mech = gss_mech_krb5;
\r
217 static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib,
\r
218 char *host, Ssh_gss_name *srv_name)
\r
222 /* Check hostname */
\r
223 if (host == NULL) return SSH_GSS_FAILURE;
\r
225 /* copy it into form host/FQDN */
\r
226 pStr = dupcat("host/", host, NULL);
\r
228 *srv_name = (Ssh_gss_name) pStr;
\r
233 static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib,
\r
236 winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx);
\r
237 memset(winctx, 0, sizeof(winSsh_gss_ctx));
\r
239 /* prepare our "wrapper" structure */
\r
240 winctx->maj_stat = winctx->min_stat = SEC_E_OK;
\r
241 winctx->context_handle = NULL;
\r
243 /* Specifying no principal name here means use the credentials of
\r
244 the current logged-in user */
\r
246 winctx->maj_stat = p_AcquireCredentialsHandleA(NULL,
\r
248 SECPKG_CRED_OUTBOUND,
\r
253 &winctx->cred_handle,
\r
256 if (winctx->maj_stat != SEC_E_OK) return SSH_GSS_FAILURE;
\r
258 *ctx = (Ssh_gss_ctx) winctx;
\r
263 static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib,
\r
265 Ssh_gss_name srv_name,
\r
267 Ssh_gss_buf *recv_tok,
\r
268 Ssh_gss_buf *send_tok)
\r
270 winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) *ctx;
\r
271 SecBuffer wsend_tok = {send_tok->length,SECBUFFER_TOKEN,send_tok->value};
\r
272 SecBuffer wrecv_tok = {recv_tok->length,SECBUFFER_TOKEN,recv_tok->value};
\r
273 SecBufferDesc output_desc={SECBUFFER_VERSION,1,&wsend_tok};
\r
274 SecBufferDesc input_desc ={SECBUFFER_VERSION,1,&wrecv_tok};
\r
275 unsigned long flags=ISC_REQ_MUTUAL_AUTH|ISC_REQ_REPLAY_DETECT|
\r
276 ISC_REQ_CONFIDENTIALITY|ISC_REQ_ALLOCATE_MEMORY;
\r
277 unsigned long ret_flags=0;
\r
279 /* check if we have to delegate ... */
\r
280 if (to_deleg) flags |= ISC_REQ_DELEGATE;
\r
281 winctx->maj_stat = p_InitializeSecurityContextA(&winctx->cred_handle,
\r
282 winctx->context_handle,
\r
286 SECURITY_NATIVE_DREP,
\r
294 /* prepare for the next round */
\r
295 winctx->context_handle = &winctx->context;
\r
296 send_tok->value = wsend_tok.pvBuffer;
\r
297 send_tok->length = wsend_tok.cbBuffer;
\r
299 /* check & return our status */
\r
300 if (winctx->maj_stat==SEC_E_OK) return SSH_GSS_S_COMPLETE;
\r
301 if (winctx->maj_stat==SEC_I_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;
\r
303 return SSH_GSS_FAILURE;
\r
306 static Ssh_gss_stat ssh_sspi_free_tok(struct ssh_gss_library *lib,
\r
307 Ssh_gss_buf *send_tok)
\r
310 if (send_tok == NULL) return SSH_GSS_FAILURE;
\r
312 /* free Windows buffer */
\r
313 p_FreeContextBuffer(send_tok->value);
\r
314 SSH_GSS_CLEAR_BUF(send_tok);
\r
319 static Ssh_gss_stat ssh_sspi_release_cred(struct ssh_gss_library *lib,
\r
322 winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) *ctx;
\r
325 if (winctx == NULL) return SSH_GSS_FAILURE;
\r
327 /* free Windows data */
\r
328 p_FreeCredentialsHandle(&winctx->cred_handle);
\r
329 p_DeleteSecurityContext(&winctx->context);
\r
331 /* delete our "wrapper" structure */
\r
333 *ctx = (Ssh_gss_ctx) NULL;
\r
339 static Ssh_gss_stat ssh_sspi_release_name(struct ssh_gss_library *lib,
\r
340 Ssh_gss_name *srv_name)
\r
342 char *pStr= (char *) *srv_name;
\r
344 if (pStr == NULL) return SSH_GSS_FAILURE;
\r
346 *srv_name = (Ssh_gss_name) NULL;
\r
351 static Ssh_gss_stat ssh_sspi_display_status(struct ssh_gss_library *lib,
\r
352 Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
\r
354 winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) ctx;
\r
357 if (winctx == NULL) return SSH_GSS_FAILURE;
\r
359 /* decode the error code */
\r
360 switch (winctx->maj_stat) {
\r
361 case SEC_E_OK: msg="SSPI status OK"; break;
\r
362 case SEC_E_INVALID_HANDLE: msg="The handle passed to the function"
\r
365 case SEC_E_TARGET_UNKNOWN: msg="The target was not recognized."; break;
\r
366 case SEC_E_LOGON_DENIED: msg="The logon failed."; break;
\r
367 case SEC_E_INTERNAL_ERROR: msg="The Local Security Authority cannot"
\r
370 case SEC_E_NO_CREDENTIALS: msg="No credentials are available in the"
\r
371 " security package.";
\r
373 case SEC_E_NO_AUTHENTICATING_AUTHORITY:
\r
374 msg="No authority could be contacted for authentication."
\r
375 "The domain name of the authenticating party could be wrong,"
\r
376 " the domain could be unreachable, or there might have been"
\r
377 " a trust relationship failure.";
\r
379 case SEC_E_INSUFFICIENT_MEMORY:
\r
380 msg="One or more of the SecBufferDesc structures passed as"
\r
381 " an OUT parameter has a buffer that is too small.";
\r
383 case SEC_E_INVALID_TOKEN:
\r
384 msg="The error is due to a malformed input token, such as a"
\r
385 " token corrupted in transit, a token"
\r
386 " of incorrect size, or a token passed into the wrong"
\r
387 " security package. Passing a token to"
\r
388 " the wrong package can happen if client and server did not"
\r
389 " negotiate the proper security package.";
\r
392 msg = "Internal SSPI error";
\r
396 buf->value = dupstr(msg);
\r
397 buf->length = strlen(buf->value);
\r
402 static Ssh_gss_stat ssh_sspi_get_mic(struct ssh_gss_library *lib,
\r
403 Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
\r
406 winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx;
\r
407 SecPkgContext_Sizes ContextSizes;
\r
408 SecBufferDesc InputBufferDescriptor;
\r
409 SecBuffer InputSecurityToken[2];
\r
411 if (winctx == NULL) return SSH_GSS_FAILURE;
\r
413 winctx->maj_stat = 0;
\r
415 memset(&ContextSizes, 0, sizeof(ContextSizes));
\r
417 winctx->maj_stat = p_QueryContextAttributesA(&winctx->context,
\r
421 if (winctx->maj_stat != SEC_E_OK ||
\r
422 ContextSizes.cbMaxSignature == 0)
\r
423 return winctx->maj_stat;
\r
425 InputBufferDescriptor.cBuffers = 2;
\r
426 InputBufferDescriptor.pBuffers = InputSecurityToken;
\r
427 InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
\r
428 InputSecurityToken[0].BufferType = SECBUFFER_DATA;
\r
429 InputSecurityToken[0].cbBuffer = buf->length;
\r
430 InputSecurityToken[0].pvBuffer = buf->value;
\r
431 InputSecurityToken[1].BufferType = SECBUFFER_TOKEN;
\r
432 InputSecurityToken[1].cbBuffer = ContextSizes.cbMaxSignature;
\r
433 InputSecurityToken[1].pvBuffer = snewn(ContextSizes.cbMaxSignature, char);
\r
435 winctx->maj_stat = p_MakeSignature(&winctx->context,
\r
437 &InputBufferDescriptor,
\r
440 if (winctx->maj_stat == SEC_E_OK) {
\r
441 hash->length = InputSecurityToken[1].cbBuffer;
\r
442 hash->value = InputSecurityToken[1].pvBuffer;
\r
445 return winctx->maj_stat;
\r
448 static Ssh_gss_stat ssh_sspi_free_mic(struct ssh_gss_library *lib,
\r
451 sfree(hash->value);
\r
455 static void ssh_sspi_bind_fns(struct ssh_gss_library *lib)
\r
457 lib->indicate_mech = ssh_sspi_indicate_mech;
\r
458 lib->import_name = ssh_sspi_import_name;
\r
459 lib->release_name = ssh_sspi_release_name;
\r
460 lib->init_sec_context = ssh_sspi_init_sec_context;
\r
461 lib->free_tok = ssh_sspi_free_tok;
\r
462 lib->acquire_cred = ssh_sspi_acquire_cred;
\r
463 lib->release_cred = ssh_sspi_release_cred;
\r
464 lib->get_mic = ssh_sspi_get_mic;
\r
465 lib->free_mic = ssh_sspi_free_mic;
\r
466 lib->display_status = ssh_sspi_display_status;
\r
471 /* Dummy function so this source file defines something if NO_GSSAPI
\r
474 void ssh_gss_init(void)
\r