Cleanups of the GSSAPI support. On Windows, standard GSS libraries
[u/mdw/putty] / windows / wingss.c
CommitLineData
42af6a67 1#ifndef NO_GSSAPI
2
a638600c 3#include "putty.h"
4
42af6a67 5#include <security.h>
a638600c 6
b3d375b2 7#include "pgssapi.h"
42af6a67 8#include "sshgss.h"
b3d375b2 9#include "sshgssc.h"
10
42af6a67 11#include "misc.h"
12
b3d375b2 13/* Windows code to set up the GSSAPI library list. */
14
1e00c92b 15const int ngsslibs = 3;
16const char *const gsslibnames[3] = {
17 "MIT Kerberos GSSAPI32.DLL",
18 "Microsoft SSPI SECUR32.DLL",
19 "User-specified GSSAPI DLL",
b3d375b2 20};
21const struct keyval gsslibkeywords[] = {
22 { "gssapi32", 0 },
23 { "sspi", 1 },
1e00c92b 24 { "custom", 2 },
b3d375b2 25};
26
65d1432e 27DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
28 AcquireCredentialsHandleA,
29 (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID,
30 PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp));
31DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
32 InitializeSecurityContextA,
33 (PCredHandle, PCtxtHandle, SEC_CHAR *, ULONG, ULONG,
34 ULONG, PSecBufferDesc, ULONG, PCtxtHandle,
35 PSecBufferDesc, PULONG, PTimeStamp));
36DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
37 FreeContextBuffer,
38 (PVOID));
39DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
40 FreeCredentialsHandle,
41 (PCredHandle));
42DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
43 DeleteSecurityContext,
44 (PCtxtHandle));
45DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
46 QueryContextAttributesA,
47 (PCtxtHandle, ULONG, PVOID));
48DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
49 MakeSignature,
50 (PCtxtHandle, ULONG, PSecBufferDesc, ULONG));
42af6a67 51
42af6a67 52typedef 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;
60
61
62const Ssh_gss_buf gss_mech_krb5={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};
63
b3d375b2 64const char *gsslogmsg = NULL;
65
66static void ssh_sspi_bind_fns(struct ssh_gss_library *lib);
67
1e00c92b 68struct ssh_gss_liblist *ssh_gss_setup(const Config *cfg)
42af6a67 69{
b3d375b2 70 HMODULE module;
1e00c92b 71 HKEY regkey;
72 struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist);
b3d375b2 73
1e00c92b 74 list->libraries = snewn(3, struct ssh_gss_library);
75 list->nlibraries = 0;
b3d375b2 76
77 /* MIT Kerberos GSSAPI implementation */
78 /* TODO: For 64-bit builds, check for gssapi64.dll */
1e00c92b 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;
85
86 /* Find out the string length */
87 ret = RegQueryValueEx(regkey, "InstallDir", NULL, &type, NULL, &size);
88
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 strcat(buffer, "\\bin\\gssapi32.dll");
95 module = LoadLibrary(buffer);
96 }
97 sfree(buffer);
98 }
99 RegCloseKey(regkey);
100 }
b3d375b2 101 if (module) {
102 struct ssh_gss_library *lib =
1e00c92b 103 &list->libraries[list->nlibraries++];
b3d375b2 104
105 lib->id = 0;
106 lib->gsslogmsg = "Using GSSAPI from GSSAPI32.DLL";
1e00c92b 107 lib->handle = (void *)module;
b3d375b2 108
109#define BIND_GSS_FN(name) \
110 lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name)
111
112 BIND_GSS_FN(delete_sec_context);
113 BIND_GSS_FN(display_status);
114 BIND_GSS_FN(get_mic);
115 BIND_GSS_FN(import_name);
116 BIND_GSS_FN(init_sec_context);
117 BIND_GSS_FN(release_buffer);
118 BIND_GSS_FN(release_cred);
119 BIND_GSS_FN(release_name);
120
121#undef BIND_GSS_FN
122
123 ssh_gssapi_bind_fns(lib);
124 }
125
126 /* Microsoft SSPI Implementation */
bda368a5 127 module = load_system32_dll("secur32.dll");
b3d375b2 128 if (module) {
129 struct ssh_gss_library *lib =
1e00c92b 130 &list->libraries[list->nlibraries++];
b3d375b2 131
132 lib->id = 1;
133 lib->gsslogmsg = "Using SSPI from SECUR32.DLL";
1e00c92b 134 lib->handle = (void *)module;
b3d375b2 135
136 GET_WINDOWS_FUNCTION(module, AcquireCredentialsHandleA);
137 GET_WINDOWS_FUNCTION(module, InitializeSecurityContextA);
138 GET_WINDOWS_FUNCTION(module, FreeContextBuffer);
139 GET_WINDOWS_FUNCTION(module, FreeCredentialsHandle);
140 GET_WINDOWS_FUNCTION(module, DeleteSecurityContext);
141 GET_WINDOWS_FUNCTION(module, QueryContextAttributesA);
142 GET_WINDOWS_FUNCTION(module, MakeSignature);
143
144 ssh_sspi_bind_fns(lib);
42af6a67 145 }
1e00c92b 146
147 /*
148 * Custom GSSAPI DLL.
149 */
150 module = NULL;
151 if (cfg->ssh_gss_custom.path[0]) {
152 module = LoadLibrary(cfg->ssh_gss_custom.path);
153 }
154 if (module) {
155 struct ssh_gss_library *lib =
156 &list->libraries[list->nlibraries++];
157
158 lib->id = 2;
159 lib->gsslogmsg = dupprintf("Using GSSAPI from user-specified"
160 " library '%s'", cfg->ssh_gss_custom.path);
161 lib->handle = (void *)module;
162
163#define BIND_GSS_FN(name) \
164 lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name)
165
166 BIND_GSS_FN(delete_sec_context);
167 BIND_GSS_FN(display_status);
168 BIND_GSS_FN(get_mic);
169 BIND_GSS_FN(import_name);
170 BIND_GSS_FN(init_sec_context);
171 BIND_GSS_FN(release_buffer);
172 BIND_GSS_FN(release_cred);
173 BIND_GSS_FN(release_name);
174
175#undef BIND_GSS_FN
176
177 ssh_gssapi_bind_fns(lib);
178 }
179
180
181 return list;
182}
183
184void ssh_gss_cleanup(struct ssh_gss_liblist *list)
185{
186 int i;
187
188 /*
189 * LoadLibrary and FreeLibrary are defined to employ reference
190 * counting in the case where the same library is repeatedly
191 * loaded, so even in a multiple-sessions-per-process context
192 * (not that we currently expect ever to have such a thing on
193 * Windows) it's safe to naively FreeLibrary everything here
194 * without worrying about destroying it under the feet of
195 * another SSH instance still using it.
196 */
197 for (i = 0; i < list->nlibraries; i++) {
198 FreeLibrary((HMODULE)list->libraries[i].handle);
199 if (list->libraries[i].id == 2) {
200 /* The 'custom' id involves a dynamically allocated message.
201 * Note that we must cast away the 'const' to free it. */
202 sfree((char *)list->libraries[i].gsslogmsg);
203 }
204 }
205 sfree(list->libraries);
206 sfree(list);
42af6a67 207}
208
b3d375b2 209static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib,
210 Ssh_gss_buf *mech)
42af6a67 211{
212 *mech = gss_mech_krb5;
213 return SSH_GSS_OK;
214}
215
216
b3d375b2 217static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib,
218 char *host, Ssh_gss_name *srv_name)
42af6a67 219{
220 char *pStr;
221
222 /* Check hostname */
223 if (host == NULL) return SSH_GSS_FAILURE;
224
225 /* copy it into form host/FQDN */
226 pStr = dupcat("host/", host, NULL);
227
228 *srv_name = (Ssh_gss_name) pStr;
229
230 return SSH_GSS_OK;
231}
232
b3d375b2 233static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib,
234 Ssh_gss_ctx *ctx)
42af6a67 235{
236 winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx);
94fe9122 237 memset(winctx, 0, sizeof(winSsh_gss_ctx));
42af6a67 238
239 /* prepare our "wrapper" structure */
240 winctx->maj_stat = winctx->min_stat = SEC_E_OK;
241 winctx->context_handle = NULL;
242
243 /* Specifying no principal name here means use the credentials of
244 the current logged-in user */
245
246 winctx->maj_stat = p_AcquireCredentialsHandleA(NULL,
247 "Kerberos",
248 SECPKG_CRED_OUTBOUND,
249 NULL,
250 NULL,
251 NULL,
252 NULL,
253 &winctx->cred_handle,
254 &winctx->expiry);
255
256 if (winctx->maj_stat != SEC_E_OK) return SSH_GSS_FAILURE;
257
258 *ctx = (Ssh_gss_ctx) winctx;
259 return SSH_GSS_OK;
260}
261
262
b3d375b2 263static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib,
264 Ssh_gss_ctx *ctx,
265 Ssh_gss_name srv_name,
266 int to_deleg,
267 Ssh_gss_buf *recv_tok,
268 Ssh_gss_buf *send_tok)
42af6a67 269{
270 winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) *ctx;
86557057 271 SecBuffer wsend_tok = {send_tok->length,SECBUFFER_TOKEN,send_tok->value};
272 SecBuffer wrecv_tok = {recv_tok->length,SECBUFFER_TOKEN,recv_tok->value};
42af6a67 273 SecBufferDesc output_desc={SECBUFFER_VERSION,1,&wsend_tok};
274 SecBufferDesc input_desc ={SECBUFFER_VERSION,1,&wrecv_tok};
275 unsigned long flags=ISC_REQ_MUTUAL_AUTH|ISC_REQ_REPLAY_DETECT|
276 ISC_REQ_CONFIDENTIALITY|ISC_REQ_ALLOCATE_MEMORY;
277 unsigned long ret_flags=0;
278
279 /* check if we have to delegate ... */
280 if (to_deleg) flags |= ISC_REQ_DELEGATE;
281 winctx->maj_stat = p_InitializeSecurityContextA(&winctx->cred_handle,
282 winctx->context_handle,
283 (char*) srv_name,
284 flags,
285 0, /* reserved */
286 SECURITY_NATIVE_DREP,
287 &input_desc,
288 0, /* reserved */
289 &winctx->context,
290 &output_desc,
291 &ret_flags,
292 &winctx->expiry);
293
294 /* prepare for the next round */
295 winctx->context_handle = &winctx->context;
86557057 296 send_tok->value = wsend_tok.pvBuffer;
297 send_tok->length = wsend_tok.cbBuffer;
42af6a67 298
299 /* check & return our status */
300 if (winctx->maj_stat==SEC_E_OK) return SSH_GSS_S_COMPLETE;
301 if (winctx->maj_stat==SEC_I_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;
302
303 return SSH_GSS_FAILURE;
304}
305
b3d375b2 306static Ssh_gss_stat ssh_sspi_free_tok(struct ssh_gss_library *lib,
307 Ssh_gss_buf *send_tok)
42af6a67 308{
309 /* check input */
310 if (send_tok == NULL) return SSH_GSS_FAILURE;
311
312 /* free Windows buffer */
86557057 313 p_FreeContextBuffer(send_tok->value);
314 SSH_GSS_CLEAR_BUF(send_tok);
42af6a67 315
316 return SSH_GSS_OK;
317}
318
b3d375b2 319static Ssh_gss_stat ssh_sspi_release_cred(struct ssh_gss_library *lib,
320 Ssh_gss_ctx *ctx)
42af6a67 321{
322 winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) *ctx;
323
324 /* check input */
325 if (winctx == NULL) return SSH_GSS_FAILURE;
326
327 /* free Windows data */
328 p_FreeCredentialsHandle(&winctx->cred_handle);
329 p_DeleteSecurityContext(&winctx->context);
330
331 /* delete our "wrapper" structure */
332 sfree(winctx);
333 *ctx = (Ssh_gss_ctx) NULL;
334
335 return SSH_GSS_OK;
336}
337
338
b3d375b2 339static Ssh_gss_stat ssh_sspi_release_name(struct ssh_gss_library *lib,
340 Ssh_gss_name *srv_name)
42af6a67 341{
342 char *pStr= (char *) *srv_name;
343
344 if (pStr == NULL) return SSH_GSS_FAILURE;
345 sfree(pStr);
346 *srv_name = (Ssh_gss_name) NULL;
347
348 return SSH_GSS_OK;
349}
350
b3d375b2 351static Ssh_gss_stat ssh_sspi_display_status(struct ssh_gss_library *lib,
352 Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
42af6a67 353{
354 winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) ctx;
355 char *msg;
356
357 if (winctx == NULL) return SSH_GSS_FAILURE;
358
359 /* decode the error code */
360 switch (winctx->maj_stat) {
361 case SEC_E_OK: msg="SSPI status OK"; break;
362 case SEC_E_INVALID_HANDLE: msg="The handle passed to the function"
363 " is invalid.";
364 break;
365 case SEC_E_TARGET_UNKNOWN: msg="The target was not recognized."; break;
366 case SEC_E_LOGON_DENIED: msg="The logon failed."; break;
367 case SEC_E_INTERNAL_ERROR: msg="The Local Security Authority cannot"
368 " be contacted.";
369 break;
370 case SEC_E_NO_CREDENTIALS: msg="No credentials are available in the"
371 " security package.";
372 break;
373 case SEC_E_NO_AUTHENTICATING_AUTHORITY:
374 msg="No authority could be contacted for authentication."
375 "The domain name of the authenticating party could be wrong,"
376 " the domain could be unreachable, or there might have been"
377 " a trust relationship failure.";
378 break;
379 case SEC_E_INSUFFICIENT_MEMORY:
380 msg="One or more of the SecBufferDesc structures passed as"
381 " an OUT parameter has a buffer that is too small.";
382 break;
383 case SEC_E_INVALID_TOKEN:
384 msg="The error is due to a malformed input token, such as a"
385 " token corrupted in transit, a token"
386 " of incorrect size, or a token passed into the wrong"
387 " security package. Passing a token to"
388 " the wrong package can happen if client and server did not"
389 " negotiate the proper security package.";
390 break;
391 default:
392 msg = "Internal SSPI error";
393 break;
394 }
395
86557057 396 buf->value = dupstr(msg);
2c19a01f 397 buf->length = strlen(buf->value);
42af6a67 398
399 return SSH_GSS_OK;
400}
401
b3d375b2 402static Ssh_gss_stat ssh_sspi_get_mic(struct ssh_gss_library *lib,
403 Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
404 Ssh_gss_buf *hash)
42af6a67 405{
406 winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx;
407 SecPkgContext_Sizes ContextSizes;
408 SecBufferDesc InputBufferDescriptor;
409 SecBuffer InputSecurityToken[2];
410
411 if (winctx == NULL) return SSH_GSS_FAILURE;
412
413 winctx->maj_stat = 0;
414
415 memset(&ContextSizes, 0, sizeof(ContextSizes));
416
417 winctx->maj_stat = p_QueryContextAttributesA(&winctx->context,
418 SECPKG_ATTR_SIZES,
419 &ContextSizes);
420
421 if (winctx->maj_stat != SEC_E_OK ||
422 ContextSizes.cbMaxSignature == 0)
423 return winctx->maj_stat;
424
425 InputBufferDescriptor.cBuffers = 2;
426 InputBufferDescriptor.pBuffers = InputSecurityToken;
427 InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
428 InputSecurityToken[0].BufferType = SECBUFFER_DATA;
86557057 429 InputSecurityToken[0].cbBuffer = buf->length;
430 InputSecurityToken[0].pvBuffer = buf->value;
42af6a67 431 InputSecurityToken[1].BufferType = SECBUFFER_TOKEN;
432 InputSecurityToken[1].cbBuffer = ContextSizes.cbMaxSignature;
433 InputSecurityToken[1].pvBuffer = snewn(ContextSizes.cbMaxSignature, char);
434
435 winctx->maj_stat = p_MakeSignature(&winctx->context,
436 0,
437 &InputBufferDescriptor,
438 0);
439
440 if (winctx->maj_stat == SEC_E_OK) {
86557057 441 hash->length = InputSecurityToken[1].cbBuffer;
442 hash->value = InputSecurityToken[1].pvBuffer;
42af6a67 443 }
444
445 return winctx->maj_stat;
446}
447
b3d375b2 448static Ssh_gss_stat ssh_sspi_free_mic(struct ssh_gss_library *lib,
449 Ssh_gss_buf *hash)
42af6a67 450{
86557057 451 sfree(hash->value);
42af6a67 452 return SSH_GSS_OK;
453}
454
b3d375b2 455static void ssh_sspi_bind_fns(struct ssh_gss_library *lib)
456{
457 lib->indicate_mech = ssh_sspi_indicate_mech;
458 lib->import_name = ssh_sspi_import_name;
459 lib->release_name = ssh_sspi_release_name;
460 lib->init_sec_context = ssh_sspi_init_sec_context;
461 lib->free_tok = ssh_sspi_free_tok;
462 lib->acquire_cred = ssh_sspi_acquire_cred;
463 lib->release_cred = ssh_sspi_release_cred;
464 lib->get_mic = ssh_sspi_get_mic;
465 lib->free_mic = ssh_sspi_free_mic;
466 lib->display_status = ssh_sspi_display_status;
467}
468
42af6a67 469#else
470
471/* Dummy function so this source file defines something if NO_GSSAPI
472 is defined. */
473
b3d375b2 474void ssh_gss_init(void)
42af6a67 475{
42af6a67 476}
477
478#endif