1 /* $OpenLDAP: pkg/ldap/tests/progs/slapd-bind.c,v 1.18.2.7 2008/02/11 23:26:50 kurt Exp $ */ 2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 1999-2008 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 44 static int 45 do_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 49 static int 50 do_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 */ 61 static void 62 usage( 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 85 int 86 main( 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 if ( base != NULL ) { 253 do_base( uri, dn, &pass, base, filter, pwattr, loops, 254 force, chaserefs, noinit, delay, -1, NULL ); 255 } else { 256 do_bind( uri, dn, &pass, loops, 257 force, chaserefs, noinit, NULL, -1, NULL ); 258 } 259 } 260 261 exit( EXIT_SUCCESS ); 262 } 263 264 265 static int 266 do_bind( char *uri, char *dn, struct berval *pass, int maxloop, 267 int force, int chaserefs, int noinit, LDAP **ldp, 268 int action_type, void *action ) 269 { 270 LDAP *ld = ldp ? *ldp : NULL; 271 int i, rc = -1; 272 273 /* for internal search */ 274 int timelimit = 0; 275 int sizelimit = 0; 276 277 switch ( action_type ) { 278 case -1: 279 break; 280 281 case TESTER_SEARCH: 282 { 283 LDAPURLDesc *ludp = (LDAPURLDesc *)action; 284 285 assert( action != NULL ); 286 287 if ( ludp->lud_exts != NULL ) { 288 for ( i = 0; ludp->lud_exts[ i ] != NULL; i++ ) { 289 char *ext = ludp->lud_exts[ i ]; 290 int crit = 0; 291 292 if (ext[0] == '!') { 293 crit++; 294 ext++; 295 } 296 297 if ( strncasecmp( ext, "x-timelimit=", STRLENOF( "x-timelimit=" ) ) == 0 ) { 298 if ( lutil_atoi( &timelimit, &ext[ STRLENOF( "x-timelimit=" ) ] ) && crit ) { 299 tester_error( "unable to parse critical extension x-timelimit" ); 300 } 301 302 } else if ( strncasecmp( ext, "x-sizelimit=", STRLENOF( "x-sizelimit=" ) ) == 0 ) { 303 if ( lutil_atoi( &sizelimit, &ext[ STRLENOF( "x-sizelimit=" ) ] ) && crit ) { 304 tester_error( "unable to parse critical extension x-sizelimit" ); 305 } 306 307 } else if ( crit ) { 308 tester_error( "unknown critical extension" ); 309 } 310 } 311 } 312 } break; 313 314 default: 315 /* nothing to do yet */ 316 break; 317 } 318 319 if ( maxloop > 1 ) { 320 fprintf( stderr, "PID=%ld - Bind(%d): dn=\"%s\".\n", 321 (long) pid, maxloop, dn ); 322 } 323 324 for ( i = 0; i < maxloop; i++ ) { 325 if ( !noinit || ld == NULL ) { 326 int version = LDAP_VERSION3; 327 ldap_initialize( &ld, uri ); 328 if ( ld == NULL ) { 329 tester_perror( "ldap_initialize", NULL ); 330 rc = -1; 331 break; 332 } 333 334 (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, 335 &version ); 336 (void) ldap_set_option( ld, LDAP_OPT_REFERRALS, 337 chaserefs ? LDAP_OPT_ON: LDAP_OPT_OFF ); 338 } 339 340 rc = ldap_sasl_bind_s( ld, dn, LDAP_SASL_SIMPLE, pass, NULL, NULL, NULL ); 341 if ( rc ) { 342 unsigned first = tester_ignore_err( rc ); 343 344 /* if ignore.. */ 345 if ( first ) { 346 /* only log if first occurrence */ 347 if ( force < 2 || first == 1 ) { 348 tester_ldap_error( ld, "ldap_sasl_bind_s", NULL ); 349 } 350 rc = LDAP_SUCCESS; 351 352 } else { 353 tester_ldap_error( ld, "ldap_sasl_bind_s", NULL ); 354 } 355 } 356 357 switch ( action_type ) { 358 case -1: 359 break; 360 361 case TESTER_SEARCH: 362 { 363 LDAPURLDesc *ludp = (LDAPURLDesc *)action; 364 LDAPMessage *res = NULL; 365 struct timeval tv = { 0 }, *tvp = NULL; 366 367 if ( timelimit ) { 368 tv.tv_sec = timelimit; 369 tvp = &tv; 370 } 371 372 assert( action != NULL ); 373 374 rc = ldap_search_ext_s( ld, 375 ludp->lud_dn, ludp->lud_scope, 376 ludp->lud_filter, ludp->lud_attrs, 0, 377 NULL, NULL, tvp, sizelimit, &res ); 378 ldap_msgfree( res ); 379 } break; 380 381 default: 382 /* nothing to do yet */ 383 break; 384 } 385 386 if ( !noinit ) { 387 ldap_unbind_ext( ld, NULL, NULL ); 388 ld = NULL; 389 } 390 391 if ( rc != LDAP_SUCCESS ) { 392 break; 393 } 394 } 395 396 if ( maxloop > 1 ) { 397 fprintf( stderr, " PID=%ld - Bind done (%d).\n", (long) pid, rc ); 398 } 399 400 if ( ldp && noinit ) { 401 *ldp = ld; 402 403 } else if ( ld != NULL ) { 404 ldap_unbind_ext( ld, NULL, NULL ); 405 } 406 407 return rc; 408 } 409 410 411 static int 412 do_base( char *uri, char *dn, struct berval *pass, char *base, char *filter, char *pwattr, 413 int maxloop, int force, int chaserefs, int noinit, int delay, 414 int action_type, void *action ) 415 { 416 LDAP *ld = NULL; 417 int i = 0; 418 int rc = LDAP_SUCCESS; 419 ber_int_t msgid; 420 LDAPMessage *res, *msg; 421 char **dns = NULL; 422 struct berval *creds = NULL; 423 char *attrs[] = { LDAP_NO_ATTRS, NULL }; 424 int ndns = 0; 425 #ifdef _WIN32 426 DWORD beg, end; 427 #else 428 struct timeval beg, end; 429 #endif 430 int version = LDAP_VERSION3; 431 char *nullstr = ""; 432 433 ldap_initialize( &ld, uri ); 434 if ( ld == NULL ) { 435 tester_perror( "ldap_initialize", NULL ); 436 exit( EXIT_FAILURE ); 437 } 438 439 (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ); 440 (void) ldap_set_option( ld, LDAP_OPT_REFERRALS, 441 chaserefs ? LDAP_OPT_ON: LDAP_OPT_OFF ); 442 443 rc = ldap_sasl_bind_s( ld, dn, LDAP_SASL_SIMPLE, pass, NULL, NULL, NULL ); 444 if ( rc != LDAP_SUCCESS ) { 445 tester_ldap_error( ld, "ldap_sasl_bind_s", NULL ); 446 exit( EXIT_FAILURE ); 447 } 448 449 fprintf( stderr, "PID=%ld - Bind(%d): base=\"%s\", filter=\"%s\" attr=\"%s\".\n", 450 (long) pid, maxloop, base, filter, pwattr ); 451 452 if ( pwattr != NULL ) { 453 attrs[ 0 ] = pwattr; 454 } 455 rc = ldap_search_ext( ld, base, LDAP_SCOPE_SUBTREE, 456 filter, attrs, 0, NULL, NULL, 0, 0, &msgid ); 457 if ( rc != LDAP_SUCCESS ) { 458 tester_ldap_error( ld, "ldap_search_ext", NULL ); 459 exit( EXIT_FAILURE ); 460 } 461 462 while ( ( rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ONE, NULL, &res ) ) > 0 ) 463 { 464 BerElement *ber; 465 struct berval bv; 466 int done = 0; 467 468 for ( msg = ldap_first_message( ld, res ); msg; 469 msg = ldap_next_message( ld, msg ) ) 470 { 471 switch ( ldap_msgtype( msg ) ) { 472 case LDAP_RES_SEARCH_ENTRY: 473 rc = ldap_get_dn_ber( ld, msg, &ber, &bv ); 474 dns = realloc( dns, (ndns + 1)*sizeof(char *) ); 475 dns[ndns] = ber_strdup( bv.bv_val ); 476 if ( pwattr != NULL ) { 477 struct berval **values = ldap_get_values_len( ld, msg, pwattr ); 478 479 creds = realloc( creds, (ndns + 1)*sizeof(struct berval) ); 480 if ( values == NULL ) { 481 novals:; 482 creds[ndns].bv_len = 0; 483 creds[ndns].bv_val = nullstr; 484 485 } else { 486 static struct berval cleartext = BER_BVC( "{CLEARTEXT} " ); 487 struct berval value = *values[ 0 ]; 488 489 if ( value.bv_val[ 0 ] == '{' ) { 490 char *end = ber_bvchr( &value, '}' ); 491 492 if ( end ) { 493 if ( ber_bvcmp( &value, &cleartext ) == 0 ) { 494 value.bv_val += cleartext.bv_len; 495 value.bv_len -= cleartext.bv_len; 496 497 } else { 498 ldap_value_free_len( values ); 499 goto novals; 500 } 501 } 502 503 } 504 505 ber_dupbv( &creds[ndns], &value ); 506 ldap_value_free_len( values ); 507 } 508 } 509 ndns++; 510 ber_free( ber, 0 ); 511 break; 512 513 case LDAP_RES_SEARCH_RESULT: 514 done = 1; 515 break; 516 } 517 if ( done ) 518 break; 519 } 520 ldap_msgfree( res ); 521 if ( done ) break; 522 } 523 524 #ifdef _WIN32 525 beg = GetTickCount(); 526 #else 527 gettimeofday( &beg, NULL ); 528 #endif 529 530 if ( ndns == 0 ) { 531 tester_error( "No DNs" ); 532 return 1; 533 } 534 535 fprintf( stderr, " PID=%ld - Bind base=\"%s\" filter=\"%s\" got %d values.\n", 536 (long) pid, base, filter, ndns ); 537 538 /* Ok, got list of DNs, now start binding to each */ 539 for ( i = 0; i < maxloop; i++ ) { 540 int j; 541 struct berval cred = { 0, NULL }; 542 543 544 #if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */ 545 j = rand() % ndns; 546 #endif 547 j = ((double)ndns)*rand()/(RAND_MAX + 1.0); 548 549 if ( creds && !BER_BVISEMPTY( &creds[j] ) ) { 550 cred = creds[j]; 551 } 552 553 if ( do_bind( uri, dns[j], &cred, 1, force, chaserefs, noinit, &ld, 554 action_type, action ) && !force ) 555 { 556 break; 557 } 558 559 if ( delay ) { 560 sleep( delay ); 561 } 562 } 563 564 if ( ld != NULL ) { 565 ldap_unbind_ext( ld, NULL, NULL ); 566 ld = NULL; 567 } 568 569 #ifdef _WIN32 570 end = GetTickCount(); 571 end -= beg; 572 573 fprintf( stderr, " PID=%ld - Bind done %d in %d.%03d seconds.\n", 574 (long) pid, i, end / 1000, end % 1000 ); 575 #else 576 gettimeofday( &end, NULL ); 577 end.tv_usec -= beg.tv_usec; 578 if (end.tv_usec < 0 ) { 579 end.tv_usec += 1000000; 580 end.tv_sec -= 1; 581 } 582 end.tv_sec -= beg.tv_sec; 583 584 fprintf( stderr, " PID=%ld - Bind done %d in %ld.%06ld seconds.\n", 585 (long) pid, i, (long) end.tv_sec, (long) end.tv_usec ); 586 #endif 587 588 if ( dns ) { 589 for ( i = 0; i < ndns; i++ ) { 590 free( dns[i] ); 591 } 592 free( dns ); 593 } 594 595 if ( creds ) { 596 for ( i = 0; i < ndns; i++ ) { 597 if ( creds[i].bv_val != nullstr ) { 598 free( creds[i].bv_val ); 599 } 600 } 601 free( creds ); 602 } 603 604 return 0; 605 } 606