]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/wingss.c
Patch from Alejandro Sedeno, somewhat modified by me, which
[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 struct ssh_gss_library ssh_gss_libraries[2];
16 int n_ssh_gss_libraries = 0;
17 static int initialised = FALSE;
18
19 const int ngsslibs = 2;
20 const char *const gsslibnames[2] = {
21     "GSSAPI32.DLL (MIT Kerberos)",
22     "SSPI.DLL (Microsoft SSPI)",
23 };
24 const struct keyval gsslibkeywords[] = {
25     { "gssapi32", 0 },
26     { "sspi", 1 },
27 };
28
29 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
30                       AcquireCredentialsHandleA,
31                       (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID,
32                        PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp));
33 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
34                       InitializeSecurityContextA,
35                       (PCredHandle, PCtxtHandle, SEC_CHAR *, ULONG, ULONG,
36                        ULONG, PSecBufferDesc, ULONG, PCtxtHandle,
37                        PSecBufferDesc, PULONG, PTimeStamp));
38 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
39                       FreeContextBuffer,
40                       (PVOID));
41 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
42                       FreeCredentialsHandle,
43                       (PCredHandle));
44 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
45                       DeleteSecurityContext,
46                       (PCtxtHandle));
47 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
48                       QueryContextAttributesA,
49                       (PCtxtHandle, ULONG, PVOID));
50 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
51                       MakeSignature,
52                       (PCtxtHandle, ULONG, PSecBufferDesc, ULONG));
53
54 typedef 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
64 const Ssh_gss_buf gss_mech_krb5={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};
65
66 const char *gsslogmsg = NULL;
67
68 static void ssh_sspi_bind_fns(struct ssh_gss_library *lib);
69
70 void ssh_gss_init(void)
71 {
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);
122     }
123 }
124
125 static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib,
126                                            Ssh_gss_buf *mech)
127 {
128     *mech = gss_mech_krb5;
129     return SSH_GSS_OK;
130 }
131
132
133 static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib,
134                                          char *host, Ssh_gss_name *srv_name)
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
149 static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib,
150                                           Ssh_gss_ctx *ctx)
151 {
152     winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx);
153     memset(winctx, 0, sizeof(winSsh_gss_ctx));
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
179 static 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)
185 {
186     winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) *ctx;
187     SecBuffer wsend_tok = {send_tok->length,SECBUFFER_TOKEN,send_tok->value};
188     SecBuffer wrecv_tok = {recv_tok->length,SECBUFFER_TOKEN,recv_tok->value};
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;
212     send_tok->value = wsend_tok.pvBuffer;
213     send_tok->length = wsend_tok.cbBuffer;
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
222 static Ssh_gss_stat ssh_sspi_free_tok(struct ssh_gss_library *lib,
223                                       Ssh_gss_buf *send_tok)
224 {
225     /* check input */
226     if (send_tok == NULL) return SSH_GSS_FAILURE;
227
228     /* free Windows buffer */
229     p_FreeContextBuffer(send_tok->value);
230     SSH_GSS_CLEAR_BUF(send_tok);
231     
232     return SSH_GSS_OK;
233 }
234
235 static Ssh_gss_stat ssh_sspi_release_cred(struct ssh_gss_library *lib,
236                                           Ssh_gss_ctx *ctx)
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
255 static Ssh_gss_stat ssh_sspi_release_name(struct ssh_gss_library *lib,
256                                           Ssh_gss_name *srv_name)
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
267 static Ssh_gss_stat ssh_sspi_display_status(struct ssh_gss_library *lib,
268                                             Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
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
312     buf->value = dupstr(msg);
313     buf->length = strlen(buf->value);
314     
315     return SSH_GSS_OK;
316 }
317
318 static 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)
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;
345     InputSecurityToken[0].cbBuffer = buf->length;
346     InputSecurityToken[0].pvBuffer = buf->value;
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) {
357         hash->length = InputSecurityToken[1].cbBuffer;
358         hash->value = InputSecurityToken[1].pvBuffer;
359     }
360
361     return winctx->maj_stat;
362 }
363
364 static Ssh_gss_stat ssh_sspi_free_mic(struct ssh_gss_library *lib,
365                                       Ssh_gss_buf *hash)
366 {
367     sfree(hash->value);
368     return SSH_GSS_OK;
369 }
370
371 static 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
385 #else
386
387 /* Dummy function so this source file defines something if NO_GSSAPI
388    is defined. */
389
390 void ssh_gss_init(void)
391 {
392 }
393
394 #endif