2 * WLDAP32 - LDAP support for Wine
4 * Copyright 2005 Hans Leidekker
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.
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.
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
22 #include "wine/port.h"
34 #include "winldap_private.h"
36 #include "wine/debug.h"
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 };
42 /* Split a space separated string of hostnames into a string array */
43 static char **split_hostnames( const char *hostnames
)
45 char **res
, *str
, *p
, *q
;
48 str
= strdupU( hostnames
);
49 if (!str
) return NULL
;
52 while (isspace( *p
)) p
++;
59 while (isspace( *p
)) p
++;
65 if (!(res
= heap_alloc( (i
+ 1) * sizeof(char *) )))
72 while (isspace( *p
)) p
++;
84 res
[i
] = strdupU( q
);
85 if (!res
[i
]) goto oom
;
88 while (isspace( *p
)) p
++;
94 res
[i
] = strdupU( q
);
95 if (!res
[i
]) goto oom
;
106 while (i
> 0) strfreeU( res
[--i
] );
114 /* Determine if a URL starts with a known LDAP scheme */
115 static BOOL
has_ldap_scheme( char *url
)
117 return !strncasecmp( url
, "ldap://", 7 ) ||
118 !strncasecmp( url
, "ldaps://", 8 ) ||
119 !strncasecmp( url
, "ldapi://", 8 ) ||
120 !strncasecmp( url
, "cldap://", 8 );
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
127 static char *join_hostnames( const char *scheme
, char **hostnames
, ULONG portnumber
)
129 char *res
, *p
, *q
, **v
;
130 unsigned int i
= 0, size
= 0;
131 static const char sep
[] = " ", fmt
[] = ":%d";
134 sprintf( port
, fmt
, portnumber
);
136 for (v
= hostnames
; *v
; v
++)
138 if (!has_ldap_scheme( *v
))
140 size
+= strlen( scheme
);
144 /* skip past colon in scheme prefix */
145 q
= strchr( *v
, '/' );
147 size
+= strlen( *v
);
149 if (!strchr( q
, ':' ))
150 size
+= strlen( port
);
155 size
+= (i
- 1) * strlen( sep
);
156 if (!(res
= heap_alloc( size
+ 1 ))) return NULL
;
159 for (v
= hostnames
; *v
; v
++)
167 if (!has_ldap_scheme( *v
))
170 p
+= strlen( scheme
);
174 /* skip past colon in scheme prefix */
175 q
= strchr( *v
, '/' );
180 if (!strchr( q
, ':' ))
189 static char *urlify_hostnames( const char *scheme
, char *hostnames
, ULONG port
)
191 char *url
= NULL
, **strarray
;
193 strarray
= split_hostnames( hostnames
);
195 url
= join_hostnames( scheme
, strarray
, port
);
199 strarrayfreeU( strarray
);
204 WINE_DEFAULT_DEBUG_CHANNEL(wldap32
);
207 static LDAP
*create_context( const char *url
)
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
);
217 /***********************************************************************
218 * cldap_openA (WLDAP32.@)
222 WLDAP32_LDAP
* CDECL
cldap_openA( PCHAR hostname
, ULONG portnumber
)
225 WLDAP32_LDAP
*ld
= NULL
;
226 WCHAR
*hostnameW
= NULL
;
228 TRACE( "(%s, %d)\n", debugstr_a(hostname
), portnumber
);
231 hostnameW
= strAtoW( hostname
);
232 if (!hostnameW
) goto exit
;
235 ld
= cldap_openW( hostnameW
, portnumber
);
238 strfreeW( hostnameW
);
246 /***********************************************************************
247 * cldap_openW (WLDAP32.@)
249 * Initialize an LDAP context and create a UDP connection.
252 * hostname [I] Name of the host to connect to.
253 * portnumber [I] Port number to use.
256 * Success: Pointer to an LDAP context.
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
267 WLDAP32_LDAP
* CDECL
cldap_openW( PWCHAR hostname
, ULONG portnumber
)
271 char *hostnameU
= NULL
, *url
= NULL
;
273 TRACE( "(%s, %d)\n", debugstr_w(hostname
), portnumber
);
276 hostnameU
= strWtoU( hostname
);
277 if (!hostnameU
) goto exit
;
280 hostnameU
= strWtoU( defaulthost
);
281 if (!hostnameU
) goto exit
;
284 url
= urlify_hostnames( "cldap://", hostnameU
, portnumber
);
287 ld
= create_context( url
);
290 strfreeU( hostnameU
);
299 /***********************************************************************
300 * ldap_connect (WLDAP32.@)
302 * Connect to an LDAP server.
305 * ld [I] Pointer to an LDAP context.
306 * timeout [I] Pointer to an l_timeval structure specifying the
307 * timeout in seconds.
310 * Success: LDAP_SUCCESS
311 * Failure: An LDAP error code.
314 * The timeout parameter may be NULL in which case a default timeout
315 * value will be used.
317 ULONG CDECL
ldap_connect( WLDAP32_LDAP
*ld
, struct l_timeval
*timeout
)
319 TRACE( "(%p, %p)\n", ld
, timeout
);
321 if (!ld
) return WLDAP32_LDAP_PARAM_ERROR
;
322 return WLDAP32_LDAP_SUCCESS
; /* FIXME: do something, e.g. ping the host */
325 /***********************************************************************
326 * ldap_initA (WLDAP32.@)
330 WLDAP32_LDAP
* CDECL
ldap_initA( const PCHAR hostname
, ULONG portnumber
)
333 WLDAP32_LDAP
*ld
= NULL
;
334 WCHAR
*hostnameW
= NULL
;
336 TRACE( "(%s, %d)\n", debugstr_a(hostname
), portnumber
);
339 hostnameW
= strAtoW( hostname
);
340 if (!hostnameW
) goto exit
;
343 ld
= ldap_initW( hostnameW
, portnumber
);
346 strfreeW( hostnameW
);
354 /***********************************************************************
355 * ldap_initW (WLDAP32.@)
357 * Initialize an LDAP context and create a TCP connection.
360 * hostname [I] Name of the host to connect to.
361 * portnumber [I] Port number to use.
364 * Success: Pointer to an LDAP context.
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.
376 WLDAP32_LDAP
* CDECL
ldap_initW( const PWCHAR hostname
, ULONG portnumber
)
380 char *hostnameU
= NULL
, *url
= NULL
;
382 TRACE( "(%s, %d)\n", debugstr_w(hostname
), portnumber
);
385 hostnameU
= strWtoU( hostname
);
386 if (!hostnameU
) goto exit
;
389 hostnameU
= strWtoU( defaulthost
);
390 if (!hostnameU
) goto exit
;
393 url
= urlify_hostnames( "ldap://", hostnameU
, portnumber
);
396 ld
= create_context( url
);
399 strfreeU( hostnameU
);
408 /***********************************************************************
409 * ldap_openA (WLDAP32.@)
413 WLDAP32_LDAP
* CDECL
ldap_openA( PCHAR hostname
, ULONG portnumber
)
416 WLDAP32_LDAP
*ld
= NULL
;
417 WCHAR
*hostnameW
= NULL
;
419 TRACE( "(%s, %d)\n", debugstr_a(hostname
), portnumber
);
422 hostnameW
= strAtoW( hostname
);
423 if (!hostnameW
) goto exit
;
426 ld
= ldap_openW( hostnameW
, portnumber
);
429 strfreeW( hostnameW
);
437 /***********************************************************************
438 * ldap_openW (WLDAP32.@)
440 * Initialize an LDAP context and create a TCP connection.
443 * hostname [I] Name of the host to connect to.
444 * portnumber [I] Port number to use.
447 * Success: Pointer to an LDAP context.
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
458 WLDAP32_LDAP
* CDECL
ldap_openW( PWCHAR hostname
, ULONG portnumber
)
462 char *hostnameU
= NULL
, *url
= NULL
;
464 TRACE( "(%s, %d)\n", debugstr_w(hostname
), portnumber
);
467 hostnameU
= strWtoU( hostname
);
468 if (!hostnameU
) goto exit
;
471 hostnameU
= strWtoU( defaulthost
);
472 if (!hostnameU
) goto exit
;
475 url
= urlify_hostnames( "ldap://", hostnameU
, portnumber
);
478 ld
= create_context( url
);
481 strfreeU( hostnameU
);
490 /***********************************************************************
491 * ldap_sslinitA (WLDAP32.@)
495 WLDAP32_LDAP
* CDECL
ldap_sslinitA( PCHAR hostname
, ULONG portnumber
, int secure
)
499 WCHAR
*hostnameW
= NULL
;
501 TRACE( "(%s, %d, 0x%08x)\n", debugstr_a(hostname
), portnumber
, secure
);
504 hostnameW
= strAtoW( hostname
);
505 if (!hostnameW
) return NULL
;
508 ld
= ldap_sslinitW( hostnameW
, portnumber
, secure
);
510 strfreeW( hostnameW
);
518 /***********************************************************************
519 * ldap_sslinitW (WLDAP32.@)
521 * Initialize an LDAP context and create a secure TCP connection.
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.
529 * Success: Pointer to an LDAP context.
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.
541 WLDAP32_LDAP
* CDECL
ldap_sslinitW( PWCHAR hostname
, ULONG portnumber
, int secure
)
544 WLDAP32_LDAP
*ld
= NULL
;
545 char *hostnameU
= NULL
, *url
= NULL
;
547 TRACE( "(%s, %d, 0x%08x)\n", debugstr_w(hostname
), portnumber
, secure
);
550 hostnameU
= strWtoU( hostname
);
551 if (!hostnameU
) goto exit
;
554 hostnameU
= strWtoU( defaulthost
);
555 if (!hostnameU
) goto exit
;
559 url
= urlify_hostnames( "ldaps://", hostnameU
, portnumber
);
561 url
= urlify_hostnames( "ldap://", hostnameU
, portnumber
);
564 ldap_initialize( &ld
, url
);
567 strfreeU( hostnameU
);
576 /***********************************************************************
577 * ldap_start_tls_sA (WLDAP32.@)
579 * See ldap_start_tls_sW.
581 ULONG CDECL
ldap_start_tls_sA( WLDAP32_LDAP
*ld
, PULONG retval
, WLDAP32_LDAPMessage
**result
,
582 PLDAPControlA
*serverctrls
, PLDAPControlA
*clientctrls
)
584 ULONG ret
= WLDAP32_LDAP_NOT_SUPPORTED
;
586 LDAPControlW
**serverctrlsW
= NULL
, **clientctrlsW
= NULL
;
588 ret
= WLDAP32_LDAP_NO_MEMORY
;
590 TRACE( "(%p, %p, %p, %p, %p)\n", ld
, retval
, result
, serverctrls
, clientctrls
);
595 serverctrlsW
= controlarrayAtoW( serverctrls
);
596 if (!serverctrlsW
) goto exit
;
599 clientctrlsW
= controlarrayAtoW( clientctrls
);
600 if (!clientctrlsW
) goto exit
;
603 ret
= ldap_start_tls_sW( ld
, retval
, result
, serverctrlsW
, clientctrlsW
);
606 controlarrayfreeW( serverctrlsW
);
607 controlarrayfreeW( clientctrlsW
);
613 /***********************************************************************
614 * ldap_start_tls_s (WLDAP32.@)
616 * Start TLS encryption on an LDAP connection.
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.
626 * Success: LDAP_SUCCESS
627 * Failure: An LDAP error code.
630 * LDAP function that needs it is called.
632 ULONG CDECL
ldap_start_tls_sW( WLDAP32_LDAP
*ld
, PULONG retval
, WLDAP32_LDAPMessage
**result
,
633 PLDAPControlW
*serverctrls
, PLDAPControlW
*clientctrls
)
635 ULONG ret
= WLDAP32_LDAP_NOT_SUPPORTED
;
637 LDAPControl
**serverctrlsU
= NULL
, **clientctrlsU
= NULL
;
639 ret
= WLDAP32_LDAP_NO_MEMORY
;
641 TRACE( "(%p, %p, %p, %p, %p)\n", ld
, retval
, result
, serverctrls
, clientctrls
);
646 serverctrlsU
= controlarrayWtoU( serverctrls
);
647 if (!serverctrlsU
) goto exit
;
650 clientctrlsU
= controlarrayWtoU( clientctrls
);
651 if (!clientctrlsU
) goto exit
;
654 ret
= map_error( ldap_start_tls_s( ld
, serverctrlsU
, clientctrlsU
));
657 controlarrayfreeU( serverctrlsU
);
658 controlarrayfreeU( clientctrlsU
);
664 /***********************************************************************
665 * ldap_startup (WLDAP32.@)
667 ULONG CDECL
ldap_startup( PLDAP_VERSION_INFO version
, HANDLE
*instance
)
669 TRACE( "(%p, %p)\n", version
, instance
);
670 return WLDAP32_LDAP_SUCCESS
;
673 /***********************************************************************
674 * ldap_stop_tls_s (WLDAP32.@)
676 * Stop TLS encryption on an LDAP connection.
679 * ld [I] Pointer to an LDAP context.
685 BOOLEAN CDECL
ldap_stop_tls_s( WLDAP32_LDAP
*ld
)
687 TRACE( "(%p)\n", ld
);
688 return TRUE
; /* FIXME: find a way to stop tls on a connection */