1 /*
2  * Tests for the Negotiate security provider
3  *
4  * Copyright 2005, 2006 Kai Blin
5  * Copyright 2012 Hans Leidekker for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <windef.h>
25 #include <winbase.h>
26 #define SECURITY_WIN32
27 #include <sspi.h>
28 #include <rpc.h>
29 #include <rpcdce.h>
30 #include <secext.h>
31 
32 #include "wine/test.h"
33 
34 #define NEGOTIATE_BASE_CAPS ( \
35     SECPKG_FLAG_INTEGRITY  | \
36     SECPKG_FLAG_PRIVACY    | \
37     SECPKG_FLAG_CONNECTION | \
38     SECPKG_FLAG_MULTI_REQUIRED | \
39     SECPKG_FLAG_EXTENDED_ERROR | \
40     SECPKG_FLAG_IMPERSONATION  | \
41     SECPKG_FLAG_ACCEPT_WIN32_NAME | \
42     SECPKG_FLAG_NEGOTIABLE        | \
43     SECPKG_FLAG_GSS_COMPATIBLE    | \
44     SECPKG_FLAG_LOGON )
45 
46 #define NTLM_BASE_CAPS ( \
47     SECPKG_FLAG_INTEGRITY  | \
48     SECPKG_FLAG_PRIVACY    | \
49     SECPKG_FLAG_TOKEN_ONLY | \
50     SECPKG_FLAG_CONNECTION | \
51     SECPKG_FLAG_MULTI_REQUIRED    | \
52     SECPKG_FLAG_IMPERSONATION     | \
53     SECPKG_FLAG_ACCEPT_WIN32_NAME | \
54     SECPKG_FLAG_NEGOTIABLE        | \
55     SECPKG_FLAG_LOGON )
56 
57 struct sspi_data
58 {
59     CredHandle cred;
60     CtxtHandle ctxt;
61     PSecBufferDesc in_buf;
62     PSecBufferDesc out_buf;
63     PSEC_WINNT_AUTH_IDENTITY_A id;
64     ULONG max_token;
65 };
66 
67 static void cleanup_buffers( struct sspi_data *data )
68 {
69     unsigned int i;
70 
71     if (data->in_buf)
72     {
73         for (i = 0; i < data->in_buf->cBuffers; ++i)
74             HeapFree( GetProcessHeap(), 0, data->in_buf->pBuffers[i].pvBuffer );
75         HeapFree( GetProcessHeap(), 0, data->in_buf->pBuffers );
76         HeapFree( GetProcessHeap(), 0, data->in_buf );
77     }
78     if (data->out_buf)
79     {
80         for (i = 0; i < data->out_buf->cBuffers; ++i)
81             HeapFree( GetProcessHeap(), 0, data->out_buf->pBuffers[i].pvBuffer );
82         HeapFree( GetProcessHeap(), 0, data->out_buf->pBuffers );
83         HeapFree( GetProcessHeap(), 0, data->out_buf );
84     }
85 }
86 
87 static void setup_buffers( struct sspi_data *data, SecPkgInfoA *info )
88 {
89     SecBuffer *buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(SecBuffer) );
90 
91     data->in_buf  = HeapAlloc( GetProcessHeap(), 0, sizeof(SecBufferDesc) );
92     data->out_buf = HeapAlloc( GetProcessHeap(), 0, sizeof(SecBufferDesc) );
93     data->max_token = info->cbMaxToken;
94 
95     data->in_buf->ulVersion = SECBUFFER_VERSION;
96     data->in_buf->cBuffers  = 1;
97     data->in_buf->pBuffers  = buffer;
98 
99     buffer->cbBuffer   = info->cbMaxToken;
100     buffer->BufferType = SECBUFFER_TOKEN;
101     buffer->pvBuffer   = HeapAlloc( GetProcessHeap(), 0, info->cbMaxToken );
102 
103     buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(SecBuffer) );
104 
105     data->out_buf->ulVersion = SECBUFFER_VERSION;
106     data->out_buf->cBuffers  = 1;
107     data->out_buf->pBuffers  = buffer;
108 
109     buffer->cbBuffer   = info->cbMaxToken;
110     buffer->BufferType = SECBUFFER_TOKEN;
111     buffer->pvBuffer   = HeapAlloc( GetProcessHeap(), 0, info->cbMaxToken );
112 }
113 
114 static SECURITY_STATUS setup_client( struct sspi_data *data, SEC_CHAR *provider )
115 {
116     SECURITY_STATUS ret;
117     SecPkgInfoA *info;
118     TimeStamp ttl;
119 
120     trace( "setting up client\n" );
121 
122     ret = QuerySecurityPackageInfoA( provider, &info );
123     ok( ret == SEC_E_OK, "QuerySecurityPackageInfo returned %08x\n", ret );
124 
125     setup_buffers( data, info );
126     FreeContextBuffer( info );
127 
128     ret = AcquireCredentialsHandleA( NULL, provider, SECPKG_CRED_OUTBOUND, NULL,
129                                      data->id, NULL, NULL, &data->cred, &ttl );
130     ok( ret == SEC_E_OK, "AcquireCredentialsHandleA returned %08x\n", ret );
131     return ret;
132 }
133 
134 static SECURITY_STATUS setup_server( struct sspi_data *data, SEC_CHAR *provider )
135 {
136     SECURITY_STATUS ret;
137     SecPkgInfoA *info;
138     TimeStamp ttl;
139 
140     trace( "setting up server\n" );
141 
142     ret = QuerySecurityPackageInfoA( provider, &info );
143     ok( ret == SEC_E_OK, "QuerySecurityPackageInfo returned %08x\n", ret );
144 
145     setup_buffers( data, info );
146     FreeContextBuffer( info );
147 
148     ret = AcquireCredentialsHandleA( NULL, provider, SECPKG_CRED_INBOUND, NULL,
149                                      NULL, NULL, NULL, &data->cred, &ttl );
150     ok( ret == SEC_E_OK, "AcquireCredentialsHandleA returned %08x\n", ret );
151     return ret;
152 }
153 
154 static SECURITY_STATUS run_client( struct sspi_data *data, BOOL first )
155 {
156     SECURITY_STATUS ret;
157     TimeStamp ttl;
158     ULONG attr;
159 
160     trace( "running client for the %s time\n", first ? "first" : "second" );
161 
162     data->out_buf->pBuffers[0].cbBuffer = data->max_token;
163     data->out_buf->pBuffers[0].BufferType = SECBUFFER_TOKEN;
164 
165     ret = InitializeSecurityContextA( first ? &data->cred : NULL, first ? NULL : &data->ctxt,
166                                       NULL, 0, 0, SECURITY_NETWORK_DREP, first ? NULL : data->in_buf,
167                                       0, &data->ctxt, data->out_buf, &attr, &ttl );
168     if (ret == SEC_I_COMPLETE_AND_CONTINUE || ret == SEC_I_COMPLETE_NEEDED)
169     {
170         CompleteAuthToken( &data->ctxt, data->out_buf );
171         if (ret == SEC_I_COMPLETE_AND_CONTINUE)
172             ret = SEC_I_CONTINUE_NEEDED;
173         else if (ret == SEC_I_COMPLETE_NEEDED)
174             ret = SEC_E_OK;
175     }
176     ok( data->out_buf->pBuffers[0].BufferType == SECBUFFER_TOKEN,
177         "buffer type changed from SECBUFFER_TOKEN to %u\n", data->out_buf->pBuffers[0].BufferType );
178     ok( data->out_buf->pBuffers[0].cbBuffer < data->max_token,
179         "InitializeSecurityContext didn't change buffer size\n" );
180     return ret;
181 }
182 
183 static SECURITY_STATUS run_server( struct sspi_data *data, BOOL first )
184 {
185     SECURITY_STATUS ret;
186     TimeStamp ttl;
187     ULONG attr;
188 
189     trace( "running server for the %s time\n", first ? "first" : "second" );
190 
191     ret = AcceptSecurityContext( &data->cred, first ? NULL : &data->ctxt,
192                                  data->in_buf, 0, SECURITY_NETWORK_DREP,
193                                  &data->ctxt, data->out_buf, &attr, &ttl );
194     if (ret == SEC_I_COMPLETE_AND_CONTINUE || ret == SEC_I_COMPLETE_NEEDED)
195     {
196         CompleteAuthToken( &data->ctxt, data->out_buf );
197         if (ret == SEC_I_COMPLETE_AND_CONTINUE)
198             ret = SEC_I_CONTINUE_NEEDED;
199         else if (ret == SEC_I_COMPLETE_NEEDED)
200             ret = SEC_E_OK;
201     }
202     return ret;
203 }
204 
205 static void communicate( struct sspi_data *from, struct sspi_data *to )
206 {
207     trace( "running communicate\n" );
208     memset( to->in_buf->pBuffers[0].pvBuffer, 0, to->max_token );
209     memcpy( to->in_buf->pBuffers[0].pvBuffer, from->out_buf->pBuffers[0].pvBuffer,
210             from->out_buf->pBuffers[0].cbBuffer );
211     to->in_buf->pBuffers[0].cbBuffer = from->out_buf->pBuffers[0].cbBuffer;
212     memset( from->out_buf->pBuffers[0].pvBuffer, 0, from->max_token );
213 }
214 
215 static void test_authentication(void)
216 {
217     SECURITY_STATUS status_c = SEC_I_CONTINUE_NEEDED,
218                     status_s = SEC_I_CONTINUE_NEEDED, status;
219     struct sspi_data client, server;
220     SEC_WINNT_AUTH_IDENTITY_A id;
221     SecPkgContext_NegotiationInfoA info;
222     SecPkgContext_Sizes sizes;
223     SecPkgInfoA *pi;
224     BOOL first = TRUE;
225 
226     memset(&client, 0, sizeof(client));
227     memset(&server, 0, sizeof(server));
228 
229     id.User = (unsigned char *)"user";
230     id.UserLength = strlen( "user" );
231     id.Domain = (unsigned char *)"domain";
232     id.DomainLength = strlen( "domain" );
233     id.Password = (unsigned char *)"password";
234     id.PasswordLength = strlen( "password" );
235     id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
236 
237     client.id = &id;
238     if ((status = setup_client( &client, (SEC_CHAR *)"Negotiate" )))
239     {
240         skip( "setup_client returned %08x, skipping test\n", status );
241         return;
242     }
243     if ((status = setup_server( &server, (SEC_CHAR *)"Negotiate" )))
244     {
245         skip( "setup_server returned %08x, skipping test\n", status );
246         FreeCredentialsHandle( &client.cred );
247         return;
248     }
249 
250     while (status_c == SEC_I_CONTINUE_NEEDED && status_s == SEC_I_CONTINUE_NEEDED)
251     {
252         status_c = run_client( &client, first );
253         ok( status_c == SEC_E_OK || status_c == SEC_I_CONTINUE_NEEDED,
254             "client returned %08x, more tests will fail\n", status_c );
255 
256         communicate( &client, &server );
257 
258         status_s = run_server( &server, first );
259         ok( status_s == SEC_E_OK || status_s == SEC_I_CONTINUE_NEEDED ||
260             status_s == SEC_E_LOGON_DENIED,
261             "server returned %08x, more tests will fail\n", status_s );
262 
263         communicate( &server, &client );
264         trace( "looping\n");
265         first = FALSE;
266     }
267     if (status_c != SEC_E_OK)
268     {
269         skip( "authentication failed, skipping remaining tests\n" );
270         goto done;
271     }
272 
273     sizes.cbMaxToken        = 0xdeadbeef;
274     sizes.cbMaxSignature    = 0xdeadbeef;
275     sizes.cbSecurityTrailer = 0xdeadbeef;
276     sizes.cbBlockSize       = 0xdeadbeef;
277     status_c = QueryContextAttributesA( &client.ctxt, SECPKG_ATTR_SIZES, &sizes );
278     ok( status_c == SEC_E_OK, "pQueryContextAttributesA returned %08x\n", status_c );
279     ok( sizes.cbMaxToken == 2888 || sizes.cbMaxToken == 1904,
280         "expected 2888 or 1904, got %u\n", sizes.cbMaxToken );
281     ok( sizes.cbMaxSignature == 16, "expected 16, got %u\n", sizes.cbMaxSignature );
282     ok( sizes.cbSecurityTrailer == 16, "expected 16, got %u\n", sizes.cbSecurityTrailer );
283     ok( !sizes.cbBlockSize, "expected 0, got %u\n", sizes.cbBlockSize );
284 
285     memset( &info, 0, sizeof(info) );
286     status_c = QueryContextAttributesA( &client.ctxt, SECPKG_ATTR_NEGOTIATION_INFO, &info );
287     ok( status_c == SEC_E_OK, "QueryContextAttributesA returned %08x\n", status_c );
288 
289     pi = info.PackageInfo;
290     ok( info.NegotiationState == SECPKG_NEGOTIATION_COMPLETE, "got %u\n", info.NegotiationState );
291     ok( pi != NULL, "expected non-NULL PackageInfo\n" );
292     if (pi)
293     {
294         UINT expected, got;
295         char *eob;
296 
297         ok( pi->fCapabilities == NTLM_BASE_CAPS ||
298             pi->fCapabilities == (NTLM_BASE_CAPS|SECPKG_FLAG_READONLY_WITH_CHECKSUM) ||
299             pi->fCapabilities == (NTLM_BASE_CAPS|SECPKG_FLAG_RESTRICTED_TOKENS) ||
300             pi->fCapabilities == (NTLM_BASE_CAPS|SECPKG_FLAG_RESTRICTED_TOKENS|
301                                   SECPKG_FLAG_APPCONTAINER_CHECKS),
302             "got %08x\n", pi->fCapabilities );
303         ok( pi->wVersion == 1, "got %u\n", pi->wVersion );
304         ok( pi->wRPCID == RPC_C_AUTHN_WINNT, "got %u\n", pi->wRPCID );
305         ok( !lstrcmpA( pi->Name, "NTLM" ), "got %s\n", pi->Name );
306 
307         expected = sizeof(*pi) + lstrlenA(pi->Name) + 1 + lstrlenA(pi->Comment) + 1;
308         got = HeapSize(GetProcessHeap(), 0, pi);
309         ok( got == expected, "got %u, expected %u\n", got, expected );
310         eob = (char *)pi + expected;
311         ok( pi->Name + lstrlenA(pi->Name) < eob, "Name doesn't fit into allocated block\n" );
312         ok( pi->Comment + lstrlenA(pi->Comment) < eob, "Comment doesn't fit into allocated block\n" );
313 
314         status = FreeContextBuffer( pi );
315         ok( status == SEC_E_OK, "FreeContextBuffer error %#x\n", status );
316     }
317 
318 done:
319     cleanup_buffers( &client );
320     cleanup_buffers( &server );
321 
322     if (client.ctxt.dwLower || client.ctxt.dwUpper)
323     {
324         status_c = DeleteSecurityContext( &client.ctxt );
325         ok( status_c == SEC_E_OK, "DeleteSecurityContext returned %08x\n", status_c );
326     }
327 
328     if (server.ctxt.dwLower || server.ctxt.dwUpper)
329     {
330         status_s = DeleteSecurityContext( &server.ctxt );
331         ok( status_s == SEC_E_OK, "DeleteSecurityContext returned %08x\n", status_s );
332     }
333 
334     if (client.cred.dwLower || client.cred.dwUpper)
335     {
336         status_c = FreeCredentialsHandle( &client.cred );
337         ok( status_c == SEC_E_OK, "FreeCredentialsHandle returned %08x\n", status_c );
338     }
339 
340     if (server.cred.dwLower || server.cred.dwUpper)
341     {
342         status_s = FreeCredentialsHandle(&server.cred);
343         ok( status_s == SEC_E_OK, "FreeCredentialsHandle returned %08x\n", status_s );
344     }
345 }
346 
347 START_TEST(negotiate)
348 {
349     SecPkgInfoA *info;
350 
351     if (QuerySecurityPackageInfoA( (SEC_CHAR *)"Negotiate", &info ))
352     {
353         ok( 0, "Negotiate package not installed, skipping test\n" );
354         return;
355     }
356     ok( info->fCapabilities == NEGOTIATE_BASE_CAPS ||
357         info->fCapabilities == (NEGOTIATE_BASE_CAPS|SECPKG_FLAG_READONLY_WITH_CHECKSUM) ||
358         info->fCapabilities == (NEGOTIATE_BASE_CAPS|SECPKG_FLAG_RESTRICTED_TOKENS) ||
359         info->fCapabilities == (NEGOTIATE_BASE_CAPS|SECPKG_FLAG_RESTRICTED_TOKENS|
360                                 SECPKG_FLAG_APPCONTAINER_CHECKS),
361         "got %08x\n", info->fCapabilities );
362     ok( info->wVersion == 1, "got %u\n", info->wVersion );
363     ok( info->wRPCID == RPC_C_AUTHN_GSS_NEGOTIATE, "got %u\n", info->wRPCID );
364     ok( !lstrcmpA( info->Name, "Negotiate" ), "got %s\n", info->Name );
365     FreeContextBuffer( info );
366 
367     test_authentication();
368 }
369