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