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