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 */
42 pid_t pid;
43 int debug;
44 
45 /* static vars */
46 static char progname[ BUFSIZ ];
47 tester_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
55 static int ignore_base        [ -TESTER_CLIENT_FIRST + TESTER_SERVER_LAST + 1 ];
56 #define    ignore_count       (ignore_base - TESTER_CLIENT_FIRST)
57 
58 static 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 
141 static int
tester_ignore_str2err(const char * err)142 tester_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 
179 int
tester_ignore_str2errlist(const char * err)180 tester_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 
195 int
tester_ignore_err(int err)196 tester_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 
211 struct tester_conn_args *
tester_init(const char * pname,tester_t ptype)212 tester_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 
231 void
tester_ldap_error(LDAP * ld,const char * fname,const char * msg)232 tester_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 
292 void
tester_perror(const char * fname,const char * msg)293 tester_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 
304 int
tester_config_opt(struct tester_conn_args * config,char opt,char * optarg)305 tester_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 
447 void
tester_config_finish(struct tester_conn_args * config)448 tester_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 
479 void
tester_init_ld(LDAP ** ldp,struct tester_conn_args * config,int flags)480 tester_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 
486 retry:;
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 
551 void
tester_error(const char * msg)552 tester_error( const char *msg )
553 {
554           fprintf( stderr, "%s: %s\n", progname, msg );
555 }
556