Initial commit of GSSAPI Kerberos support.
[u/mdw/putty] / windows / wingss.c
1 #ifndef NO_GSSAPI
2
3 #include <windows.h>
4 #define SECURITY_WIN32
5 #include <security.h>
6 #include "sshgss.h"
7 #include "misc.h"
8
9 #define NOTHING
10 #define DECL_SSPI_FUNCTION(linkage, rettype, name, params) \
11 typedef rettype (WINAPI *t_##name) params; \
12 linkage t_##name p_##name
13 #define GET_SSPI_FUNCTION(module, name) \
14 p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL
15
16 DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
17 AcquireCredentialsHandleA,
18 (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID,
19 PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp));
20 DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
21 InitializeSecurityContextA,
22 (PCredHandle, PCtxtHandle, SEC_CHAR *, ULONG, ULONG,
23 ULONG, PSecBufferDesc, ULONG, PCtxtHandle,
24 PSecBufferDesc, PULONG, PTimeStamp));
25 DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
26 FreeContextBuffer,
27 (PVOID));
28 DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
29 FreeCredentialsHandle,
30 (PCredHandle));
31 DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
32 DeleteSecurityContext,
33 (PCtxtHandle));
34 DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
35 QueryContextAttributesA,
36 (PCtxtHandle, ULONG, PVOID));
37 DECL_SSPI_FUNCTION(static, SECURITY_STATUS,
38 MakeSignature,
39 (PCtxtHandle, ULONG, PSecBufferDesc, ULONG));
40
41 static HMODULE security_module = NULL;
42
43 typedef struct winSsh_gss_ctx {
44 unsigned long maj_stat;
45 unsigned long min_stat;
46 CredHandle cred_handle;
47 CtxtHandle context;
48 PCtxtHandle context_handle;
49 TimeStamp expiry;
50 } winSsh_gss_ctx;
51
52
53 const Ssh_gss_buf gss_mech_krb5={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};
54
55 int ssh_gss_init(void)
56 {
57 if (security_module)
58 return 1; /* already initialised */
59
60 security_module = LoadLibrary("secur32.dll");
61 if (security_module) {
62 GET_SSPI_FUNCTION(security_module, AcquireCredentialsHandleA);
63 GET_SSPI_FUNCTION(security_module, InitializeSecurityContextA);
64 GET_SSPI_FUNCTION(security_module, FreeContextBuffer);
65 GET_SSPI_FUNCTION(security_module, FreeCredentialsHandle);
66 GET_SSPI_FUNCTION(security_module, DeleteSecurityContext);
67 GET_SSPI_FUNCTION(security_module, QueryContextAttributesA);
68 GET_SSPI_FUNCTION(security_module, MakeSignature);
69 return 1;
70 }
71 return 0;
72 }
73
74 Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *mech)
75 {
76 *mech = gss_mech_krb5;
77 return SSH_GSS_OK;
78 }
79
80
81 Ssh_gss_stat ssh_gss_import_name(char *host, Ssh_gss_name *srv_name)
82 {
83 char *pStr;
84
85 /* Check hostname */
86 if (host == NULL) return SSH_GSS_FAILURE;
87
88 /* copy it into form host/FQDN */
89 pStr = dupcat("host/", host, NULL);
90
91 *srv_name = (Ssh_gss_name) pStr;
92
93 return SSH_GSS_OK;
94 }
95
96 Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *ctx)
97 {
98 winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx);
99
100 /* prepare our "wrapper" structure */
101 winctx->maj_stat = winctx->min_stat = SEC_E_OK;
102 winctx->context_handle = NULL;
103
104 /* Specifying no principal name here means use the credentials of
105 the current logged-in user */
106
107 winctx->maj_stat = p_AcquireCredentialsHandleA(NULL,
108 "Kerberos",
109 SECPKG_CRED_OUTBOUND,
110 NULL,
111 NULL,
112 NULL,
113 NULL,
114 &winctx->cred_handle,
115 &winctx->expiry);
116
117 if (winctx->maj_stat != SEC_E_OK) return SSH_GSS_FAILURE;
118
119 *ctx = (Ssh_gss_ctx) winctx;
120 return SSH_GSS_OK;
121 }
122
123
124 Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx,
125 Ssh_gss_name srv_name,
126 int to_deleg,
127 Ssh_gss_buf *recv_tok,
128 Ssh_gss_buf *send_tok)
129 {
130 winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) *ctx;
131 SecBuffer wsend_tok = {send_tok->len,SECBUFFER_TOKEN,send_tok->data};
132 SecBuffer wrecv_tok = {recv_tok->len,SECBUFFER_TOKEN,recv_tok->data};
133 SecBufferDesc output_desc={SECBUFFER_VERSION,1,&wsend_tok};
134 SecBufferDesc input_desc ={SECBUFFER_VERSION,1,&wrecv_tok};
135 unsigned long flags=ISC_REQ_MUTUAL_AUTH|ISC_REQ_REPLAY_DETECT|
136 ISC_REQ_CONFIDENTIALITY|ISC_REQ_ALLOCATE_MEMORY;
137 unsigned long ret_flags=0;
138
139 /* check if we have to delegate ... */
140 if (to_deleg) flags |= ISC_REQ_DELEGATE;
141 winctx->maj_stat = p_InitializeSecurityContextA(&winctx->cred_handle,
142 winctx->context_handle,
143 (char*) srv_name,
144 flags,
145 0, /* reserved */
146 SECURITY_NATIVE_DREP,
147 &input_desc,
148 0, /* reserved */
149 &winctx->context,
150 &output_desc,
151 &ret_flags,
152 &winctx->expiry);
153
154 /* prepare for the next round */
155 winctx->context_handle = &winctx->context;
156 send_tok->data = (char*) wsend_tok.pvBuffer;
157 send_tok->len = wsend_tok.cbBuffer;
158
159 /* check & return our status */
160 if (winctx->maj_stat==SEC_E_OK) return SSH_GSS_S_COMPLETE;
161 if (winctx->maj_stat==SEC_I_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;
162
163 return SSH_GSS_FAILURE;
164 }
165
166 Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *send_tok)
167 {
168 /* check input */
169 if (send_tok == NULL) return SSH_GSS_FAILURE;
170
171 /* free Windows buffer */
172 p_FreeContextBuffer(send_tok->data);
173 send_tok->len = 0; send_tok->data = NULL;
174
175 return SSH_GSS_OK;
176 }
177
178 Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *ctx)
179 {
180 winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) *ctx;
181
182 /* check input */
183 if (winctx == NULL) return SSH_GSS_FAILURE;
184
185 /* free Windows data */
186 p_FreeCredentialsHandle(&winctx->cred_handle);
187 p_DeleteSecurityContext(&winctx->context);
188
189 /* delete our "wrapper" structure */
190 sfree(winctx);
191 *ctx = (Ssh_gss_ctx) NULL;
192
193 return SSH_GSS_OK;
194 }
195
196
197 Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *srv_name)
198 {
199 char *pStr= (char *) *srv_name;
200
201 if (pStr == NULL) return SSH_GSS_FAILURE;
202 sfree(pStr);
203 *srv_name = (Ssh_gss_name) NULL;
204
205 return SSH_GSS_OK;
206 }
207
208 Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
209 {
210 winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) ctx;
211 char *msg;
212
213 if (winctx == NULL) return SSH_GSS_FAILURE;
214
215 /* decode the error code */
216 switch (winctx->maj_stat) {
217 case SEC_E_OK: msg="SSPI status OK"; break;
218 case SEC_E_INVALID_HANDLE: msg="The handle passed to the function"
219 " is invalid.";
220 break;
221 case SEC_E_TARGET_UNKNOWN: msg="The target was not recognized."; break;
222 case SEC_E_LOGON_DENIED: msg="The logon failed."; break;
223 case SEC_E_INTERNAL_ERROR: msg="The Local Security Authority cannot"
224 " be contacted.";
225 break;
226 case SEC_E_NO_CREDENTIALS: msg="No credentials are available in the"
227 " security package.";
228 break;
229 case SEC_E_NO_AUTHENTICATING_AUTHORITY:
230 msg="No authority could be contacted for authentication."
231 "The domain name of the authenticating party could be wrong,"
232 " the domain could be unreachable, or there might have been"
233 " a trust relationship failure.";
234 break;
235 case SEC_E_INSUFFICIENT_MEMORY:
236 msg="One or more of the SecBufferDesc structures passed as"
237 " an OUT parameter has a buffer that is too small.";
238 break;
239 case SEC_E_INVALID_TOKEN:
240 msg="The error is due to a malformed input token, such as a"
241 " token corrupted in transit, a token"
242 " of incorrect size, or a token passed into the wrong"
243 " security package. Passing a token to"
244 " the wrong package can happen if client and server did not"
245 " negotiate the proper security package.";
246 break;
247 default:
248 msg = "Internal SSPI error";
249 break;
250 }
251
252 buf->data = dupstr(msg);
253 buf->len = strlen(buf->data);
254
255 return SSH_GSS_OK;
256 }
257
258 Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
259 Ssh_gss_buf *hash)
260 {
261 winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx;
262 SecPkgContext_Sizes ContextSizes;
263 SecBufferDesc InputBufferDescriptor;
264 SecBuffer InputSecurityToken[2];
265
266 if (winctx == NULL) return SSH_GSS_FAILURE;
267
268 winctx->maj_stat = 0;
269
270 memset(&ContextSizes, 0, sizeof(ContextSizes));
271
272 winctx->maj_stat = p_QueryContextAttributesA(&winctx->context,
273 SECPKG_ATTR_SIZES,
274 &ContextSizes);
275
276 if (winctx->maj_stat != SEC_E_OK ||
277 ContextSizes.cbMaxSignature == 0)
278 return winctx->maj_stat;
279
280 InputBufferDescriptor.cBuffers = 2;
281 InputBufferDescriptor.pBuffers = InputSecurityToken;
282 InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
283 InputSecurityToken[0].BufferType = SECBUFFER_DATA;
284 InputSecurityToken[0].cbBuffer = buf->len;
285 InputSecurityToken[0].pvBuffer = buf->data;
286 InputSecurityToken[1].BufferType = SECBUFFER_TOKEN;
287 InputSecurityToken[1].cbBuffer = ContextSizes.cbMaxSignature;
288 InputSecurityToken[1].pvBuffer = snewn(ContextSizes.cbMaxSignature, char);
289
290 winctx->maj_stat = p_MakeSignature(&winctx->context,
291 0,
292 &InputBufferDescriptor,
293 0);
294
295 if (winctx->maj_stat == SEC_E_OK) {
296 hash->len = InputSecurityToken[1].cbBuffer;
297 hash->data = InputSecurityToken[1].pvBuffer;
298 }
299
300 return winctx->maj_stat;
301 }
302
303 Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *hash)
304 {
305 sfree(hash->data);
306 return SSH_GSS_OK;
307 }
308
309 #else
310
311 /* Dummy function so this source file defines something if NO_GSSAPI
312 is defined. */
313
314 int ssh_gss_init(void)
315 {
316 return 0;
317 }
318
319 #endif