Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/ldap/libldap/sasl_w.c
4394 views
1
/*
2
* Copyright 2022 Hans Leidekker for CodeWeavers
3
* Copyright 2023 Dmitry Timoshkov
4
*
5
* SSPI based replacement for Cyrus SASL
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 <assert.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <windef.h>
26
#include <winerror.h>
27
#include <winsock.h>
28
#include <sspi.h>
29
#include <rpc.h>
30
#include <sasl.h>
31
32
struct connection
33
{
34
char *target;
35
CredHandle cred_handle;
36
CtxtHandle ctxt_handle;
37
sasl_interact_t prompts[4];
38
unsigned int max_token;
39
unsigned int trailer_size;
40
unsigned int flags;
41
unsigned int qop;
42
unsigned short package_id;
43
sasl_ssf_t ssf;
44
char *buf;
45
unsigned buf_size;
46
};
47
48
int sasl_client_init( const sasl_callback_t *callbacks )
49
{
50
return SASL_OK;
51
}
52
53
static int grow_buffer( struct connection *conn, unsigned int min_size )
54
{
55
char *tmp;
56
unsigned int new_size = max( conn->buf_size * 2, min_size );
57
if (conn->buf_size >= min_size) return SASL_OK;
58
if (!(tmp = realloc( conn->buf, new_size ))) return SASL_NOMEM;
59
conn->buf = tmp;
60
conn->buf_size = new_size;
61
return SASL_OK;
62
}
63
64
int sasl_decode( sasl_conn_t *handle, const char *input, unsigned int inputlen, const char **output,
65
unsigned int *outputlen )
66
{
67
struct connection *conn = (struct connection *)handle;
68
unsigned int len;
69
SecBuffer bufs[2] =
70
{
71
{ 0, SECBUFFER_STREAM, NULL },
72
{ 0, SECBUFFER_DATA, NULL }
73
};
74
SecBufferDesc buf_desc = { SECBUFFER_VERSION, ARRAYSIZE(bufs), bufs };
75
SECURITY_STATUS status;
76
int ret;
77
78
if (inputlen < sizeof(len)) return SASL_FAIL;
79
len = ntohl( *(unsigned int *)input );
80
if (inputlen < sizeof(len) + len) return SASL_FAIL;
81
82
if ((ret = grow_buffer( conn, len )) < 0) return ret;
83
memcpy( conn->buf, input + sizeof(len), len );
84
85
bufs[0].pvBuffer = conn->buf;
86
bufs[0].cbBuffer = len;
87
88
status = DecryptMessage( &conn->ctxt_handle, &buf_desc, 0, NULL );
89
if (status == SEC_E_OK)
90
{
91
*output = bufs[1].pvBuffer;
92
*outputlen = bufs[1].cbBuffer;
93
return SASL_OK;
94
}
95
96
return SASL_FAIL;
97
}
98
99
int sasl_encode( sasl_conn_t *handle, const char *input, unsigned int inputlen, const char **output,
100
unsigned int *outputlen )
101
{
102
struct connection *conn = (struct connection *)handle;
103
SecBuffer bufs[2] =
104
{
105
{ inputlen, SECBUFFER_DATA, NULL },
106
{ conn->trailer_size, SECBUFFER_TOKEN, NULL }
107
};
108
SecBufferDesc buf_desc = { SECBUFFER_VERSION, ARRAYSIZE(bufs), bufs };
109
SECURITY_STATUS status;
110
unsigned int len;
111
int ret;
112
113
if ((ret = grow_buffer( conn, sizeof(len) + inputlen + conn->trailer_size )) < 0) return ret;
114
if (conn->package_id == RPC_C_AUTHN_GSS_KERBEROS)
115
{
116
memcpy( conn->buf + sizeof(len), input, inputlen );
117
bufs[0].pvBuffer = conn->buf + sizeof(len);
118
bufs[1].pvBuffer = conn->buf + sizeof(len) + inputlen;
119
}
120
else
121
{
122
memcpy( conn->buf + sizeof(len) + conn->trailer_size, input, inputlen );
123
bufs[0].pvBuffer = conn->buf + sizeof(len) + conn->trailer_size;
124
bufs[1].pvBuffer = conn->buf + sizeof(len);
125
}
126
127
status = EncryptMessage( &conn->ctxt_handle, (conn->qop & ISC_RET_CONFIDENTIALITY) ? 0 : SECQOP_WRAP_NO_ENCRYPT, &buf_desc, 0 );
128
if (status == SEC_E_OK)
129
{
130
len = htonl( bufs[0].cbBuffer + bufs[1].cbBuffer );
131
memcpy( conn->buf, &len, sizeof(len) );
132
*output = conn->buf;
133
*outputlen = sizeof(len) + bufs[0].cbBuffer + bufs[1].cbBuffer;
134
return SASL_OK;
135
}
136
137
return SASL_FAIL;
138
}
139
140
const char *sasl_errstring( int saslerr, const char *langlist, const char **outlang )
141
{
142
return NULL;
143
}
144
145
void sasl_set_mutex( void *mutex_alloc, void *mutex_lock, void *mutex_unlock, void *mutex_free )
146
{
147
}
148
149
static int check_callback( const sasl_callback_t *callbacks, unsigned long id )
150
{
151
const sasl_callback_t *ptr = callbacks;
152
while (ptr->id)
153
{
154
if (ptr->id == id) return 1;
155
ptr++;
156
}
157
return 0;
158
}
159
160
int sasl_client_new( const char *service, const char *server, const char *localport, const char *remoteport,
161
const sasl_callback_t *prompt, unsigned int flags, sasl_conn_t **ret )
162
{
163
struct connection *conn;
164
SECURITY_STATUS status;
165
SecPkgInfoA *info;
166
int len;
167
168
if (!check_callback( prompt, SASL_CB_AUTHNAME ) || !check_callback( prompt, SASL_CB_GETREALM ) ||
169
!check_callback( prompt, SASL_CB_PASS )) return SASL_BADPARAM;
170
171
if (!(conn = calloc( 1, sizeof(*conn) ))) return SASL_NOMEM;
172
173
if (service && !*service) service = NULL;
174
175
len = strlen( server ) + 1; /* '\0' */;
176
if (service) len += strlen( service ) + 1; /* '/' */
177
if (!(conn->target = malloc( len )))
178
{
179
free( conn );
180
return SASL_NOMEM;
181
}
182
if (service)
183
{
184
strcpy( conn->target, service );
185
strcat( conn->target, "/" );
186
strcat( conn->target, server );
187
}
188
else
189
strcpy( conn->target, server );
190
191
status = QuerySecurityPackageInfoA( (SEC_CHAR *)"Negotiate", &info );
192
if (status != SEC_E_OK)
193
{
194
free( conn->target );
195
free( conn );
196
return SASL_FAIL;
197
}
198
conn->max_token = conn->buf_size = info->cbMaxToken;
199
FreeContextBuffer( info );
200
201
if (!(conn->buf = malloc( conn->buf_size )))
202
{
203
free( conn->target );
204
free( conn );
205
return SASL_NOMEM;
206
}
207
208
conn->prompts[0].id = SASL_CB_AUTHNAME;
209
conn->prompts[1].id = SASL_CB_GETREALM;
210
conn->prompts[2].id = SASL_CB_PASS;
211
conn->prompts[3].id = SASL_CB_LIST_END;
212
213
*ret = (sasl_conn_t *)conn;
214
return SASL_OK;
215
}
216
217
void sasl_dispose( sasl_conn_t **handle_ptr )
218
{
219
struct connection *conn = *(struct connection **)handle_ptr;
220
221
DeleteSecurityContext( &conn->ctxt_handle );
222
FreeCredentialsHandle( &conn->cred_handle );
223
free( conn->target );
224
free( conn->buf );
225
free( conn );
226
}
227
228
static unsigned short *get_prompt_result( const sasl_interact_t *prompts, unsigned long id, ULONG *len )
229
{
230
const sasl_interact_t *ptr = prompts;
231
while (ptr->id != SASL_CB_LIST_END)
232
{
233
if (ptr->id == id)
234
{
235
*len = ptr->len;
236
return (unsigned short *)ptr->result;
237
}
238
ptr++;
239
}
240
return NULL;
241
}
242
243
static int fill_auth_identity( const sasl_interact_t *prompts, SEC_WINNT_AUTH_IDENTITY_W *id )
244
{
245
if (!(id->User = get_prompt_result( prompts, SASL_CB_AUTHNAME, &id->UserLength )))
246
return SASL_BADPARAM;
247
if (!(id->Domain = get_prompt_result( prompts, SASL_CB_GETREALM, &id->DomainLength )))
248
return SASL_BADPARAM;
249
if (!(id->Password = get_prompt_result( prompts, SASL_CB_PASS, &id->PasswordLength )))
250
return SASL_BADPARAM;
251
252
id->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
253
return SASL_OK;
254
}
255
256
static ULONG get_key_size( CtxtHandle *ctx )
257
{
258
SecPkgContext_SessionKey key;
259
if (QueryContextAttributesA( ctx, SECPKG_ATTR_SESSION_KEY, &key )) return 0;
260
FreeContextBuffer( key.SessionKey );
261
return key.SessionKeyLength * 8;
262
}
263
264
static ULONG get_trailer_size( CtxtHandle *ctx )
265
{
266
SecPkgContext_Sizes sizes;
267
if (QueryContextAttributesA( ctx, SECPKG_ATTR_SIZES, &sizes )) return 0;
268
return sizes.cbSecurityTrailer;
269
}
270
271
static unsigned short get_package_id( CtxtHandle *ctx )
272
{
273
SecPkgContext_NegotiationInfoW info;
274
unsigned short id;
275
276
memset( &info, 0, sizeof(info) );
277
if (QueryContextAttributesW( ctx, SECPKG_ATTR_NEGOTIATION_INFO, &info )) return 0;
278
id = info.PackageInfo->wRPCID;
279
FreeContextBuffer( info.PackageInfo );
280
return id;
281
}
282
283
int sasl_client_step( sasl_conn_t *handle, const char *serverin, unsigned int serverinlen,
284
sasl_interact_t **prompts, const char **clientout, unsigned int *clientoutlen )
285
{
286
struct connection *conn = (struct connection *)handle;
287
SecBuffer in_bufs[] =
288
{
289
{ serverinlen, SECBUFFER_TOKEN, (void *)serverin },
290
{ 0, SECBUFFER_EMPTY, NULL }
291
};
292
SecBuffer out_bufs[] =
293
{
294
{ conn->buf_size, SECBUFFER_TOKEN, conn->buf },
295
{ 0, SECBUFFER_ALERT, NULL }
296
};
297
SecBufferDesc in_buf_desc = { SECBUFFER_VERSION, ARRAYSIZE(in_bufs), in_bufs };
298
SecBufferDesc out_buf_desc = { SECBUFFER_VERSION, ARRAYSIZE(out_bufs), out_bufs };
299
ULONG attrs;
300
SECURITY_STATUS status;
301
302
if (!serverin) /* initial step */
303
status = InitializeSecurityContextA( &conn->cred_handle, NULL, conn->target, conn->flags, 0, 0,
304
NULL, 0, &conn->ctxt_handle, &out_buf_desc, &attrs, NULL );
305
else
306
status = InitializeSecurityContextA( NULL, &conn->ctxt_handle, conn->target, conn->flags, 0, 0,
307
&in_buf_desc, 0, &conn->ctxt_handle, &out_buf_desc, &attrs, NULL );
308
if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED)
309
{
310
*clientout = out_bufs[0].pvBuffer;
311
*clientoutlen = out_bufs[0].cbBuffer;
312
if (status == SEC_I_CONTINUE_NEEDED) return SASL_CONTINUE;
313
314
conn->ssf = get_key_size( &conn->ctxt_handle );
315
conn->trailer_size = get_trailer_size( &conn->ctxt_handle );
316
conn->qop = attrs;
317
conn->package_id = get_package_id( &conn->ctxt_handle );
318
return SASL_OK;
319
}
320
321
return SASL_FAIL;
322
}
323
324
int sasl_client_start( sasl_conn_t *handle, const char *mechlist, sasl_interact_t **prompts,
325
const char **clientout, unsigned int *clientoutlen, const char **mech )
326
{
327
struct connection *conn = (struct connection *)handle;
328
SEC_WINNT_AUTH_IDENTITY_W id;
329
SECURITY_STATUS status;
330
int ret;
331
332
if (!*prompts)
333
{
334
*prompts = conn->prompts;
335
return SASL_INTERACT;
336
}
337
if ((ret = fill_auth_identity( conn->prompts, &id )) < 0) return ret;
338
339
status = AcquireCredentialsHandleA( NULL, (SEC_CHAR *)"Negotiate", SECPKG_CRED_OUTBOUND, NULL,
340
(SEC_WINNT_AUTH_IDENTITY_A *)&id, NULL, NULL, &conn->cred_handle, NULL );
341
if (status != SEC_E_OK) return SASL_FAIL;
342
343
/* FIXME: flags probably should depend on LDAP_OPT_SSPI_FLAGS */
344
conn->flags = ISC_REQ_INTEGRITY | ISC_REQ_CONFIDENTIALITY | ISC_REQ_MUTUAL_AUTH | ISC_REQ_EXTENDED_ERROR;
345
346
ret = sasl_client_step( handle, NULL, 0, prompts, clientout, clientoutlen );
347
if (ret == SASL_OK || ret == SASL_CONTINUE)
348
*mech = "GSS-SPNEGO";
349
350
return ret;
351
}
352
353
int sasl_getprop( sasl_conn_t *handle, int propnum, const void **pvalue )
354
{
355
struct connection *conn = (struct connection *)handle;
356
357
switch (propnum)
358
{
359
case SASL_SSF:
360
*(sasl_ssf_t **)pvalue = &conn->ssf;
361
return SASL_OK;
362
case SASL_MAXOUTBUF:
363
*(unsigned int *)pvalue = conn->max_token;
364
return SASL_OK;
365
default:
366
break;
367
}
368
369
return SASL_FAIL;
370
}
371
372
int sasl_setprop( sasl_conn_t *handle, int propnum, const void *value )
373
{
374
switch (propnum)
375
{
376
case SASL_SEC_PROPS:
377
case SASL_AUTH_EXTERNAL:
378
return SASL_OK;
379
default:
380
break;
381
}
382
return SASL_FAIL;
383
}
384
385
const char *sasl_errdetail( sasl_conn_t *handle )
386
{
387
return NULL;
388
}
389
390
const char **sasl_global_listmech( void )
391
{
392
static const char *mechs[] = { "GSS-SPNEGO", NULL };
393
return mechs;
394
}
395
396