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 */ 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 */ 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 */ 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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