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