OSDN Git Service

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