Patch from Alejandro Sedeno, somewhat modified by me, which
[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
15struct ssh_gss_library ssh_gss_libraries[2];
16int n_ssh_gss_libraries = 0;
17static int initialised = FALSE;
18
19const int ngsslibs = 2;
20const char *const gsslibnames[2] = {
21 "GSSAPI32.DLL (MIT Kerberos)",
22 "SSPI.DLL (Microsoft SSPI)",
23};
24const struct keyval gsslibkeywords[] = {
25 { "gssapi32", 0 },
26 { "sspi", 1 },
27};
28
65d1432e 29DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
30 AcquireCredentialsHandleA,
31 (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID,
32 PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp));
33DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
34 InitializeSecurityContextA,
35 (PCredHandle, PCtxtHandle, SEC_CHAR *, ULONG, ULONG,
36 ULONG, PSecBufferDesc, ULONG, PCtxtHandle,
37 PSecBufferDesc, PULONG, PTimeStamp));
38DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
39 FreeContextBuffer,
40 (PVOID));
41DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
42 FreeCredentialsHandle,
43 (PCredHandle));
44DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
45 DeleteSecurityContext,
46 (PCtxtHandle));
47DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
48 QueryContextAttributesA,
49 (PCtxtHandle, ULONG, PVOID));
50DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
51 MakeSignature,
52 (PCtxtHandle, ULONG, PSecBufferDesc, ULONG));
42af6a67 53
42af6a67 54typedef struct winSsh_gss_ctx {
55 unsigned long maj_stat;
56 unsigned long min_stat;
57 CredHandle cred_handle;
58 CtxtHandle context;
59 PCtxtHandle context_handle;
60 TimeStamp expiry;
61} winSsh_gss_ctx;
62
63
64const Ssh_gss_buf gss_mech_krb5={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};
65
b3d375b2 66const char *gsslogmsg = NULL;
67
68static void ssh_sspi_bind_fns(struct ssh_gss_library *lib);
69
70void ssh_gss_init(void)
42af6a67 71{
b3d375b2 72 HMODULE module;
73
74 if (initialised) return;
75 initialised = TRUE;
76
77 /* MIT Kerberos GSSAPI implementation */
78 /* TODO: For 64-bit builds, check for gssapi64.dll */
79 module = LoadLibrary("gssapi32.dll");
80 if (module) {
81 struct ssh_gss_library *lib =
82 &ssh_gss_libraries[n_ssh_gss_libraries++];
83
84 lib->id = 0;
85 lib->gsslogmsg = "Using GSSAPI from GSSAPI32.DLL";
86
87#define BIND_GSS_FN(name) \
88 lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name)
89
90 BIND_GSS_FN(delete_sec_context);
91 BIND_GSS_FN(display_status);
92 BIND_GSS_FN(get_mic);
93 BIND_GSS_FN(import_name);
94 BIND_GSS_FN(init_sec_context);
95 BIND_GSS_FN(release_buffer);
96 BIND_GSS_FN(release_cred);
97 BIND_GSS_FN(release_name);
98
99#undef BIND_GSS_FN
100
101 ssh_gssapi_bind_fns(lib);
102 }
103
104 /* Microsoft SSPI Implementation */
105 module = LoadLibrary("secur32.dll");
106 if (module) {
107 struct ssh_gss_library *lib =
108 &ssh_gss_libraries[n_ssh_gss_libraries++];
109
110 lib->id = 1;
111 lib->gsslogmsg = "Using SSPI from SECUR32.DLL";
112
113 GET_WINDOWS_FUNCTION(module, AcquireCredentialsHandleA);
114 GET_WINDOWS_FUNCTION(module, InitializeSecurityContextA);
115 GET_WINDOWS_FUNCTION(module, FreeContextBuffer);
116 GET_WINDOWS_FUNCTION(module, FreeCredentialsHandle);
117 GET_WINDOWS_FUNCTION(module, DeleteSecurityContext);
118 GET_WINDOWS_FUNCTION(module, QueryContextAttributesA);
119 GET_WINDOWS_FUNCTION(module, MakeSignature);
120
121 ssh_sspi_bind_fns(lib);
42af6a67 122 }
42af6a67 123}
124
b3d375b2 125static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib,
126 Ssh_gss_buf *mech)
42af6a67 127{
128 *mech = gss_mech_krb5;
129 return SSH_GSS_OK;
130}
131
132
b3d375b2 133static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib,
134 char *host, Ssh_gss_name *srv_name)
42af6a67 135{
136 char *pStr;
137
138 /* Check hostname */
139 if (host == NULL) return SSH_GSS_FAILURE;
140
141 /* copy it into form host/FQDN */
142 pStr = dupcat("host/", host, NULL);
143
144 *srv_name = (Ssh_gss_name) pStr;
145
146 return SSH_GSS_OK;
147}
148
b3d375b2 149static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib,
150 Ssh_gss_ctx *ctx)
42af6a67 151{
152 winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx);
94fe9122 153 memset(winctx, 0, sizeof(winSsh_gss_ctx));
42af6a67 154
155 /* prepare our "wrapper" structure */
156 winctx->maj_stat = winctx->min_stat = SEC_E_OK;
157 winctx->context_handle = NULL;
158
159 /* Specifying no principal name here means use the credentials of
160 the current logged-in user */
161
162 winctx->maj_stat = p_AcquireCredentialsHandleA(NULL,
163 "Kerberos",
164 SECPKG_CRED_OUTBOUND,
165 NULL,
166 NULL,
167 NULL,
168 NULL,
169 &winctx->cred_handle,
170 &winctx->expiry);
171
172 if (winctx->maj_stat != SEC_E_OK) return SSH_GSS_FAILURE;
173
174 *ctx = (Ssh_gss_ctx) winctx;
175 return SSH_GSS_OK;
176}
177
178
b3d375b2 179static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib,
180 Ssh_gss_ctx *ctx,
181 Ssh_gss_name srv_name,
182 int to_deleg,
183 Ssh_gss_buf *recv_tok,
184 Ssh_gss_buf *send_tok)
42af6a67 185{
186 winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) *ctx;
86557057 187 SecBuffer wsend_tok = {send_tok->length,SECBUFFER_TOKEN,send_tok->value};
188 SecBuffer wrecv_tok = {recv_tok->length,SECBUFFER_TOKEN,recv_tok->value};
42af6a67 189 SecBufferDesc output_desc={SECBUFFER_VERSION,1,&wsend_tok};
190 SecBufferDesc input_desc ={SECBUFFER_VERSION,1,&wrecv_tok};
191 unsigned long flags=ISC_REQ_MUTUAL_AUTH|ISC_REQ_REPLAY_DETECT|
192 ISC_REQ_CONFIDENTIALITY|ISC_REQ_ALLOCATE_MEMORY;
193 unsigned long ret_flags=0;
194
195 /* check if we have to delegate ... */
196 if (to_deleg) flags |= ISC_REQ_DELEGATE;
197 winctx->maj_stat = p_InitializeSecurityContextA(&winctx->cred_handle,
198 winctx->context_handle,
199 (char*) srv_name,
200 flags,
201 0, /* reserved */
202 SECURITY_NATIVE_DREP,
203 &input_desc,
204 0, /* reserved */
205 &winctx->context,
206 &output_desc,
207 &ret_flags,
208 &winctx->expiry);
209
210 /* prepare for the next round */
211 winctx->context_handle = &winctx->context;
86557057 212 send_tok->value = wsend_tok.pvBuffer;
213 send_tok->length = wsend_tok.cbBuffer;
42af6a67 214
215 /* check & return our status */
216 if (winctx->maj_stat==SEC_E_OK) return SSH_GSS_S_COMPLETE;
217 if (winctx->maj_stat==SEC_I_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;
218
219 return SSH_GSS_FAILURE;
220}
221
b3d375b2 222static Ssh_gss_stat ssh_sspi_free_tok(struct ssh_gss_library *lib,
223 Ssh_gss_buf *send_tok)
42af6a67 224{
225 /* check input */
226 if (send_tok == NULL) return SSH_GSS_FAILURE;
227
228 /* free Windows buffer */
86557057 229 p_FreeContextBuffer(send_tok->value);
230 SSH_GSS_CLEAR_BUF(send_tok);
42af6a67 231
232 return SSH_GSS_OK;
233}
234
b3d375b2 235static Ssh_gss_stat ssh_sspi_release_cred(struct ssh_gss_library *lib,
236 Ssh_gss_ctx *ctx)
42af6a67 237{
238 winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) *ctx;
239
240 /* check input */
241 if (winctx == NULL) return SSH_GSS_FAILURE;
242
243 /* free Windows data */
244 p_FreeCredentialsHandle(&winctx->cred_handle);
245 p_DeleteSecurityContext(&winctx->context);
246
247 /* delete our "wrapper" structure */
248 sfree(winctx);
249 *ctx = (Ssh_gss_ctx) NULL;
250
251 return SSH_GSS_OK;
252}
253
254
b3d375b2 255static Ssh_gss_stat ssh_sspi_release_name(struct ssh_gss_library *lib,
256 Ssh_gss_name *srv_name)
42af6a67 257{
258 char *pStr= (char *) *srv_name;
259
260 if (pStr == NULL) return SSH_GSS_FAILURE;
261 sfree(pStr);
262 *srv_name = (Ssh_gss_name) NULL;
263
264 return SSH_GSS_OK;
265}
266
b3d375b2 267static Ssh_gss_stat ssh_sspi_display_status(struct ssh_gss_library *lib,
268 Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
42af6a67 269{
270 winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) ctx;
271 char *msg;
272
273 if (winctx == NULL) return SSH_GSS_FAILURE;
274
275 /* decode the error code */
276 switch (winctx->maj_stat) {
277 case SEC_E_OK: msg="SSPI status OK"; break;
278 case SEC_E_INVALID_HANDLE: msg="The handle passed to the function"
279 " is invalid.";
280 break;
281 case SEC_E_TARGET_UNKNOWN: msg="The target was not recognized."; break;
282 case SEC_E_LOGON_DENIED: msg="The logon failed."; break;
283 case SEC_E_INTERNAL_ERROR: msg="The Local Security Authority cannot"
284 " be contacted.";
285 break;
286 case SEC_E_NO_CREDENTIALS: msg="No credentials are available in the"
287 " security package.";
288 break;
289 case SEC_E_NO_AUTHENTICATING_AUTHORITY:
290 msg="No authority could be contacted for authentication."
291 "The domain name of the authenticating party could be wrong,"
292 " the domain could be unreachable, or there might have been"
293 " a trust relationship failure.";
294 break;
295 case SEC_E_INSUFFICIENT_MEMORY:
296 msg="One or more of the SecBufferDesc structures passed as"
297 " an OUT parameter has a buffer that is too small.";
298 break;
299 case SEC_E_INVALID_TOKEN:
300 msg="The error is due to a malformed input token, such as a"
301 " token corrupted in transit, a token"
302 " of incorrect size, or a token passed into the wrong"
303 " security package. Passing a token to"
304 " the wrong package can happen if client and server did not"
305 " negotiate the proper security package.";
306 break;
307 default:
308 msg = "Internal SSPI error";
309 break;
310 }
311
86557057 312 buf->value = dupstr(msg);
2c19a01f 313 buf->length = strlen(buf->value);
42af6a67 314
315 return SSH_GSS_OK;
316}
317
b3d375b2 318static Ssh_gss_stat ssh_sspi_get_mic(struct ssh_gss_library *lib,
319 Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
320 Ssh_gss_buf *hash)
42af6a67 321{
322 winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx;
323 SecPkgContext_Sizes ContextSizes;
324 SecBufferDesc InputBufferDescriptor;
325 SecBuffer InputSecurityToken[2];
326
327 if (winctx == NULL) return SSH_GSS_FAILURE;
328
329 winctx->maj_stat = 0;
330
331 memset(&ContextSizes, 0, sizeof(ContextSizes));
332
333 winctx->maj_stat = p_QueryContextAttributesA(&winctx->context,
334 SECPKG_ATTR_SIZES,
335 &ContextSizes);
336
337 if (winctx->maj_stat != SEC_E_OK ||
338 ContextSizes.cbMaxSignature == 0)
339 return winctx->maj_stat;
340
341 InputBufferDescriptor.cBuffers = 2;
342 InputBufferDescriptor.pBuffers = InputSecurityToken;
343 InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
344 InputSecurityToken[0].BufferType = SECBUFFER_DATA;
86557057 345 InputSecurityToken[0].cbBuffer = buf->length;
346 InputSecurityToken[0].pvBuffer = buf->value;
42af6a67 347 InputSecurityToken[1].BufferType = SECBUFFER_TOKEN;
348 InputSecurityToken[1].cbBuffer = ContextSizes.cbMaxSignature;
349 InputSecurityToken[1].pvBuffer = snewn(ContextSizes.cbMaxSignature, char);
350
351 winctx->maj_stat = p_MakeSignature(&winctx->context,
352 0,
353 &InputBufferDescriptor,
354 0);
355
356 if (winctx->maj_stat == SEC_E_OK) {
86557057 357 hash->length = InputSecurityToken[1].cbBuffer;
358 hash->value = InputSecurityToken[1].pvBuffer;
42af6a67 359 }
360
361 return winctx->maj_stat;
362}
363
b3d375b2 364static Ssh_gss_stat ssh_sspi_free_mic(struct ssh_gss_library *lib,
365 Ssh_gss_buf *hash)
42af6a67 366{
86557057 367 sfree(hash->value);
42af6a67 368 return SSH_GSS_OK;
369}
370
b3d375b2 371static void ssh_sspi_bind_fns(struct ssh_gss_library *lib)
372{
373 lib->indicate_mech = ssh_sspi_indicate_mech;
374 lib->import_name = ssh_sspi_import_name;
375 lib->release_name = ssh_sspi_release_name;
376 lib->init_sec_context = ssh_sspi_init_sec_context;
377 lib->free_tok = ssh_sspi_free_tok;
378 lib->acquire_cred = ssh_sspi_acquire_cred;
379 lib->release_cred = ssh_sspi_release_cred;
380 lib->get_mic = ssh_sspi_get_mic;
381 lib->free_mic = ssh_sspi_free_mic;
382 lib->display_status = ssh_sspi_display_status;
383}
384
42af6a67 385#else
386
387/* Dummy function so this source file defines something if NO_GSSAPI
388 is defined. */
389
b3d375b2 390void ssh_gss_init(void)
42af6a67 391{
42af6a67 392}
393
394#endif