Can also refresh Rebase dialog by double clicking to delete conflict file
[TortoiseGit.git] / src / TortoisePlink / Windows / wingss.c
blob97198188ca15ccd8163a6ab6de8eed8d56135412
1 #ifndef NO_GSSAPI
3 #include "putty.h"
5 #include <security.h>
7 #include "pgssapi.h"
8 #include "sshgss.h"
9 #include "sshgssc.h"
11 #include "misc.h"
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,
37 FreeContextBuffer,
38 (PVOID));
39 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
40 FreeCredentialsHandle,
41 (PCredHandle));
42 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
43 DeleteSecurityContext,
44 (PCtxtHandle));
45 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
46 QueryContextAttributesA,
47 (PCtxtHandle, ULONG, PVOID));
48 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
49 MakeSignature,
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;
56 CtxtHandle context;
57 PCtxtHandle context_handle;
58 TimeStamp expiry;
59 } winSsh_gss_ctx;
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)
70 HMODULE module;
71 HKEY regkey;
72 struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist);
74 list->libraries = snewn(3, struct ssh_gss_library);
75 list->nlibraries = 0;
77 /* MIT Kerberos GSSAPI implementation */
78 /* TODO: For 64-bit builds, check for gssapi64.dll */
79 module = NULL;
80 if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MIT\\Kerberos", &regkey)
81 == ERROR_SUCCESS) {
82 DWORD type, size;
83 LONG ret;
84 char *buffer;
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) {
94 #ifdef _WIN64
95 strcat(buffer, "\\bin\\gssapi64.dll");
96 #else
97 strcat(buffer, "\\bin\\gssapi32.dll");
98 #endif
99 module = LoadLibrary(buffer);
101 sfree(buffer);
103 RegCloseKey(regkey);
105 if (module) {
106 struct ssh_gss_library *lib =
107 &list->libraries[list->nlibraries++];
109 lib->id = 0;
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);
125 #undef BIND_GSS_FN
127 ssh_gssapi_bind_fns(lib);
130 /* Microsoft SSPI Implementation */
131 module = load_system32_dll("secur32.dll");
132 if (module) {
133 struct ssh_gss_library *lib =
134 &list->libraries[list->nlibraries++];
136 lib->id = 1;
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);
152 * Custom GSSAPI DLL.
154 module = NULL;
155 if (cfg->ssh_gss_custom.path[0]) {
156 module = LoadLibrary(cfg->ssh_gss_custom.path);
158 if (module) {
159 struct ssh_gss_library *lib =
160 &list->libraries[list->nlibraries++];
162 lib->id = 2;
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);
179 #undef BIND_GSS_FN
181 ssh_gssapi_bind_fns(lib);
185 return list;
188 void ssh_gss_cleanup(struct ssh_gss_liblist *list)
190 int i;
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);
210 sfree(list);
213 static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib,
214 Ssh_gss_buf *mech)
216 *mech = gss_mech_krb5;
217 return SSH_GSS_OK;
221 static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib,
222 char *host, Ssh_gss_name *srv_name)
224 char *pStr;
226 /* Check hostname */
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;
234 return SSH_GSS_OK;
237 static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib,
238 Ssh_gss_ctx *ctx)
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,
251 "Kerberos",
252 SECPKG_CRED_OUTBOUND,
253 NULL,
254 NULL,
255 NULL,
256 NULL,
257 &winctx->cred_handle,
258 &winctx->expiry);
260 if (winctx->maj_stat != SEC_E_OK) return SSH_GSS_FAILURE;
262 *ctx = (Ssh_gss_ctx) winctx;
263 return SSH_GSS_OK;
267 static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib,
268 Ssh_gss_ctx *ctx,
269 Ssh_gss_name srv_name,
270 int to_deleg,
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,
287 (char*) srv_name,
288 flags,
289 0, /* reserved */
290 SECURITY_NATIVE_DREP,
291 &input_desc,
292 0, /* reserved */
293 &winctx->context,
294 &output_desc,
295 &ret_flags,
296 &winctx->expiry);
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)
313 /* check input */
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);
320 return SSH_GSS_OK;
323 static Ssh_gss_stat ssh_sspi_release_cred(struct ssh_gss_library *lib,
324 Ssh_gss_ctx *ctx)
326 winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) *ctx;
328 /* check input */
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 */
336 sfree(winctx);
337 *ctx = (Ssh_gss_ctx) NULL;
339 return SSH_GSS_OK;
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;
349 sfree(pStr);
350 *srv_name = (Ssh_gss_name) NULL;
352 return SSH_GSS_OK;
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;
359 char *msg;
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"
367 " is invalid.";
368 break;
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"
372 " be contacted.";
373 break;
374 case SEC_E_NO_CREDENTIALS: msg="No credentials are available in the"
375 " security package.";
376 break;
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.";
382 break;
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.";
386 break;
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.";
394 break;
395 default:
396 msg = "Internal SSPI error";
397 break;
400 buf->value = dupstr(msg);
401 buf->length = strlen(buf->value);
403 return SSH_GSS_OK;
406 static Ssh_gss_stat ssh_sspi_get_mic(struct ssh_gss_library *lib,
407 Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
408 Ssh_gss_buf *hash)
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,
422 SECPKG_ATTR_SIZES,
423 &ContextSizes);
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,
453 Ssh_gss_buf *hash)
455 sfree(hash->value);
456 return SSH_GSS_OK;
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;
473 #else
475 /* Dummy function so this source file defines something if NO_GSSAPI
476 is defined. */
478 void ssh_gss_init(void)
482 #endif