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