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