xref: /reactos/dll/win32/wldap32/init.c (revision 6b9bd93f)
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