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