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