1/* $NetBSD: slapd-common.c,v 1.3 2021/08/14 16:15:03 christos Exp $ */ 2 3/* $OpenLDAP$ */ 4/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1999-2021 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-common.c,v 1.3 2021/08/14 16:15:03 christos Exp $"); 24 25#include "portable.h" 26 27#include <stdio.h> 28 29#include "ac/stdlib.h" 30#include "ac/unistd.h" 31#include "ac/string.h" 32#include "ac/errno.h" 33 34#include "ldap.h" 35 36#include "lutil.h" 37#include "lutil_ldap.h" 38#include "ldap_pvt.h" 39#include "slapd-common.h" 40 41/* global vars */ 42pid_t pid; 43int debug; 44 45/* static vars */ 46static char progname[ BUFSIZ ]; 47tester_t progtype; 48 49/* 50 * ignore_count[] is indexed by result code: 51 * negative for OpenLDAP client-side errors, positive for protocol codes. 52 */ 53#define TESTER_CLIENT_FIRST LDAP_REFERRAL_LIMIT_EXCEEDED /* negative */ 54#define TESTER_SERVER_LAST LDAP_OTHER 55static int ignore_base [ -TESTER_CLIENT_FIRST + TESTER_SERVER_LAST + 1 ]; 56#define ignore_count (ignore_base - TESTER_CLIENT_FIRST) 57 58static const struct { 59 const char *name; 60 int err; 61} ignore_str2err[] = { 62 { "OPERATIONS_ERROR", LDAP_OPERATIONS_ERROR }, 63 { "PROTOCOL_ERROR", LDAP_PROTOCOL_ERROR }, 64 { "TIMELIMIT_EXCEEDED", LDAP_TIMELIMIT_EXCEEDED }, 65 { "SIZELIMIT_EXCEEDED", LDAP_SIZELIMIT_EXCEEDED }, 66 { "COMPARE_FALSE", LDAP_COMPARE_FALSE }, 67 { "COMPARE_TRUE", LDAP_COMPARE_TRUE }, 68 { "AUTH_METHOD_NOT_SUPPORTED", LDAP_AUTH_METHOD_NOT_SUPPORTED }, 69 { "STRONG_AUTH_NOT_SUPPORTED", LDAP_STRONG_AUTH_NOT_SUPPORTED }, 70 { "STRONG_AUTH_REQUIRED", LDAP_STRONG_AUTH_REQUIRED }, 71 { "STRONGER_AUTH_REQUIRED", LDAP_STRONGER_AUTH_REQUIRED }, 72 { "PARTIAL_RESULTS", LDAP_PARTIAL_RESULTS }, 73 74 { "REFERRAL", LDAP_REFERRAL }, 75 { "ADMINLIMIT_EXCEEDED", LDAP_ADMINLIMIT_EXCEEDED }, 76 { "UNAVAILABLE_CRITICAL_EXTENSION", LDAP_UNAVAILABLE_CRITICAL_EXTENSION }, 77 { "CONFIDENTIALITY_REQUIRED", LDAP_CONFIDENTIALITY_REQUIRED }, 78 { "SASL_BIND_IN_PROGRESS", LDAP_SASL_BIND_IN_PROGRESS }, 79 80 { "NO_SUCH_ATTRIBUTE", LDAP_NO_SUCH_ATTRIBUTE }, 81 { "UNDEFINED_TYPE", LDAP_UNDEFINED_TYPE }, 82 { "INAPPROPRIATE_MATCHING", LDAP_INAPPROPRIATE_MATCHING }, 83 { "CONSTRAINT_VIOLATION", LDAP_CONSTRAINT_VIOLATION }, 84 { "TYPE_OR_VALUE_EXISTS", LDAP_TYPE_OR_VALUE_EXISTS }, 85 { "INVALID_SYNTAX", LDAP_INVALID_SYNTAX }, 86 87 { "NO_SUCH_OBJECT", LDAP_NO_SUCH_OBJECT }, 88 { "ALIAS_PROBLEM", LDAP_ALIAS_PROBLEM }, 89 { "INVALID_DN_SYNTAX", LDAP_INVALID_DN_SYNTAX }, 90 { "IS_LEAF", LDAP_IS_LEAF }, 91 { "ALIAS_DEREF_PROBLEM", LDAP_ALIAS_DEREF_PROBLEM }, 92 93 /* obsolete */ 94 { "PROXY_AUTHZ_FAILURE", LDAP_X_PROXY_AUTHZ_FAILURE }, 95 { "INAPPROPRIATE_AUTH", LDAP_INAPPROPRIATE_AUTH }, 96 { "INVALID_CREDENTIALS", LDAP_INVALID_CREDENTIALS }, 97 { "INSUFFICIENT_ACCESS", LDAP_INSUFFICIENT_ACCESS }, 98 99 { "BUSY", LDAP_BUSY }, 100 { "UNAVAILABLE", LDAP_UNAVAILABLE }, 101 { "UNWILLING_TO_PERFORM", LDAP_UNWILLING_TO_PERFORM }, 102 { "LOOP_DETECT", LDAP_LOOP_DETECT }, 103 104 { "NAMING_VIOLATION", LDAP_NAMING_VIOLATION }, 105 { "OBJECT_CLASS_VIOLATION", LDAP_OBJECT_CLASS_VIOLATION }, 106 { "NOT_ALLOWED_ON_NONLEAF", LDAP_NOT_ALLOWED_ON_NONLEAF }, 107 { "NOT_ALLOWED_ON_RDN", LDAP_NOT_ALLOWED_ON_RDN }, 108 { "ALREADY_EXISTS", LDAP_ALREADY_EXISTS }, 109 { "NO_OBJECT_CLASS_MODS", LDAP_NO_OBJECT_CLASS_MODS }, 110 { "RESULTS_TOO_LARGE", LDAP_RESULTS_TOO_LARGE }, 111 { "AFFECTS_MULTIPLE_DSAS", LDAP_AFFECTS_MULTIPLE_DSAS }, 112 113 { "OTHER", LDAP_OTHER }, 114 115 { "SERVER_DOWN", LDAP_SERVER_DOWN }, 116 { "LOCAL_ERROR", LDAP_LOCAL_ERROR }, 117 { "ENCODING_ERROR", LDAP_ENCODING_ERROR }, 118 { "DECODING_ERROR", LDAP_DECODING_ERROR }, 119 { "TIMEOUT", LDAP_TIMEOUT }, 120 { "AUTH_UNKNOWN", LDAP_AUTH_UNKNOWN }, 121 { "FILTER_ERROR", LDAP_FILTER_ERROR }, 122 { "USER_CANCELLED", LDAP_USER_CANCELLED }, 123 { "PARAM_ERROR", LDAP_PARAM_ERROR }, 124 { "NO_MEMORY", LDAP_NO_MEMORY }, 125 { "CONNECT_ERROR", LDAP_CONNECT_ERROR }, 126 { "NOT_SUPPORTED", LDAP_NOT_SUPPORTED }, 127 { "CONTROL_NOT_FOUND", LDAP_CONTROL_NOT_FOUND }, 128 { "NO_RESULTS_RETURNED", LDAP_NO_RESULTS_RETURNED }, 129 { "MORE_RESULTS_TO_RETURN", LDAP_MORE_RESULTS_TO_RETURN }, 130 { "CLIENT_LOOP", LDAP_CLIENT_LOOP }, 131 { "REFERRAL_LIMIT_EXCEEDED", LDAP_REFERRAL_LIMIT_EXCEEDED }, 132 133 { NULL } 134}; 135 136#define UNKNOWN_ERR (1234567890) 137 138#define RETRIES 0 139#define LOOPS 100 140 141static int 142tester_ignore_str2err( const char *err ) 143{ 144 int i, ignore = 1; 145 146 if ( strcmp( err, "ALL" ) == 0 ) { 147 for ( i = 0; ignore_str2err[ i ].name != NULL; i++ ) { 148 ignore_count[ ignore_str2err[ i ].err ] = 1; 149 } 150 ignore_count[ LDAP_SUCCESS ] = 0; 151 152 return 0; 153 } 154 155 if ( err[ 0 ] == '!' ) { 156 ignore = 0; 157 err++; 158 159 } else if ( err[ 0 ] == '*' ) { 160 ignore = -1; 161 err++; 162 } 163 164 for ( i = 0; ignore_str2err[ i ].name != NULL; i++ ) { 165 if ( strcmp( err, ignore_str2err[ i ].name ) == 0 ) { 166 int err = ignore_str2err[ i ].err; 167 168 if ( err != LDAP_SUCCESS ) { 169 ignore_count[ err ] = ignore; 170 } 171 172 return err; 173 } 174 } 175 176 return UNKNOWN_ERR; 177} 178 179int 180tester_ignore_str2errlist( const char *err ) 181{ 182 int i; 183 char **errs = ldap_str2charray( err, "," ); 184 185 for ( i = 0; errs[ i ] != NULL; i++ ) { 186 /* TODO: allow <err>:<prog> to ignore <err> only when <prog> */ 187 (void)tester_ignore_str2err( errs[ i ] ); 188 } 189 190 ldap_charray_free( errs ); 191 192 return 0; 193} 194 195int 196tester_ignore_err( int err ) 197{ 198 int rc = 1; 199 200 if ( err && TESTER_CLIENT_FIRST <= err && err <= TESTER_SERVER_LAST ) { 201 rc = ignore_count[ err ]; 202 if ( rc != 0 ) { 203 ignore_count[ err ] = rc + (rc > 0 ? 1 : -1); 204 } 205 } 206 207 /* SUCCESS is always "ignored" */ 208 return rc; 209} 210 211struct tester_conn_args * 212tester_init( const char *pname, tester_t ptype ) 213{ 214 static struct tester_conn_args config = { 215 .authmethod = -1, 216 .retries = RETRIES, 217 .loops = LOOPS, 218 .outerloops = 1, 219 220 .uri = NULL, 221 }; 222 223 pid = getpid(); 224 srand( pid ); 225 snprintf( progname, sizeof( progname ), "%s PID=%d", pname, pid ); 226 progtype = ptype; 227 228 return &config; 229} 230 231void 232tester_ldap_error( LDAP *ld, const char *fname, const char *msg ) 233{ 234 int err; 235 char *text = NULL; 236 LDAPControl **ctrls = NULL; 237 238 ldap_get_option( ld, LDAP_OPT_RESULT_CODE, (void *)&err ); 239 if ( err != LDAP_SUCCESS ) { 240 ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void *)&text ); 241 } 242 243 fprintf( stderr, "%s: %s: %s (%d) %s %s\n", 244 progname, fname, ldap_err2string( err ), err, 245 text == NULL ? "" : text, 246 msg ? msg : "" ); 247 248 if ( text ) { 249 ldap_memfree( text ); 250 text = NULL; 251 } 252 253 ldap_get_option( ld, LDAP_OPT_MATCHED_DN, (void *)&text ); 254 if ( text != NULL ) { 255 if ( text[ 0 ] != '\0' ) { 256 fprintf( stderr, "\tmatched: %s\n", text ); 257 } 258 ldap_memfree( text ); 259 text = NULL; 260 } 261 262 ldap_get_option( ld, LDAP_OPT_SERVER_CONTROLS, (void *)&ctrls ); 263 if ( ctrls != NULL ) { 264 int i; 265 266 fprintf( stderr, "\tcontrols:\n" ); 267 for ( i = 0; ctrls[ i ] != NULL; i++ ) { 268 fprintf( stderr, "\t\t%s\n", ctrls[ i ]->ldctl_oid ); 269 } 270 ldap_controls_free( ctrls ); 271 ctrls = NULL; 272 } 273 274 if ( err == LDAP_REFERRAL ) { 275 char **refs = NULL; 276 277 ldap_get_option( ld, LDAP_OPT_REFERRAL_URLS, (void *)&refs ); 278 279 if ( refs ) { 280 int i; 281 282 fprintf( stderr, "\treferral:\n" ); 283 for ( i = 0; refs[ i ] != NULL; i++ ) { 284 fprintf( stderr, "\t\t%s\n", refs[ i ] ); 285 } 286 287 ber_memvfree( (void **)refs ); 288 } 289 } 290} 291 292void 293tester_perror( const char *fname, const char *msg ) 294{ 295 int save_errno = errno; 296 char buf[ BUFSIZ ]; 297 298 fprintf( stderr, "%s: %s: (%d) %s %s\n", 299 progname, fname, save_errno, 300 AC_STRERROR_R( save_errno, buf, sizeof( buf ) ), 301 msg ? msg : "" ); 302} 303 304int 305tester_config_opt( struct tester_conn_args *config, char opt, char *optarg ) 306{ 307 switch ( opt ) { 308 case 'C': 309 config->chaserefs++; 310 break; 311 312 case 'D': 313 config->binddn = optarg; 314 break; 315 316 case 'd': 317 { 318 if ( lutil_atoi( &debug, optarg ) != 0 ) { 319 return -1; 320 } 321 322 if ( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug ) 323 != LBER_OPT_SUCCESS ) 324 { 325 fprintf( stderr, 326 "Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug ); 327 } 328 329 if ( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug ) 330 != LDAP_OPT_SUCCESS ) 331 { 332 fprintf( stderr, 333 "Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug ); 334 } 335 break; 336 } 337 338 case 'H': 339 config->uri = optarg; 340 break; 341 342 case 'i': 343 tester_ignore_str2errlist( optarg ); 344 break; 345 346 case 'L': 347 if ( lutil_atoi( &config->outerloops, optarg ) != 0 ) { 348 return -1; 349 } 350 break; 351 352 case 'l': 353 if ( lutil_atoi( &config->loops, optarg ) != 0 ) { 354 return -1; 355 } 356 break; 357 358#ifdef HAVE_CYRUS_SASL 359 case 'O': 360 if ( config->secprops != NULL ) { 361 return -1; 362 } 363 if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) { 364 return -1; 365 } 366 config->authmethod = LDAP_AUTH_SASL; 367 config->secprops = optarg; 368 break; 369 370 case 'R': 371 if ( config->realm != NULL ) { 372 return -1; 373 } 374 if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) { 375 return -1; 376 } 377 config->authmethod = LDAP_AUTH_SASL; 378 config->realm = optarg; 379 break; 380 381 case 'U': 382 if ( config->authc_id != NULL ) { 383 return -1; 384 } 385 if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) { 386 return -1; 387 } 388 config->authmethod = LDAP_AUTH_SASL; 389 config->authc_id = optarg; 390 break; 391 392 case 'X': 393 if ( config->authz_id != NULL ) { 394 return -1; 395 } 396 if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) { 397 return -1; 398 } 399 config->authmethod = LDAP_AUTH_SASL; 400 config->authz_id = optarg; 401 break; 402 403 case 'Y': 404 if ( config->mech != NULL ) { 405 return -1; 406 } 407 if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) { 408 return -1; 409 } 410 config->authmethod = LDAP_AUTH_SASL; 411 config->mech = optarg; 412 break; 413#endif 414 415 case 'r': 416 if ( lutil_atoi( &config->retries, optarg ) != 0 ) { 417 return -1; 418 } 419 break; 420 421 case 't': 422 if ( lutil_atoi( &config->delay, optarg ) != 0 ) { 423 return -1; 424 } 425 break; 426 427 case 'w': 428 config->pass.bv_val = strdup( optarg ); 429 config->pass.bv_len = strlen( optarg ); 430 memset( optarg, '*', config->pass.bv_len ); 431 break; 432 433 case 'x': 434 if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SIMPLE ) { 435 return -1; 436 } 437 config->authmethod = LDAP_AUTH_SIMPLE; 438 break; 439 440 default: 441 return -1; 442 } 443 444 return LDAP_SUCCESS; 445} 446 447void 448tester_config_finish( struct tester_conn_args *config ) 449{ 450 if ( config->authmethod == -1 ) { 451#ifdef HAVE_CYRUS_SASL 452 if ( config->binddn != NULL ) { 453 config->authmethod = LDAP_AUTH_SIMPLE; 454 } else { 455 config->authmethod = LDAP_AUTH_SASL; 456 } 457#else 458 config->authmethod = LDAP_AUTH_SIMPLE; 459#endif 460 } 461 462#ifdef HAVE_CYRUS_SASL 463 if ( config->authmethod == LDAP_AUTH_SASL ) { 464 config->defaults = lutil_sasl_defaults( NULL, 465 config->mech, 466 config->realm, 467 config->authc_id, 468 config->pass.bv_val, 469 config->authz_id ); 470 471 if ( config->defaults == NULL ) { 472 tester_error( "unable to prepare SASL defaults" ); 473 exit( EXIT_FAILURE ); 474 } 475 } 476#endif 477} 478 479void 480tester_init_ld( LDAP **ldp, struct tester_conn_args *config, int flags ) 481{ 482 LDAP *ld; 483 int rc, do_retry = config->retries; 484 int version = LDAP_VERSION3; 485 486retry:; 487 ldap_initialize( &ld, config->uri ); 488 if ( ld == NULL ) { 489 tester_perror( "ldap_initialize", NULL ); 490 exit( EXIT_FAILURE ); 491 } 492 493 (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ); 494 (void) ldap_set_option( ld, LDAP_OPT_REFERRALS, 495 config->chaserefs ? LDAP_OPT_ON: LDAP_OPT_OFF ); 496 497 if ( !( flags & TESTER_INIT_ONLY ) ) { 498 if ( config->authmethod == LDAP_AUTH_SASL ) { 499#ifdef HAVE_CYRUS_SASL 500 if ( config->secprops != NULL ) { 501 rc = ldap_set_option( ld, 502 LDAP_OPT_X_SASL_SECPROPS, config->secprops ); 503 504 if ( rc != LDAP_OPT_SUCCESS ) { 505 tester_ldap_error( ld, "ldap_set_option(SECPROPS)", NULL ); 506 ldap_unbind_ext( ld, NULL, NULL ); 507 exit( EXIT_FAILURE ); 508 } 509 } 510 511 rc = ldap_sasl_interactive_bind_s( ld, 512 config->binddn, 513 config->mech, 514 NULL, NULL, 515 LDAP_SASL_QUIET, 516 lutil_sasl_interact, 517 config->defaults ); 518#else /* HAVE_CYRUS_SASL */ 519 /* caller shouldn't have allowed this */ 520 assert(0); 521#endif 522 } else if ( config->authmethod == LDAP_AUTH_SIMPLE ) { 523 rc = ldap_sasl_bind_s( ld, 524 config->binddn, LDAP_SASL_SIMPLE, 525 &config->pass, NULL, NULL, NULL ); 526 } 527 528 if ( rc != LDAP_SUCCESS ) { 529 tester_ldap_error( ld, "ldap_sasl_bind_s", NULL ); 530 switch ( rc ) { 531 case LDAP_BUSY: 532 case LDAP_UNAVAILABLE: 533 if ( do_retry > 0 ) { 534 do_retry--; 535 if ( config->delay > 0 ) { 536 sleep( config->delay ); 537 } 538 goto retry; 539 } 540 } 541 ldap_unbind_ext( ld, NULL, NULL ); 542 ld = NULL; 543 if ( !( flags & TESTER_INIT_NOEXIT )) 544 exit( EXIT_FAILURE ); 545 } 546 } 547 548 *ldp = ld; 549} 550 551void 552tester_error( const char *msg ) 553{ 554 fprintf( stderr, "%s: %s\n", progname, msg ); 555} 556