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