1/* $OpenLDAP$ */ 2/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 1999-2011 The OpenLDAP Foundation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted only as authorized by the OpenLDAP 9 * Public License. 10 * 11 * A copy of this license is available in file LICENSE in the 12 * top-level directory of the distribution or, alternatively, at 13 * <http://www.OpenLDAP.org/license.html>. 14 */ 15/* ACKNOWLEDGEMENTS: 16 * This work was initially developed by Howard Chu for inclusion 17 * in OpenLDAP Software. 18 */ 19 20#include "portable.h" 21 22#include <stdio.h> 23 24#include "ac/stdlib.h" 25#include "ac/time.h" 26 27#include "ac/ctype.h" 28#include "ac/param.h" 29#include "ac/socket.h" 30#include "ac/string.h" 31#include "ac/unistd.h" 32#include "ac/wait.h" 33#include "ac/time.h" 34 35#include "ldap.h" 36#include "lutil.h" 37#include "lber_pvt.h" 38#include "ldap_pvt.h" 39 40#include "slapd-common.h" 41 42#define LOOPS 100 43 44static int 45do_bind( char *uri, char *dn, struct berval *pass, int maxloop, 46 int force, int chaserefs, int noinit, LDAP **ldp, 47 int action_type, void *action ); 48 49static int 50do_base( char *uri, char *dn, struct berval *pass, char *base, char *filter, char *pwattr, 51 int maxloop, int force, int chaserefs, int noinit, int delay, 52 int action_type, void *action ); 53 54/* This program can be invoked two ways: if -D is used to specify a Bind DN, 55 * that DN will be used repeatedly for all of the Binds. If instead -b is used 56 * to specify a base DN, a search will be done for all "person" objects under 57 * that base DN. Then DNs from this list will be randomly selected for each 58 * Bind request. All of the users must have identical passwords. Also it is 59 * assumed that the users are all onelevel children of the base. 60 */ 61static void 62usage( char *name, char opt ) 63{ 64 if ( opt ) { 65 fprintf( stderr, "%s: unable to handle option \'%c\'\n\n", 66 name, opt ); 67 } 68 69 fprintf( stderr, "usage: %s " 70 "[-H uri | -h <host> [-p port]] " 71 "[-D <dn> [-w <passwd>]] " 72 "[-b <baseDN> [-f <searchfilter>] [-a pwattr]] " 73 "[-l <loops>] " 74 "[-L <outerloops>] " 75 "[-B <extra>[,...]] " 76 "[-F] " 77 "[-C] " 78 "[-I] " 79 "[-i <ignore>] " 80 "[-t delay]\n", 81 name ); 82 exit( EXIT_FAILURE ); 83} 84 85int 86main( int argc, char **argv ) 87{ 88 int i; 89 char *uri = NULL; 90 char *host = "localhost"; 91 char *dn = NULL; 92 char *base = NULL; 93 char *filter = "(objectClass=person)"; 94 struct berval pass = { 0, NULL }; 95 char *pwattr = NULL; 96 int port = -1; 97 int loops = LOOPS; 98 int outerloops = 1; 99 int force = 0; 100 int chaserefs = 0; 101 int noinit = 1; 102 int delay = 0; 103 104 /* extra action to do after bind... */ 105 struct berval type[] = { 106 BER_BVC( "tester=" ), 107 BER_BVC( "add=" ), 108 BER_BVC( "bind=" ), 109 BER_BVC( "modify=" ), 110 BER_BVC( "modrdn=" ), 111 BER_BVC( "read=" ), 112 BER_BVC( "search=" ), 113 BER_BVNULL 114 }; 115 116 LDAPURLDesc *extra_ludp = NULL; 117 118 tester_init( "slapd-bind", TESTER_BIND ); 119 120 /* by default, tolerate invalid credentials */ 121 tester_ignore_str2errlist( "INVALID_CREDENTIALS" ); 122 123 while ( ( i = getopt( argc, argv, "a:B:b:D:Ff:H:h:Ii:L:l:p:t:w:" ) ) != EOF ) 124 { 125 switch ( i ) { 126 case 'a': 127 pwattr = optarg; 128 break; 129 130 case 'b': /* base DN of a tree of user DNs */ 131 base = optarg; 132 break; 133 134 case 'B': 135 { 136 int c; 137 138 for ( c = 0; type[c].bv_val; c++ ) { 139 if ( strncasecmp( optarg, type[c].bv_val, type[c].bv_len ) == 0 ) 140 { 141 break; 142 } 143 } 144 145 if ( type[c].bv_val == NULL ) { 146 usage( argv[0], 'B' ); 147 } 148 149 switch ( c ) { 150 case TESTER_TESTER: 151 case TESTER_BIND: 152 /* invalid */ 153 usage( argv[0], 'B' ); 154 155 case TESTER_SEARCH: 156 { 157 if ( ldap_url_parse( &optarg[type[c].bv_len], &extra_ludp ) != LDAP_URL_SUCCESS ) 158 { 159 usage( argv[0], 'B' ); 160 } 161 } break; 162 163 case TESTER_ADDEL: 164 case TESTER_MODIFY: 165 case TESTER_MODRDN: 166 case TESTER_READ: 167 /* nothing to do */ 168 break; 169 170 default: 171 assert( 0 ); 172 } 173 174 } break; 175 176 case 'C': 177 chaserefs++; 178 break; 179 180 case 'H': /* the server uri */ 181 uri = optarg; 182 break; 183 184 case 'h': /* the servers host */ 185 host = optarg; 186 break; 187 188 case 'i': 189 tester_ignore_str2errlist( optarg ); 190 break; 191 192 case 'p': /* the servers port */ 193 if ( lutil_atoi( &port, optarg ) != 0 ) { 194 usage( argv[0], 'p' ); 195 } 196 break; 197 198 case 'D': 199 dn = optarg; 200 break; 201 202 case 'w': 203 ber_str2bv( optarg, 0, 1, &pass ); 204 memset( optarg, '*', pass.bv_len ); 205 break; 206 207 case 'l': /* the number of loops */ 208 if ( lutil_atoi( &loops, optarg ) != 0 ) { 209 usage( argv[0], 'l' ); 210 } 211 break; 212 213 case 'L': /* the number of outerloops */ 214 if ( lutil_atoi( &outerloops, optarg ) != 0 ) { 215 usage( argv[0], 'L' ); 216 } 217 break; 218 219 case 'f': 220 filter = optarg; 221 break; 222 223 case 'F': 224 force++; 225 break; 226 227 case 'I': 228 /* reuse connection */ 229 noinit = 0; 230 break; 231 232 case 't': 233 /* sleep between binds */ 234 if ( lutil_atoi( &delay, optarg ) != 0 ) { 235 usage( argv[0], 't' ); 236 } 237 break; 238 239 default: 240 usage( argv[0], i ); 241 break; 242 } 243 } 244 245 if ( port == -1 && uri == NULL ) { 246 usage( argv[0], '\0' ); 247 } 248 249 uri = tester_uri( uri, host, port ); 250 251 for ( i = 0; i < outerloops; i++ ) { 252 int rc; 253 254 if ( base != NULL ) { 255 rc = do_base( uri, dn, &pass, base, filter, pwattr, loops, 256 force, chaserefs, noinit, delay, -1, NULL ); 257 } else { 258 rc = do_bind( uri, dn, &pass, loops, 259 force, chaserefs, noinit, NULL, -1, NULL ); 260 } 261 if ( rc == LDAP_SERVER_DOWN ) 262 break; 263 } 264 265 exit( EXIT_SUCCESS ); 266} 267 268 269static int 270do_bind( char *uri, char *dn, struct berval *pass, int maxloop, 271 int force, int chaserefs, int noinit, LDAP **ldp, 272 int action_type, void *action ) 273{ 274 LDAP *ld = ldp ? *ldp : NULL; 275 int i, rc = -1; 276 277 /* for internal search */ 278 int timelimit = 0; 279 int sizelimit = 0; 280 281 switch ( action_type ) { 282 case -1: 283 break; 284 285 case TESTER_SEARCH: 286 { 287 LDAPURLDesc *ludp = (LDAPURLDesc *)action; 288 289 assert( action != NULL ); 290 291 if ( ludp->lud_exts != NULL ) { 292 for ( i = 0; ludp->lud_exts[ i ] != NULL; i++ ) { 293 char *ext = ludp->lud_exts[ i ]; 294 int crit = 0; 295 296 if (ext[0] == '!') { 297 crit++; 298 ext++; 299 } 300 301 if ( strncasecmp( ext, "x-timelimit=", STRLENOF( "x-timelimit=" ) ) == 0 ) { 302 if ( lutil_atoi( &timelimit, &ext[ STRLENOF( "x-timelimit=" ) ] ) && crit ) { 303 tester_error( "unable to parse critical extension x-timelimit" ); 304 } 305 306 } else if ( strncasecmp( ext, "x-sizelimit=", STRLENOF( "x-sizelimit=" ) ) == 0 ) { 307 if ( lutil_atoi( &sizelimit, &ext[ STRLENOF( "x-sizelimit=" ) ] ) && crit ) { 308 tester_error( "unable to parse critical extension x-sizelimit" ); 309 } 310 311 } else if ( crit ) { 312 tester_error( "unknown critical extension" ); 313 } 314 } 315 } 316 } break; 317 318 default: 319 /* nothing to do yet */ 320 break; 321 } 322 323 if ( maxloop > 1 ) { 324 fprintf( stderr, "PID=%ld - Bind(%d): dn=\"%s\".\n", 325 (long) pid, maxloop, dn ); 326 } 327 328 for ( i = 0; i < maxloop; i++ ) { 329 if ( !noinit || ld == NULL ) { 330 int version = LDAP_VERSION3; 331 ldap_initialize( &ld, uri ); 332 if ( ld == NULL ) { 333 tester_perror( "ldap_initialize", NULL ); 334 rc = -1; 335 break; 336 } 337 338 (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, 339 &version ); 340 (void) ldap_set_option( ld, LDAP_OPT_REFERRALS, 341 chaserefs ? LDAP_OPT_ON: LDAP_OPT_OFF ); 342 } 343 344 rc = ldap_sasl_bind_s( ld, dn, LDAP_SASL_SIMPLE, pass, NULL, NULL, NULL ); 345 if ( rc ) { 346 int first = tester_ignore_err( rc ); 347 348 /* if ignore.. */ 349 if ( first ) { 350 /* only log if first occurrence */ 351 if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) { 352 tester_ldap_error( ld, "ldap_sasl_bind_s", NULL ); 353 } 354 rc = LDAP_SUCCESS; 355 356 } else { 357 tester_ldap_error( ld, "ldap_sasl_bind_s", NULL ); 358 } 359 } 360 361 switch ( action_type ) { 362 case -1: 363 break; 364 365 case TESTER_SEARCH: 366 { 367 LDAPURLDesc *ludp = (LDAPURLDesc *)action; 368 LDAPMessage *res = NULL; 369 struct timeval tv = { 0 }, *tvp = NULL; 370 371 if ( timelimit ) { 372 tv.tv_sec = timelimit; 373 tvp = &tv; 374 } 375 376 assert( action != NULL ); 377 378 rc = ldap_search_ext_s( ld, 379 ludp->lud_dn, ludp->lud_scope, 380 ludp->lud_filter, ludp->lud_attrs, 0, 381 NULL, NULL, tvp, sizelimit, &res ); 382 ldap_msgfree( res ); 383 } break; 384 385 default: 386 /* nothing to do yet */ 387 break; 388 } 389 390 if ( !noinit ) { 391 ldap_unbind_ext( ld, NULL, NULL ); 392 ld = NULL; 393 } 394 395 if ( rc != LDAP_SUCCESS ) { 396 break; 397 } 398 } 399 400 if ( maxloop > 1 ) { 401 fprintf( stderr, " PID=%ld - Bind done (%d).\n", (long) pid, rc ); 402 } 403 404 if ( ldp && noinit ) { 405 *ldp = ld; 406 407 } else if ( ld != NULL ) { 408 ldap_unbind_ext( ld, NULL, NULL ); 409 } 410 411 return rc; 412} 413 414 415static int 416do_base( char *uri, char *dn, struct berval *pass, char *base, char *filter, char *pwattr, 417 int maxloop, int force, int chaserefs, int noinit, int delay, 418 int action_type, void *action ) 419{ 420 LDAP *ld = NULL; 421 int i = 0; 422 int rc = LDAP_SUCCESS; 423 ber_int_t msgid; 424 LDAPMessage *res, *msg; 425 char **dns = NULL; 426 struct berval *creds = NULL; 427 char *attrs[] = { LDAP_NO_ATTRS, NULL }; 428 int ndns = 0; 429#ifdef _WIN32 430 DWORD beg, end; 431#else 432 struct timeval beg, end; 433#endif 434 int version = LDAP_VERSION3; 435 char *nullstr = ""; 436 437 ldap_initialize( &ld, uri ); 438 if ( ld == NULL ) { 439 tester_perror( "ldap_initialize", NULL ); 440 exit( EXIT_FAILURE ); 441 } 442 443 (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ); 444 (void) ldap_set_option( ld, LDAP_OPT_REFERRALS, 445 chaserefs ? LDAP_OPT_ON: LDAP_OPT_OFF ); 446 447 rc = ldap_sasl_bind_s( ld, dn, LDAP_SASL_SIMPLE, pass, NULL, NULL, NULL ); 448 if ( rc != LDAP_SUCCESS ) { 449 tester_ldap_error( ld, "ldap_sasl_bind_s", NULL ); 450 exit( EXIT_FAILURE ); 451 } 452 453 fprintf( stderr, "PID=%ld - Bind(%d): base=\"%s\", filter=\"%s\" attr=\"%s\".\n", 454 (long) pid, maxloop, base, filter, pwattr ); 455 456 if ( pwattr != NULL ) { 457 attrs[ 0 ] = pwattr; 458 } 459 rc = ldap_search_ext( ld, base, LDAP_SCOPE_SUBTREE, 460 filter, attrs, 0, NULL, NULL, 0, 0, &msgid ); 461 if ( rc != LDAP_SUCCESS ) { 462 tester_ldap_error( ld, "ldap_search_ext", NULL ); 463 exit( EXIT_FAILURE ); 464 } 465 466 while ( ( rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ONE, NULL, &res ) ) > 0 ) 467 { 468 BerElement *ber; 469 struct berval bv; 470 int done = 0; 471 472 for ( msg = ldap_first_message( ld, res ); msg; 473 msg = ldap_next_message( ld, msg ) ) 474 { 475 switch ( ldap_msgtype( msg ) ) { 476 case LDAP_RES_SEARCH_ENTRY: 477 rc = ldap_get_dn_ber( ld, msg, &ber, &bv ); 478 dns = realloc( dns, (ndns + 1)*sizeof(char *) ); 479 dns[ndns] = ber_strdup( bv.bv_val ); 480 if ( pwattr != NULL ) { 481 struct berval **values = ldap_get_values_len( ld, msg, pwattr ); 482 483 creds = realloc( creds, (ndns + 1)*sizeof(struct berval) ); 484 if ( values == NULL ) { 485novals:; 486 creds[ndns].bv_len = 0; 487 creds[ndns].bv_val = nullstr; 488 489 } else { 490 static struct berval cleartext = BER_BVC( "{CLEARTEXT} " ); 491 struct berval value = *values[ 0 ]; 492 493 if ( value.bv_val[ 0 ] == '{' ) { 494 char *end = ber_bvchr( &value, '}' ); 495 496 if ( end ) { 497 if ( ber_bvcmp( &value, &cleartext ) == 0 ) { 498 value.bv_val += cleartext.bv_len; 499 value.bv_len -= cleartext.bv_len; 500 501 } else { 502 ldap_value_free_len( values ); 503 goto novals; 504 } 505 } 506 507 } 508 509 ber_dupbv( &creds[ndns], &value ); 510 ldap_value_free_len( values ); 511 } 512 } 513 ndns++; 514 ber_free( ber, 0 ); 515 break; 516 517 case LDAP_RES_SEARCH_RESULT: 518 done = 1; 519 break; 520 } 521 if ( done ) 522 break; 523 } 524 ldap_msgfree( res ); 525 if ( done ) break; 526 } 527 528#ifdef _WIN32 529 beg = GetTickCount(); 530#else 531 gettimeofday( &beg, NULL ); 532#endif 533 534 if ( ndns == 0 ) { 535 tester_error( "No DNs" ); 536 return 1; 537 } 538 539 fprintf( stderr, " PID=%ld - Bind base=\"%s\" filter=\"%s\" got %d values.\n", 540 (long) pid, base, filter, ndns ); 541 542 /* Ok, got list of DNs, now start binding to each */ 543 for ( i = 0; i < maxloop; i++ ) { 544 int j; 545 struct berval cred = { 0, NULL }; 546 547 548#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */ 549 j = rand() % ndns; 550#endif 551 j = ((double)ndns)*rand()/(RAND_MAX + 1.0); 552 553 if ( creds && !BER_BVISEMPTY( &creds[j] ) ) { 554 cred = creds[j]; 555 } 556 557 if ( do_bind( uri, dns[j], &cred, 1, force, chaserefs, noinit, &ld, 558 action_type, action ) && !force ) 559 { 560 break; 561 } 562 563 if ( delay ) { 564 sleep( delay ); 565 } 566 } 567 568 if ( ld != NULL ) { 569 ldap_unbind_ext( ld, NULL, NULL ); 570 ld = NULL; 571 } 572 573#ifdef _WIN32 574 end = GetTickCount(); 575 end -= beg; 576 577 fprintf( stderr, " PID=%ld - Bind done %d in %d.%03d seconds.\n", 578 (long) pid, i, end / 1000, end % 1000 ); 579#else 580 gettimeofday( &end, NULL ); 581 end.tv_usec -= beg.tv_usec; 582 if (end.tv_usec < 0 ) { 583 end.tv_usec += 1000000; 584 end.tv_sec -= 1; 585 } 586 end.tv_sec -= beg.tv_sec; 587 588 fprintf( stderr, " PID=%ld - Bind done %d in %ld.%06ld seconds.\n", 589 (long) pid, i, (long) end.tv_sec, (long) end.tv_usec ); 590#endif 591 592 if ( dns ) { 593 for ( i = 0; i < ndns; i++ ) { 594 ber_memfree( dns[i] ); 595 } 596 free( dns ); 597 } 598 599 if ( creds ) { 600 for ( i = 0; i < ndns; i++ ) { 601 if ( creds[i].bv_val != nullstr ) { 602 ber_memfree( creds[i].bv_val ); 603 } 604 } 605 free( creds ); 606 } 607 608 return 0; 609} 610