]> git.sur5r.net Git - openldap/blob - tests/progs/slapd-bind.c
1b94e2fcaf87de32c05d20a424bea301a5511931
[openldap] / tests / progs / slapd-bind.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2017 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 "lutil_ldap.h"
38 #include "lber_pvt.h"
39 #include "ldap_pvt.h"
40
41 #include "slapd-common.h"
42
43 static int
44 do_bind( struct tester_conn_args *config, char *dn, int maxloop,
45         int force, int noinit, LDAP **ldp, int action_type, void *action );
46
47 static int
48 do_base( struct tester_conn_args *config, char *dn, char *base, char *filter, char *pwattr,
49         int force, int noinit, int action_type, void *action );
50
51 /* This program can be invoked two ways: if -D is used to specify a Bind DN,
52  * that DN will be used repeatedly for all of the Binds. If instead -b is used
53  * to specify a base DN, a search will be done for all "person" objects under
54  * that base DN. Then DNs from this list will be randomly selected for each
55  * Bind request. All of the users must have identical passwords. Also it is
56  * assumed that the users are all onelevel children of the base.
57  */
58 static void
59 usage( char *name, char opt )
60 {
61         if ( opt ) {
62                 fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
63                         name, opt );
64         }
65
66         fprintf( stderr, "usage: %s " TESTER_COMMON_HELP
67                 "[-b <baseDN> [-f <searchfilter>] [-a pwattr]] "
68                 "[-B <extra>[,...]] "
69                 "[-F] "
70                 "[-I]\n",
71                 name );
72         exit( EXIT_FAILURE );
73 }
74
75 int
76 main( int argc, char **argv )
77 {
78         int             i;
79         char            *base = NULL;
80         char            *filter = "(objectClass=person)";
81         char            *pwattr = NULL;
82         int             force = 0;
83         int             noinit = 1;
84         struct tester_conn_args *config;
85
86         /* extra action to do after bind... */
87         struct berval   type[] = {
88                 BER_BVC( "tester=" ),
89                 BER_BVC( "add=" ),
90                 BER_BVC( "bind=" ),
91                 BER_BVC( "modify=" ),
92                 BER_BVC( "modrdn=" ),
93                 BER_BVC( "read=" ),
94                 BER_BVC( "search=" ),
95                 BER_BVNULL
96         };
97
98         LDAPURLDesc     *extra_ludp = NULL;
99
100         config = tester_init( "slapd-bind", TESTER_BIND );
101
102         /* by default, tolerate invalid credentials */
103         tester_ignore_str2errlist( "INVALID_CREDENTIALS" );
104
105         while ( ( i = getopt( argc, argv, TESTER_COMMON_OPTS "a:B:b:Ff:I" ) ) != EOF )
106         {
107                 switch ( i ) {
108                 case 'a':
109                         pwattr = optarg;
110                         break;
111
112                 case 'b':               /* base DN of a tree of user DNs */
113                         base = optarg;
114                         break;
115
116                 case 'B':
117                         {
118                         int     c;
119
120                         for ( c = 0; type[c].bv_val; c++ ) {
121                                 if ( strncasecmp( optarg, type[c].bv_val, type[c].bv_len ) == 0 )
122                                 {
123                                         break;
124                                 }
125                         }
126
127                         if ( type[c].bv_val == NULL ) {
128                                 usage( argv[0], 'B' );
129                         }
130
131                         switch ( c ) {
132                         case TESTER_TESTER:
133                         case TESTER_BIND:
134                                 /* invalid */
135                                 usage( argv[0], 'B' );
136
137                         case TESTER_SEARCH:
138                                 {
139                                 if ( ldap_url_parse( &optarg[type[c].bv_len], &extra_ludp ) != LDAP_URL_SUCCESS )
140                                 {
141                                         usage( argv[0], 'B' );
142                                 }
143                                 } break;
144
145                         case TESTER_ADDEL:
146                         case TESTER_MODIFY:
147                         case TESTER_MODRDN:
148                         case TESTER_READ:
149                                 /* nothing to do */
150                                 break;
151
152                         default:
153                                 assert( 0 );
154                         }
155
156                         } break;
157
158                 case 'f':
159                         filter = optarg;
160                         break;
161
162                 case 'F':
163                         force++;
164                         break;
165
166                 case 'I':
167                         /* reuse connection */
168                         noinit = 0;
169                         break;
170
171                 default:
172                         if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) {
173                                 break;
174                         }
175                         usage( argv[0], i );
176                         break;
177                 }
178         }
179
180         tester_config_finish( config );
181
182         for ( i = 0; i < config->outerloops; i++ ) {
183                 int rc;
184
185                 if ( base != NULL ) {
186                         rc = do_base( config, config->binddn, base,
187                                 filter, pwattr, force, noinit, -1, NULL );
188                 } else {
189                         rc = do_bind( config, config->binddn,
190                                 config->loops, force, noinit, NULL, -1, NULL );
191                 }
192                 if ( rc == LDAP_SERVER_DOWN )
193                         break;
194         }
195
196         exit( EXIT_SUCCESS );
197 }
198
199
200 static int
201 do_bind( struct tester_conn_args *config, char *dn, int maxloop,
202         int force, int noinit, LDAP **ldp, int action_type, void *action )
203 {
204         LDAP    *ld = ldp ? *ldp : NULL;
205         char    *bindfunc = "ldap_sasl_bind_s";
206         int     i, rc = -1;
207
208         /* for internal search */
209         int     timelimit = 0;
210         int     sizelimit = 0;
211
212         switch ( action_type ) {
213         case -1:
214                 break;
215
216         case TESTER_SEARCH:
217                 {
218                 LDAPURLDesc     *ludp = (LDAPURLDesc *)action;
219
220                 assert( action != NULL );
221
222                 if ( ludp->lud_exts != NULL ) {
223                         for ( i = 0; ludp->lud_exts[ i ] != NULL; i++ ) {
224                                 char    *ext = ludp->lud_exts[ i ];
225                                 int     crit = 0;
226
227                                 if (ext[0] == '!') {
228                                         crit++;
229                                         ext++;
230                                 }
231
232                                 if ( strncasecmp( ext, "x-timelimit=", STRLENOF( "x-timelimit=" ) ) == 0 ) {
233                                         if ( lutil_atoi( &timelimit, &ext[ STRLENOF( "x-timelimit=" ) ] ) && crit ) {
234                                                 tester_error( "unable to parse critical extension x-timelimit" );
235                                         }
236
237                                 } else if ( strncasecmp( ext, "x-sizelimit=", STRLENOF( "x-sizelimit=" ) ) == 0 ) {
238                                         if ( lutil_atoi( &sizelimit, &ext[ STRLENOF( "x-sizelimit=" ) ] ) && crit ) {
239                                                 tester_error( "unable to parse critical extension x-sizelimit" );
240                                         }
241
242                                 } else if ( crit ) {
243                                         tester_error( "unknown critical extension" );
244                                 }
245                         }
246                 }
247                 } break;
248
249         default:
250                 /* nothing to do yet */
251                 break;
252         }
253                         
254         if ( maxloop > 1 ) {
255                 fprintf( stderr, "PID=%ld - Bind(%d): dn=\"%s\".\n",
256                          (long) pid, maxloop, dn );
257         }
258
259         for ( i = 0; i < maxloop; i++ ) {
260                 if ( !noinit || ld == NULL ) {
261                         tester_init_ld( &ld, config, TESTER_INIT_ONLY );
262
263 #ifdef HAVE_CYRUS_SASL
264                         if ( config->secprops != NULL ) {
265                                 rc = ldap_set_option( ld,
266                                                 LDAP_OPT_X_SASL_SECPROPS, config->secprops );
267
268                                 if( rc != LDAP_OPT_SUCCESS ) {
269                                         tester_ldap_error( ld, "ldap_set_option(SECPROPS)", NULL );
270                                         exit( EXIT_FAILURE );
271                                 }
272                         }
273 #endif
274                 }
275
276                 if ( config->authmethod == LDAP_AUTH_SASL ) {
277 #ifdef HAVE_CYRUS_SASL
278                         bindfunc = "ldap_sasl_interactive_bind_s";
279                         rc = ldap_sasl_interactive_bind_s( ld,
280                                         config->binddn,
281                                         config->mech,
282                                         NULL, NULL,
283                                         LDAP_SASL_QUIET,
284                                         lutil_sasl_interact,
285                                         config->defaults );
286 #else /* HAVE_CYRUS_SASL */
287                         /* caller shouldn't have allowed this */
288                         assert(0);
289 #endif
290                 } else if ( config->authmethod == LDAP_AUTH_SIMPLE ) {
291                         bindfunc = "ldap_sasl_bind_s";
292                         rc = ldap_sasl_bind_s( ld,
293                                         config->binddn, LDAP_SASL_SIMPLE,
294                                         &config->pass, NULL, NULL, NULL );
295                 }
296
297                 if ( rc ) {
298                         int first = tester_ignore_err( rc );
299
300                         /* if ignore.. */
301                         if ( first ) {
302                                 /* only log if first occurrence */
303                                 if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
304                                         tester_ldap_error( ld, bindfunc, NULL );
305                                 }
306                                 rc = LDAP_SUCCESS;
307
308                         } else {
309                                 tester_ldap_error( ld, bindfunc, NULL );
310                         }
311                 }
312
313                 switch ( action_type ) {
314                 case -1:
315                         break;
316
317                 case TESTER_SEARCH:
318                         {
319                         LDAPURLDesc     *ludp = (LDAPURLDesc *)action;
320                         LDAPMessage     *res = NULL;
321                         struct timeval  tv = { 0 }, *tvp = NULL;
322
323                         if ( timelimit ) {
324                                 tv.tv_sec = timelimit;
325                                 tvp = &tv;
326                         }
327
328                         assert( action != NULL );
329
330                         rc = ldap_search_ext_s( ld,
331                                 ludp->lud_dn, ludp->lud_scope,
332                                 ludp->lud_filter, ludp->lud_attrs, 0,
333                                 NULL, NULL, tvp, sizelimit, &res );
334                         ldap_msgfree( res );
335                         } break;
336
337                 default:
338                         /* nothing to do yet */
339                         break;
340                 }
341                         
342                 if ( !noinit ) {
343                         ldap_unbind_ext( ld, NULL, NULL );
344                         ld = NULL;
345                 }
346
347                 if ( rc != LDAP_SUCCESS ) {
348                         break;
349                 }
350         }
351
352         if ( maxloop > 1 ) {
353                 fprintf( stderr, "  PID=%ld - Bind done (%d).\n", (long) pid, rc );
354         }
355
356         if ( ldp && noinit ) {
357                 *ldp = ld;
358
359         } else if ( ld != NULL ) {
360                 ldap_unbind_ext( ld, NULL, NULL );
361         }
362
363         return rc;
364 }
365
366
367 static int
368 do_base( struct tester_conn_args *config, char *dn, char *base, char *filter, char *pwattr,
369         int force, int noinit, int action_type, void *action )
370 {
371         LDAP    *ld = NULL;
372         int     i = 0;
373         int     rc = LDAP_SUCCESS;
374         ber_int_t msgid;
375         LDAPMessage *res, *msg;
376         char **dns = NULL;
377         struct berval *creds = NULL;
378         char *attrs[] = { LDAP_NO_ATTRS, NULL };
379         int ndns = 0;
380 #ifdef _WIN32
381         DWORD beg, end;
382 #else
383         struct timeval beg, end;
384 #endif
385         char *nullstr = "";
386
387         tester_init_ld( &ld, config, 0 );
388
389         fprintf( stderr, "PID=%ld - Bind(%d): base=\"%s\", filter=\"%s\" attr=\"%s\".\n",
390                         (long) pid, config->loops, base, filter, pwattr );
391
392         if ( pwattr != NULL ) {
393                 attrs[ 0 ] = pwattr;
394         }
395         rc = ldap_search_ext( ld, base, LDAP_SCOPE_SUBTREE,
396                         filter, attrs, 0, NULL, NULL, 0, 0, &msgid );
397         if ( rc != LDAP_SUCCESS ) {
398                 tester_ldap_error( ld, "ldap_search_ext", NULL );
399                 exit( EXIT_FAILURE );
400         }
401
402         while ( ( rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ONE, NULL, &res ) ) > 0 )
403         {
404                 BerElement *ber;
405                 struct berval bv;
406                 int done = 0;
407
408                 for ( msg = ldap_first_message( ld, res ); msg;
409                         msg = ldap_next_message( ld, msg ) )
410                 {
411                         switch ( ldap_msgtype( msg ) ) {
412                         case LDAP_RES_SEARCH_ENTRY:
413                                 rc = ldap_get_dn_ber( ld, msg, &ber, &bv );
414                                 dns = realloc( dns, (ndns + 1)*sizeof(char *) );
415                                 dns[ndns] = ber_strdup( bv.bv_val );
416                                 if ( pwattr != NULL ) {
417                                         struct berval   **values = ldap_get_values_len( ld, msg, pwattr );
418
419                                         creds = realloc( creds, (ndns + 1)*sizeof(struct berval) );
420                                         if ( values == NULL ) {
421 novals:;
422                                                 creds[ndns].bv_len = 0;
423                                                 creds[ndns].bv_val = nullstr;
424
425                                         } else {
426                                                 static struct berval    cleartext = BER_BVC( "{CLEARTEXT} " );
427                                                 struct berval           value = *values[ 0 ];
428
429                                                 if ( value.bv_val[ 0 ] == '{' ) {
430                                                         char *end = ber_bvchr( &value, '}' );
431
432                                                         if ( end ) {
433                                                                 if ( ber_bvcmp( &value, &cleartext ) == 0 ) {
434                                                                         value.bv_val += cleartext.bv_len;
435                                                                         value.bv_len -= cleartext.bv_len;
436
437                                                                 } else {
438                                                                         ldap_value_free_len( values );
439                                                                         goto novals;
440                                                                 }
441                                                         }
442
443                                                 }
444
445                                                 ber_dupbv( &creds[ndns], &value );
446                                                 ldap_value_free_len( values );
447                                         }
448                                 }
449                                 ndns++;
450                                 ber_free( ber, 0 );
451                                 break;
452
453                         case LDAP_RES_SEARCH_RESULT:
454                                 done = 1;
455                                 break;
456                         }
457                         if ( done )
458                                 break;
459                 }
460                 ldap_msgfree( res );
461                 if ( done ) break;
462         }
463
464 #ifdef _WIN32
465         beg = GetTickCount();
466 #else
467         gettimeofday( &beg, NULL );
468 #endif
469
470         if ( ndns == 0 ) {
471                 tester_error( "No DNs" );
472                 return 1;
473         }
474
475         fprintf( stderr, "  PID=%ld - Bind base=\"%s\" filter=\"%s\" got %d values.\n",
476                 (long) pid, base, filter, ndns );
477
478         /* Ok, got list of DNs, now start binding to each */
479         for ( i = 0; i < config->loops; i++ ) {
480                 int             j;
481
482 #if 0   /* use high-order bits for better randomness (Numerical Recipes in "C") */
483                 j = rand() % ndns;
484 #endif
485                 j = ((double)ndns)*rand()/(RAND_MAX + 1.0);
486
487                 if ( creds && !BER_BVISEMPTY( &creds[j] ) ) {
488                         config->pass = creds[j];
489                 }
490
491                 if ( do_bind( config, dns[j], 1, force, noinit, &ld,
492                         action_type, action ) && !force )
493                 {
494                         break;
495                 }
496
497                 if ( config->delay ) {
498                         sleep( config->delay );
499                 }
500         }
501
502         if ( ld != NULL ) {
503                 ldap_unbind_ext( ld, NULL, NULL );
504                 ld = NULL;
505         }
506
507 #ifdef _WIN32
508         end = GetTickCount();
509         end -= beg;
510
511         fprintf( stderr, "  PID=%ld - Bind done %d in %d.%03d seconds.\n",
512                 (long) pid, i, end / 1000, end % 1000 );
513 #else
514         gettimeofday( &end, NULL );
515         end.tv_usec -= beg.tv_usec;
516         if (end.tv_usec < 0 ) {
517                 end.tv_usec += 1000000;
518                 end.tv_sec -= 1;
519         }
520         end.tv_sec -= beg.tv_sec;
521
522         fprintf( stderr, "  PID=%ld - Bind done %d in %ld.%06ld seconds.\n",
523                 (long) pid, i, (long) end.tv_sec, (long) end.tv_usec );
524 #endif
525
526         if ( dns ) {
527                 for ( i = 0; i < ndns; i++ ) {
528                         ber_memfree( dns[i] );
529                 }
530                 free( dns );
531         }
532
533         if ( creds ) {
534                 for ( i = 0; i < ndns; i++ ) {
535                         if ( creds[i].bv_val != nullstr ) {
536                                 ber_memfree( creds[i].bv_val );
537                         }
538                 }
539                 free( creds );
540         }
541
542         return 0;
543 }