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