[WINTRUST_WINETEST] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / dll / win32 / wldap32 / init.c
1 /*
2 * WLDAP32 - LDAP support for Wine
3 *
4 * Copyright 2005 Hans Leidekker
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdio.h>
25 #include <stdarg.h>
26 #ifdef HAVE_LDAP_H
27 #include <ldap.h>
28 #endif
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winnls.h"
33
34 #include "winldap_private.h"
35 #include "wldap32.h"
36 #include "wine/debug.h"
37
38 #ifdef HAVE_LDAP
39 /* Should eventually be determined by the algorithm documented on MSDN. */
40 static const WCHAR defaulthost[] = { 'l','o','c','a','l','h','o','s','t',0 };
41
42 /* Split a space separated string of hostnames into a string array */
43 static char **split_hostnames( const char *hostnames )
44 {
45 char **res, *str, *p, *q;
46 unsigned int i = 0;
47
48 str = strdupU( hostnames );
49 if (!str) return NULL;
50
51 p = str;
52 while (isspace( *p )) p++;
53 if (*p) i++;
54
55 while (*p)
56 {
57 if (isspace( *p ))
58 {
59 while (isspace( *p )) p++;
60 if (*p) i++;
61 }
62 p++;
63 }
64
65 if (!(res = heap_alloc( (i + 1) * sizeof(char *) )))
66 {
67 heap_free( str );
68 return NULL;
69 }
70
71 p = str;
72 while (isspace( *p )) p++;
73
74 q = p;
75 i = 0;
76
77 while (*p)
78 {
79 if (p[1] != '\0')
80 {
81 if (isspace( *p ))
82 {
83 *p = '\0'; p++;
84 res[i] = strdupU( q );
85 if (!res[i]) goto oom;
86 i++;
87
88 while (isspace( *p )) p++;
89 q = p;
90 }
91 }
92 else
93 {
94 res[i] = strdupU( q );
95 if (!res[i]) goto oom;
96 i++;
97 }
98 p++;
99 }
100 res[i] = NULL;
101
102 heap_free( str );
103 return res;
104
105 oom:
106 while (i > 0) strfreeU( res[--i] );
107
108 heap_free( res );
109 heap_free( str );
110
111 return NULL;
112 }
113
114 /* Determine if a URL starts with a known LDAP scheme */
115 static BOOL has_ldap_scheme( char *url )
116 {
117 return !strncasecmp( url, "ldap://", 7 ) ||
118 !strncasecmp( url, "ldaps://", 8 ) ||
119 !strncasecmp( url, "ldapi://", 8 ) ||
120 !strncasecmp( url, "cldap://", 8 );
121 }
122
123 /* Flatten an array of hostnames into a space separated string of URLs.
124 * Prepend a given scheme and append a given port number to each hostname
125 * if necessary.
126 */
127 static char *join_hostnames( const char *scheme, char **hostnames, ULONG portnumber )
128 {
129 char *res, *p, *q, **v;
130 unsigned int i = 0, size = 0;
131 static const char sep[] = " ", fmt[] = ":%d";
132 char port[7];
133
134 sprintf( port, fmt, portnumber );
135
136 for (v = hostnames; *v; v++)
137 {
138 if (!has_ldap_scheme( *v ))
139 {
140 size += strlen( scheme );
141 q = *v;
142 }
143 else
144 /* skip past colon in scheme prefix */
145 q = strchr( *v, '/' );
146
147 size += strlen( *v );
148
149 if (!strchr( q, ':' ))
150 size += strlen( port );
151
152 i++;
153 }
154
155 size += (i - 1) * strlen( sep );
156 if (!(res = heap_alloc( size + 1 ))) return NULL;
157
158 p = res;
159 for (v = hostnames; *v; v++)
160 {
161 if (v != hostnames)
162 {
163 strcpy( p, sep );
164 p += strlen( sep );
165 }
166
167 if (!has_ldap_scheme( *v ))
168 {
169 strcpy( p, scheme );
170 p += strlen( scheme );
171 q = *v;
172 }
173 else
174 /* skip past colon in scheme prefix */
175 q = strchr( *v, '/' );
176
177 strcpy( p, *v );
178 p += strlen( *v );
179
180 if (!strchr( q, ':' ))
181 {
182 strcpy( p, port );
183 p += strlen( port );
184 }
185 }
186 return res;
187 }
188
189 static char *urlify_hostnames( const char *scheme, char *hostnames, ULONG port )
190 {
191 char *url = NULL, **strarray;
192
193 strarray = split_hostnames( hostnames );
194 if (strarray)
195 url = join_hostnames( scheme, strarray, port );
196 else
197 return NULL;
198
199 strarrayfreeU( strarray );
200 return url;
201 }
202 #endif
203
204 WINE_DEFAULT_DEBUG_CHANNEL(wldap32);
205
206 #ifdef HAVE_LDAP
207 static LDAP *create_context( const char *url )
208 {
209 LDAP *ld;
210 int version = LDAP_VERSION3;
211 if (ldap_initialize( &ld, url ) != LDAP_SUCCESS) return NULL;
212 ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
213 return ld;
214 }
215 #endif
216
217 /***********************************************************************
218 * cldap_openA (WLDAP32.@)
219 *
220 * See cldap_openW.
221 */
222 WLDAP32_LDAP * CDECL cldap_openA( PCHAR hostname, ULONG portnumber )
223 {
224 #ifdef HAVE_LDAP
225 WLDAP32_LDAP *ld = NULL;
226 WCHAR *hostnameW = NULL;
227
228 TRACE( "(%s, %d)\n", debugstr_a(hostname), portnumber );
229
230 if (hostname) {
231 hostnameW = strAtoW( hostname );
232 if (!hostnameW) goto exit;
233 }
234
235 ld = cldap_openW( hostnameW, portnumber );
236
237 exit:
238 strfreeW( hostnameW );
239 return ld;
240
241 #else
242 return NULL;
243 #endif
244 }
245
246 /***********************************************************************
247 * cldap_openW (WLDAP32.@)
248 *
249 * Initialize an LDAP context and create a UDP connection.
250 *
251 * PARAMS
252 * hostname [I] Name of the host to connect to.
253 * portnumber [I] Port number to use.
254 *
255 * RETURNS
256 * Success: Pointer to an LDAP context.
257 * Failure: NULL
258 *
259 * NOTES
260 * The hostname string can be a space separated string of hostnames,
261 * in which case the LDAP runtime will try to connect to the hosts
262 * in order, until a connection can be made. A hostname may have a
263 * trailing port number (separated from the hostname by a ':'), which
264 * will take precedence over the port number supplied as a parameter
265 * to this function.
266 */
267 WLDAP32_LDAP * CDECL cldap_openW( PWCHAR hostname, ULONG portnumber )
268 {
269 #ifdef HAVE_LDAP
270 LDAP *ld = NULL;
271 char *hostnameU = NULL, *url = NULL;
272
273 TRACE( "(%s, %d)\n", debugstr_w(hostname), portnumber );
274
275 if (hostname) {
276 hostnameU = strWtoU( hostname );
277 if (!hostnameU) goto exit;
278 }
279 else {
280 hostnameU = strWtoU( defaulthost );
281 if (!hostnameU) goto exit;
282 }
283
284 url = urlify_hostnames( "cldap://", hostnameU, portnumber );
285 if (!url) goto exit;
286
287 ld = create_context( url );
288
289 exit:
290 strfreeU( hostnameU );
291 strfreeU( url );
292 return ld;
293
294 #else
295 return NULL;
296 #endif
297 }
298
299 /***********************************************************************
300 * ldap_connect (WLDAP32.@)
301 *
302 * Connect to an LDAP server.
303 *
304 * PARAMS
305 * ld [I] Pointer to an LDAP context.
306 * timeout [I] Pointer to an l_timeval structure specifying the
307 * timeout in seconds.
308 *
309 * RETURNS
310 * Success: LDAP_SUCCESS
311 * Failure: An LDAP error code.
312 *
313 * NOTES
314 * The timeout parameter may be NULL in which case a default timeout
315 * value will be used.
316 */
317 ULONG CDECL ldap_connect( WLDAP32_LDAP *ld, struct l_timeval *timeout )
318 {
319 TRACE( "(%p, %p)\n", ld, timeout );
320
321 if (!ld) return WLDAP32_LDAP_PARAM_ERROR;
322 return WLDAP32_LDAP_SUCCESS; /* FIXME: do something, e.g. ping the host */
323 }
324
325 /***********************************************************************
326 * ldap_initA (WLDAP32.@)
327 *
328 * See ldap_initW.
329 */
330 WLDAP32_LDAP * CDECL ldap_initA( const PCHAR hostname, ULONG portnumber )
331 {
332 #ifdef HAVE_LDAP
333 WLDAP32_LDAP *ld = NULL;
334 WCHAR *hostnameW = NULL;
335
336 TRACE( "(%s, %d)\n", debugstr_a(hostname), portnumber );
337
338 if (hostname) {
339 hostnameW = strAtoW( hostname );
340 if (!hostnameW) goto exit;
341 }
342
343 ld = ldap_initW( hostnameW, portnumber );
344
345 exit:
346 strfreeW( hostnameW );
347 return ld;
348
349 #else
350 return NULL;
351 #endif
352 }
353
354 /***********************************************************************
355 * ldap_initW (WLDAP32.@)
356 *
357 * Initialize an LDAP context and create a TCP connection.
358 *
359 * PARAMS
360 * hostname [I] Name of the host to connect to.
361 * portnumber [I] Port number to use.
362 *
363 * RETURNS
364 * Success: Pointer to an LDAP context.
365 * Failure: NULL
366 *
367 * NOTES
368 * The hostname string can be a space separated string of hostnames,
369 * in which case the LDAP runtime will try to connect to the hosts
370 * in order, until a connection can be made. A hostname may have a
371 * trailing port number (separated from the hostname by a ':'), which
372 * will take precedence over the port number supplied as a parameter
373 * to this function. The connection will not be made until the first
374 * LDAP function that needs it is called.
375 */
376 WLDAP32_LDAP * CDECL ldap_initW( const PWCHAR hostname, ULONG portnumber )
377 {
378 #ifdef HAVE_LDAP
379 LDAP *ld = NULL;
380 char *hostnameU = NULL, *url = NULL;
381
382 TRACE( "(%s, %d)\n", debugstr_w(hostname), portnumber );
383
384 if (hostname) {
385 hostnameU = strWtoU( hostname );
386 if (!hostnameU) goto exit;
387 }
388 else {
389 hostnameU = strWtoU( defaulthost );
390 if (!hostnameU) goto exit;
391 }
392
393 url = urlify_hostnames( "ldap://", hostnameU, portnumber );
394 if (!url) goto exit;
395
396 ld = create_context( url );
397
398 exit:
399 strfreeU( hostnameU );
400 strfreeU( url );
401 return ld;
402
403 #else
404 return NULL;
405 #endif
406 }
407
408 /***********************************************************************
409 * ldap_openA (WLDAP32.@)
410 *
411 * See ldap_openW.
412 */
413 WLDAP32_LDAP * CDECL ldap_openA( PCHAR hostname, ULONG portnumber )
414 {
415 #ifdef HAVE_LDAP
416 WLDAP32_LDAP *ld = NULL;
417 WCHAR *hostnameW = NULL;
418
419 TRACE( "(%s, %d)\n", debugstr_a(hostname), portnumber );
420
421 if (hostname) {
422 hostnameW = strAtoW( hostname );
423 if (!hostnameW) goto exit;
424 }
425
426 ld = ldap_openW( hostnameW, portnumber );
427
428 exit:
429 strfreeW( hostnameW );
430 return ld;
431
432 #else
433 return NULL;
434 #endif
435 }
436
437 /***********************************************************************
438 * ldap_openW (WLDAP32.@)
439 *
440 * Initialize an LDAP context and create a TCP connection.
441 *
442 * PARAMS
443 * hostname [I] Name of the host to connect to.
444 * portnumber [I] Port number to use.
445 *
446 * RETURNS
447 * Success: Pointer to an LDAP context.
448 * Failure: NULL
449 *
450 * NOTES
451 * The hostname string can be a space separated string of hostnames,
452 * in which case the LDAP runtime will try to connect to the hosts
453 * in order, until a connection can be made. A hostname may have a
454 * trailing port number (separated from the hostname by a ':'), which
455 * will take precedence over the port number supplied as a parameter
456 * to this function.
457 */
458 WLDAP32_LDAP * CDECL ldap_openW( PWCHAR hostname, ULONG portnumber )
459 {
460 #ifdef HAVE_LDAP
461 LDAP *ld = NULL;
462 char *hostnameU = NULL, *url = NULL;
463
464 TRACE( "(%s, %d)\n", debugstr_w(hostname), portnumber );
465
466 if (hostname) {
467 hostnameU = strWtoU( hostname );
468 if (!hostnameU) goto exit;
469 }
470 else {
471 hostnameU = strWtoU( defaulthost );
472 if (!hostnameU) goto exit;
473 }
474
475 url = urlify_hostnames( "ldap://", hostnameU, portnumber );
476 if (!url) goto exit;
477
478 ld = create_context( url );
479
480 exit:
481 strfreeU( hostnameU );
482 strfreeU( url );
483 return ld;
484
485 #else
486 return NULL;
487 #endif
488 }
489
490 /***********************************************************************
491 * ldap_sslinitA (WLDAP32.@)
492 *
493 * See ldap_sslinitW.
494 */
495 WLDAP32_LDAP * CDECL ldap_sslinitA( PCHAR hostname, ULONG portnumber, int secure )
496 {
497 #ifdef HAVE_LDAP
498 WLDAP32_LDAP *ld;
499 WCHAR *hostnameW = NULL;
500
501 TRACE( "(%s, %d, 0x%08x)\n", debugstr_a(hostname), portnumber, secure );
502
503 if (hostname) {
504 hostnameW = strAtoW( hostname );
505 if (!hostnameW) return NULL;
506 }
507
508 ld = ldap_sslinitW( hostnameW, portnumber, secure );
509
510 strfreeW( hostnameW );
511 return ld;
512
513 #else
514 return NULL;
515 #endif
516 }
517
518 /***********************************************************************
519 * ldap_sslinitW (WLDAP32.@)
520 *
521 * Initialize an LDAP context and create a secure TCP connection.
522 *
523 * PARAMS
524 * hostname [I] Name of the host to connect to.
525 * portnumber [I] Port number to use.
526 * secure [I] Ask the server to create an SSL connection.
527 *
528 * RETURNS
529 * Success: Pointer to an LDAP context.
530 * Failure: NULL
531 *
532 * NOTES
533 * The hostname string can be a space separated string of hostnames,
534 * in which case the LDAP runtime will try to connect to the hosts
535 * in order, until a connection can be made. A hostname may have a
536 * trailing port number (separated from the hostname by a ':'), which
537 * will take precedence over the port number supplied as a parameter
538 * to this function. The connection will not be made until the first
539 * LDAP function that needs it is called.
540 */
541 WLDAP32_LDAP * CDECL ldap_sslinitW( PWCHAR hostname, ULONG portnumber, int secure )
542 {
543 #ifdef HAVE_LDAP
544 WLDAP32_LDAP *ld = NULL;
545 char *hostnameU = NULL, *url = NULL;
546
547 TRACE( "(%s, %d, 0x%08x)\n", debugstr_w(hostname), portnumber, secure );
548
549 if (hostname) {
550 hostnameU = strWtoU( hostname );
551 if (!hostnameU) goto exit;
552 }
553 else {
554 hostnameU = strWtoU( defaulthost );
555 if (!hostnameU) goto exit;
556 }
557
558 if (secure)
559 url = urlify_hostnames( "ldaps://", hostnameU, portnumber );
560 else
561 url = urlify_hostnames( "ldap://", hostnameU, portnumber );
562
563 if (!url) goto exit;
564 ldap_initialize( &ld, url );
565
566 exit:
567 strfreeU( hostnameU );
568 strfreeU( url );
569 return ld;
570
571 #else
572 return NULL;
573 #endif
574 }
575
576 /***********************************************************************
577 * ldap_start_tls_sA (WLDAP32.@)
578 *
579 * See ldap_start_tls_sW.
580 */
581 ULONG CDECL ldap_start_tls_sA( WLDAP32_LDAP *ld, PULONG retval, WLDAP32_LDAPMessage **result,
582 PLDAPControlA *serverctrls, PLDAPControlA *clientctrls )
583 {
584 ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
585 #ifdef HAVE_LDAP
586 LDAPControlW **serverctrlsW = NULL, **clientctrlsW = NULL;
587
588 ret = WLDAP32_LDAP_NO_MEMORY;
589
590 TRACE( "(%p, %p, %p, %p, %p)\n", ld, retval, result, serverctrls, clientctrls );
591
592 if (!ld) return ~0u;
593
594 if (serverctrls) {
595 serverctrlsW = controlarrayAtoW( serverctrls );
596 if (!serverctrlsW) goto exit;
597 }
598 if (clientctrls) {
599 clientctrlsW = controlarrayAtoW( clientctrls );
600 if (!clientctrlsW) goto exit;
601 }
602
603 ret = ldap_start_tls_sW( ld, retval, result, serverctrlsW, clientctrlsW );
604
605 exit:
606 controlarrayfreeW( serverctrlsW );
607 controlarrayfreeW( clientctrlsW );
608
609 #endif
610 return ret;
611 }
612
613 /***********************************************************************
614 * ldap_start_tls_s (WLDAP32.@)
615 *
616 * Start TLS encryption on an LDAP connection.
617 *
618 * PARAMS
619 * ld [I] Pointer to an LDAP context.
620 * retval [I] Return value from the server.
621 * result [I] Response message from the server.
622 * serverctrls [I] Array of LDAP server controls.
623 * clientctrls [I] Array of LDAP client controls.
624 *
625 * RETURNS
626 * Success: LDAP_SUCCESS
627 * Failure: An LDAP error code.
628 *
629 * NOTES
630 * LDAP function that needs it is called.
631 */
632 ULONG CDECL ldap_start_tls_sW( WLDAP32_LDAP *ld, PULONG retval, WLDAP32_LDAPMessage **result,
633 PLDAPControlW *serverctrls, PLDAPControlW *clientctrls )
634 {
635 ULONG ret = WLDAP32_LDAP_NOT_SUPPORTED;
636 #ifdef HAVE_LDAP
637 LDAPControl **serverctrlsU = NULL, **clientctrlsU = NULL;
638
639 ret = WLDAP32_LDAP_NO_MEMORY;
640
641 TRACE( "(%p, %p, %p, %p, %p)\n", ld, retval, result, serverctrls, clientctrls );
642
643 if (!ld) return ~0u;
644
645 if (serverctrls) {
646 serverctrlsU = controlarrayWtoU( serverctrls );
647 if (!serverctrlsU) goto exit;
648 }
649 if (clientctrls) {
650 clientctrlsU = controlarrayWtoU( clientctrls );
651 if (!clientctrlsU) goto exit;
652 }
653
654 ret = map_error( ldap_start_tls_s( ld, serverctrlsU, clientctrlsU ));
655
656 exit:
657 controlarrayfreeU( serverctrlsU );
658 controlarrayfreeU( clientctrlsU );
659
660 #endif
661 return ret;
662 }
663
664 /***********************************************************************
665 * ldap_startup (WLDAP32.@)
666 */
667 ULONG CDECL ldap_startup( PLDAP_VERSION_INFO version, HANDLE *instance )
668 {
669 TRACE( "(%p, %p)\n", version, instance );
670 return WLDAP32_LDAP_SUCCESS;
671 }
672
673 /***********************************************************************
674 * ldap_stop_tls_s (WLDAP32.@)
675 *
676 * Stop TLS encryption on an LDAP connection.
677 *
678 * PARAMS
679 * ld [I] Pointer to an LDAP context.
680 *
681 * RETURNS
682 * Success: TRUE
683 * Failure: FALSE
684 */
685 BOOLEAN CDECL ldap_stop_tls_s( WLDAP32_LDAP *ld )
686 {
687 TRACE( "(%p)\n", ld );
688 return TRUE; /* FIXME: find a way to stop tls on a connection */
689 }