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 | |
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)", |
186d2907 |
22 | "SECUR32.DLL (Microsoft SSPI)", |
b3d375b2 |
23 | }; |
24 | const struct keyval gsslibkeywords[] = { |
25 | { "gssapi32", 0 }, |
26 | { "sspi", 1 }, |
27 | }; |
28 | |
65d1432e |
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)); |
42af6a67 |
53 | |
42af6a67 |
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 | |
b3d375b2 |
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) |
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 */ |
bda368a5 |
105 | module = load_system32_dll("secur32.dll"); |
b3d375b2 |
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 |
125 | static 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 |
133 | static 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 |
149 | static 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 |
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) |
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 |
222 | static 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 |
235 | static 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 |
255 | static 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 |
267 | static 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 |
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) |
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 |
364 | static 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 |
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 | |
42af6a67 |
385 | #else |
386 | |
387 | /* Dummy function so this source file defines something if NO_GSSAPI |
388 | is defined. */ |
389 | |
b3d375b2 |
390 | void ssh_gss_init(void) |
42af6a67 |
391 | { |
42af6a67 |
392 | } |
393 | |
394 | #endif |