common.c 58.7 KB
Newer Older
1
/* common.c - common routines for the ldap client tools */
2
/* $OpenLDAP$ */
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
5
 * Copyright 1998-2020 The OpenLDAP Foundation.
6
7
8
9
10
11
12
13
14
15
16
17
 * Portions Copyright 2003 Kurt D. Zeilenga.
 * Portions Copyright 2003 IBM Corporation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
18
19
/* ACKNOWLEDGEMENTS:
 * This file was initially created by Hallvard B. Furuseth based (in
20
21
22
 * part) upon argument parsing code for individual tools located in
 * this directory.   Additional contributors include:
 *   Kurt D. Zeilenga (additional common argument and control support)
23
24
25
26
27
28
29
30
31
 */

#include "portable.h"

#include <stdio.h>

#include <ac/stdlib.h>
#include <ac/signal.h>
#include <ac/string.h>
32
#include <ac/ctype.h>
33
34
#include <ac/unistd.h>
#include <ac/errno.h>
Howard Chu's avatar
Howard Chu committed
35
#include <ac/time.h>
36
#include <ac/socket.h>
37

38
#ifdef HAVE_CYRUS_SASL
Pierangelo Masarati's avatar
Pierangelo Masarati committed
39
40
41
42
43
#ifdef HAVE_SASL_SASL_H
#include <sasl/sasl.h>
#else
#include <sasl.h>
#endif
44
#endif
Pierangelo Masarati's avatar
Pierangelo Masarati committed
45

46
47
#include <ldap.h>

48
49
#include "ldif.h"
#include "lutil.h"
50
#include "lutil_ldap.h"
51
#include "ldap_defaults.h"
52
#include "ldap_pvt.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
53
#include "lber_pvt.h"
54
55
56

#include "common.h"

57
58
59
60
61
62
63
64
/* input-related vars */

/* misc. parameters */
tool_type_t	tool_type;
int		contoper = 0;
int		debug = 0;
char		*infile = NULL;
int		dont = 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
65
int		nocanon = 0;
66
67
68
int		referrals = 0;
int		verbose = 0;
int		ldif = 0;
Howard Chu's avatar
Howard Chu committed
69
ber_len_t	ldif_wrap = 0;
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
char		*prog = NULL;

/* connection */
char		*ldapuri = NULL;
char		*ldaphost = NULL;
int  		ldapport = 0;
int		use_tls = 0;
int		protocol = -1;
int		version = 0;

/* authc/authz */
int		authmethod = -1;
char		*binddn = NULL;
int		want_bindpw = 0;
struct berval	passwd = { 0, NULL };
char		*pw_file = NULL;
86
#ifdef HAVE_CYRUS_SASL
87
88
89
90
91
92
unsigned	sasl_flags = LDAP_SASL_AUTOMATIC;
char		*sasl_realm = NULL;
char		*sasl_authc_id = NULL;
char		*sasl_authz_id = NULL;
char		*sasl_mech = NULL;
char		*sasl_secprops = NULL;
93
94
#endif

95
96
97
/* controls */
int		assertctl;
char		*assertion = NULL;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
98
struct berval	assertionvalue = BER_BVNULL;
99
char		*authzid = NULL;
100
101
102
103
104
/* support deprecated early version of proxyAuthz */
#define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ	"2.16.840.1.113730.3.4.12"
#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
char		*proxydn = NULL;
#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
105
106
107
108
109
110
111
112
113
114
int		manageDIT = 0;
int		manageDSAit = 0;
int		noop = 0;
int		ppolicy = 0;
int		preread = 0;
static char	*preread_attrs = NULL;
int		postread = 0;
static char	*postread_attrs = NULL;
ber_int_t	pr_morePagedResults = 1;
struct berval	pr_cookie = { 0, NULL };
115
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
116
117
118
int		chaining = 0;
static int	chainingResolve = -1;
static int	chainingContinuation = -1;
119
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
120
121
122
123
#ifdef LDAP_CONTROL_X_SESSION_TRACKING
static int	sessionTracking = 0;
struct berval	stValue;
#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
124
125
126
ber_int_t vlvPos;
ber_int_t vlvCount;
struct berval *vlvContext;
127
128
129

LDAPControl	*unknown_ctrls = NULL;
int		unknown_ctrls_num = 0;
130

131
132
133
/* options */
struct timeval	nettimeout = { -1 , 0 };

134
135
136
137
138
139
140
141
typedef int (*print_ctrl_fn)( LDAP *ld, LDAPControl *ctrl );

static int print_preread( LDAP *ld, LDAPControl *ctrl );
static int print_postread( LDAP *ld, LDAPControl *ctrl );
static int print_paged_results( LDAP *ld, LDAPControl *ctrl );
#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
static int print_ppolicy( LDAP *ld, LDAPControl *ctrl );
#endif
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
142
static int print_sss( LDAP *ld, LDAPControl *ctrl );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
143
static int print_vlv( LDAP *ld, LDAPControl *ctrl );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
144
145
146
#ifdef LDAP_CONTROL_X_DEREF
static int print_deref( LDAP *ld, LDAPControl *ctrl );
#endif
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
147
148
149
#ifdef LDAP_CONTROL_X_WHATFAILED
static int print_whatfailed( LDAP *ld, LDAPControl *ctrl );
#endif
150
151
152
153
#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED
static int print_netscape_pwexpired( LDAP *ld, LDAPControl *ctrl );
static int print_netscape_pwexpiring( LDAP *ld, LDAPControl *ctrl );
#endif
154
155
156
157
158
159
160
161
162
163
164
165

static struct tool_ctrls_t {
	const char	*oid;
	unsigned	mask;
	print_ctrl_fn	func;
} tool_ctrl_response[] = {
	{ LDAP_CONTROL_PRE_READ,			TOOL_ALL,	print_preread },
	{ LDAP_CONTROL_POST_READ,			TOOL_ALL,	print_postread },
	{ LDAP_CONTROL_PAGEDRESULTS,			TOOL_SEARCH,	print_paged_results },
#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
	{ LDAP_CONTROL_PASSWORDPOLICYRESPONSE,		TOOL_ALL,	print_ppolicy },
#endif
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
166
	{ LDAP_CONTROL_SORTRESPONSE,	TOOL_SEARCH,	print_sss },
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
167
	{ LDAP_CONTROL_VLVRESPONSE,		TOOL_SEARCH,	print_vlv },
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
168
169
#ifdef LDAP_CONTROL_X_DEREF
	{ LDAP_CONTROL_X_DEREF,				TOOL_SEARCH,	print_deref },
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
170
171
172
#endif
#ifdef LDAP_CONTROL_X_WHATFAILED
	{ LDAP_CONTROL_X_WHATFAILED,			TOOL_ALL,	print_whatfailed },
173
174
175
176
#endif
#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED
	{ LDAP_CONTROL_X_PASSWORD_EXPIRED,		TOOL_ALL,	print_netscape_pwexpired },
	{ LDAP_CONTROL_X_PASSWORD_EXPIRING,		TOOL_ALL,	print_netscape_pwexpiring },
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
177
#endif
178
179
180
181
	{ NULL,						0,		NULL }
};

/* "features" */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
182
183
enum { Intr_None = 0, Intr_Abandon, Intr_Cancel, Intr_Ignore }; 
static volatile sig_atomic_t	gotintr, abcan;
184

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231

#ifdef LDAP_CONTROL_X_SESSION_TRACKING
static int
st_value( LDAP *ld, struct berval *value )
{
	char		*ip = NULL, *name = NULL;
	struct berval	id = { 0 };
	char		namebuf[ MAXHOSTNAMELEN ];

	if ( gethostname( namebuf, sizeof( namebuf ) ) == 0 ) {
		struct hostent	*h;
		struct in_addr	addr;

		name = namebuf;

		h = gethostbyname( name );
		if ( h != NULL ) {
			AC_MEMCPY( &addr, h->h_addr, sizeof( addr ) );
			ip = inet_ntoa( addr );
		}
	}

#ifdef HAVE_CYRUS_SASL
	if ( sasl_authz_id != NULL ) {
		ber_str2bv( sasl_authz_id, 0, 0, &id );

	} else if ( sasl_authc_id != NULL ) {
		ber_str2bv( sasl_authc_id, 0, 0, &id );

	} else 
#endif /* HAVE_CYRUS_SASL */
	if ( binddn != NULL ) {
		ber_str2bv( binddn, 0, 0, &id );
	}

	if ( ldap_create_session_tracking_value( ld,
		ip, name, LDAP_CONTROL_X_SESSION_TRACKING_USERNAME,
		&id, &stValue ) )
	{
		fprintf( stderr, _("Session tracking control encoding error!\n") );
		return -1;
	}

	return 0;
}
#endif /* LDAP_CONTROL_X_SESSION_TRACKING */

232
233
234
235
236
237
RETSIGTYPE
do_sig( int sig )
{
	gotintr = abcan;
}

238
void
239
tool_init( tool_type_t type )
240
{
241
	tool_type = type;
242
243
244
	ldap_pvt_setlocale(LC_MESSAGES, "");
	ldap_pvt_bindtextdomain(OPENLDAP_PACKAGE, LDAP_LOCALEDIR);
	ldap_pvt_textdomain(OPENLDAP_PACKAGE);
245
246
}

247
248
249
void
tool_destroy( void )
{
250
251
252
253
	static int destroyed;
	if ( destroyed++ )
		return;

254
255
256
257
258
259
#ifdef HAVE_CYRUS_SASL
	sasl_done();
#endif
#ifdef HAVE_TLS
	ldap_pvt_tls_destroy();
#endif
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
260
261
262
263
264
265
266
267

	if ( ldapuri != NULL ) {
		ber_memfree( ldapuri );
		ldapuri = NULL;
	}

	if ( pr_cookie.bv_val != NULL ) {
		ber_memfree( pr_cookie.bv_val );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
268
		BER_BVZERO( &pr_cookie );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
269
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
270
271
272

	if ( binddn != NULL ) {
		ber_memfree( binddn );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
273
		binddn = NULL;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
274
275
276
277
	}

	if ( passwd.bv_val != NULL ) {
		ber_memfree( passwd.bv_val );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
278
		BER_BVZERO( &passwd );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
279
	}
280
281
282

	if ( infile != NULL ) {
		ber_memfree( infile );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
		infile = NULL;
	}

	if ( assertion ) {
		ber_memfree( assertion );
		assertion = NULL;
	}

	if ( authzid ) {
		ber_memfree( authzid );
		authzid = NULL;
	}

	if ( proxydn ) {
		ber_memfree( proxydn );
		proxydn = NULL;
	}

	if ( preread_attrs ) {
		ber_memfree( preread_attrs );
		preread_attrs = NULL;
	}

	if ( postread_attrs ) {
		ber_memfree( postread_attrs );
		postread_attrs = NULL;
309
	}
310
311
}

312
313
314
315
void
tool_common_usage( void )
{
	static const char *const descriptions[] = {
316
317
N_("  -d level   set LDAP debugging level to `level'\n"),
N_("  -D binddn  bind DN\n"),
318
N_("  -e [!]<ext>[=<extparam>] general extensions (! indicates criticality)\n")
319
320
N_("             [!]assert=<filter>     (RFC 4528; a RFC 4515 Filter string)\n")
N_("             [!]authzid=<authzid>   (RFC 4370; \"dn:<dn>\" or \"u:<user>\")\n")
321
322
323
#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
#if 0
                 /* non-advertized support for proxyDN */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
324
N_("             [!]proxydn=<dn>        (a RFC 4514 DN string)\n")
325
326
#endif
#endif
327
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
Pierangelo Masarati's avatar
Pierangelo Masarati committed
328
N_("             [!]chaining[=<resolveBehavior>[/<continuationBehavior>]]\n")
329
330
331
N_("                     one of \"chainingPreferred\", \"chainingRequired\",\n")
N_("                     \"referralsPreferred\", \"referralsRequired\"\n")
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
332
N_("             [!]manageDSAit         (RFC 3296)\n")
333
334
335
336
N_("             [!]noop\n")
#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
N_("             ppolicy\n")
#endif
337
338
N_("             [!]postread[=<attrs>]  (RFC 4527; comma-separated attr list)\n")
N_("             [!]preread[=<attrs>]   (RFC 4527; comma-separated attr list)\n")
Kurt Zeilenga's avatar
Kurt Zeilenga committed
339
N_("             [!]relax\n")
340
341
342
#ifdef LDAP_CONTROL_X_SESSION_TRACKING
N_("             [!]sessiontracking\n")
#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
343
344
345
N_("             abandon, cancel, ignore (SIGINT sends abandon/cancel,\n"
   "             or ignores response; if critical, doesn't wait for SIGINT.\n"
   "             not really controls)\n")
346
N_("  -h host    LDAP server\n"),
347
N_("  -H URI     LDAP Uniform Resource Identifier(s)\n"),
348
349
N_("  -I         use SASL Interactive mode\n"),
N_("  -n         show what would be done but don't actually do it\n"),
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
350
N_("  -N         do not use reverse DNS to canonicalize SASL host name\n"),
351
N_("  -O props   SASL security properties\n"),
352
N_("  -o <opt>[=<optparam>] general options\n"),
353
N_("             nettimeout=<timeout> (in seconds, or \"none\" or \"max\")\n"),
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
354
N_("             ldif-wrap=<width> (in columns, or \"no\" for no wrapping)\n"),
355
356
357
358
359
360
361
362
363
364
365
366
367
N_("  -p port    port on LDAP server\n"),
N_("  -Q         use SASL Quiet mode\n"),
N_("  -R realm   SASL realm\n"),
N_("  -U authcid SASL authentication identity\n"),
N_("  -v         run in verbose mode (diagnostics to standard output)\n"),
N_("  -V         print version info (-VV only)\n"),
N_("  -w passwd  bind password (for simple authentication)\n"),
N_("  -W         prompt for bind password\n"),
N_("  -x         Simple authentication\n"),
N_("  -X authzid SASL authorization identity (\"dn:<dn>\" or \"u:<user>\")\n"),
N_("  -y file    Read password from file\n"),
N_("  -Y mech    SASL mechanism\n"),
N_("  -Z         Start TLS request (-ZZ to require successful response)\n"),
368
369
NULL
	};
370
	const char *const *cpp;
371

372
	fputs( _("Common options:\n"), stderr );
373
	for( cpp = descriptions; *cpp != NULL; cpp++ ) {
374
		if( strchr( options, (*cpp)[3] ) || (*cpp)[3] == ' ' ) {
375
			fputs( _(*cpp), stderr );
376
377
		}
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
378
379

	tool_destroy();
380
381
}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
382
void tool_perror(
383
	const char *func,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
384
	int err,
385
386
387
	const char *extra,
	const char *matched,
	const char *info,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
388
389
	char **refs )
{
390
391
	fprintf( stderr, "%s: %s (%d)%s\n",
		func, ldap_err2string( err ), err, extra ? extra : "" );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409

	if ( matched && *matched ) {
		fprintf( stderr, _("\tmatched DN: %s\n"), matched );
	}

	if ( info && *info ) {
		fprintf( stderr, _("\tadditional info: %s\n"), info );
	}

	if ( refs && *refs ) {
		int i;
		fprintf( stderr, _("\treferrals:\n") );
		for( i=0; refs[i]; i++ ) {
			fprintf( stderr, "\t\t%s\n", refs[i] );
		}
	}
}

410
411
412
413
414

void
tool_args( int argc, char **argv )
{
	int i;
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
415
416

	while (( i = getopt( argc, argv, options )) != EOF ) {
417
418
		int crit, ival;
		char *control, *cvalue, *next;
419
420
		switch( i ) {
		case 'c':	/* continuous operation mode */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
421
			contoper++;
422
			break;
423
		case 'C':	/* referrals: obsolete */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
424
			referrals++;
425
426
			break;
		case 'd':
427
428
429
430
431
432
			ival = strtol( optarg, &next, 10 );
			if (next == NULL || next[0] != '\0') {
				fprintf( stderr, "%s: unable to parse debug value \"%s\"\n", prog, optarg);
				exit(EXIT_FAILURE);
			}
			debug |= ival;
433
434
435
436
437
438
439
440
			break;
		case 'D':	/* bind DN */
			if( binddn != NULL ) {
				fprintf( stderr, "%s: -D previously specified\n", prog );
				exit( EXIT_FAILURE );
			}
			binddn = ber_strdup( optarg );
			break;
441
		case 'e':	/* general extensions (controls and such) */
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
			/* should be extended to support comma separated list of
			 *	[!]key[=value] parameters, e.g.  -e !foo,bar=567
			 */

			crit = 0;
			cvalue = NULL;
			if( optarg[0] == '!' ) {
				crit = 1;
				optarg++;
			}

			control = ber_strdup( optarg );
			if ( (cvalue = strchr( control, '=' )) != NULL ) {
				*cvalue++ = '\0';
			}

458
			if ( strcasecmp( control, "assert" ) == 0 ) {
459
				if( assertctl ) {
460
461
462
463
464
465
466
467
					fprintf( stderr, "assert control previously specified\n");
					exit( EXIT_FAILURE );
				}
				if( cvalue == NULL ) {
					fprintf( stderr, "assert: control value expected\n" );
					usage();
				}

468
469
				assertctl = 1 + crit;

470
				assert( assertion == NULL );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
471
				assertion = ber_strdup( cvalue );
472
473

			} else if ( strcasecmp( control, "authzid" ) == 0 ) {
474
475
476
477
				if( authzid != NULL ) {
					fprintf( stderr, "authzid control previously specified\n");
					exit( EXIT_FAILURE );
				}
478
479
480
481
482
483
#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
				if( proxydn != NULL ) {
					fprintf( stderr, "authzid control incompatible with proxydn\n");
					exit( EXIT_FAILURE );
				}
#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
484
485
486
487
488
489
490
491
492
493
				if( cvalue == NULL ) {
					fprintf( stderr, "authzid: control value expected\n" );
					usage();
				}
				if( !crit ) {
					fprintf( stderr, "authzid: must be marked critical\n" );
					usage();
				}

				assert( authzid == NULL );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
494
				authzid = ber_strdup( cvalue );
495

496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
			} else if ( strcasecmp( control, "proxydn" ) == 0 ) {
				if( proxydn != NULL ) {
					fprintf( stderr, "proxydn control previously specified\n");
					exit( EXIT_FAILURE );
				}
				if( authzid != NULL ) {
					fprintf( stderr, "proxydn control incompatible with authzid\n");
					exit( EXIT_FAILURE );
				}
				if( cvalue == NULL ) {
					fprintf( stderr, "proxydn: control value expected\n" );
					usage();
				}
				if( !crit ) {
					fprintf( stderr, "proxydn: must be marked critical\n" );
					usage();
				}

				assert( proxydn == NULL );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
516
				proxydn = ber_strdup( cvalue );
517
518
#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
519
520
521
			} else if ( ( strcasecmp( control, "relax" ) == 0 ) ||
				( strcasecmp( control, "manageDIT" ) == 0 ) )
			{
522
523
				if( manageDIT ) {
					fprintf( stderr,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
524
						"relax control previously specified\n");
525
526
527
528
					exit( EXIT_FAILURE );
				}
				if( cvalue != NULL ) {
					fprintf( stderr,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
529
						"relax: no control value expected\n" );
530
531
532
533
534
					usage();
				}

				manageDIT = 1 + crit;

535
536
537
			} else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
				if( manageDSAit ) {
					fprintf( stderr,
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
538
						"manageDSAit control previously specified\n");
539
540
541
542
					exit( EXIT_FAILURE );
				}
				if( cvalue != NULL ) {
					fprintf( stderr,
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
543
						"manageDSAit: no control value expected\n" );
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
					usage();
				}

				manageDSAit = 1 + crit;

			} else if ( strcasecmp( control, "noop" ) == 0 ) {
				if( noop ) {
					fprintf( stderr, "noop control previously specified\n");
					exit( EXIT_FAILURE );
				}
				if( cvalue != NULL ) {
					fprintf( stderr, "noop: no control value expected\n" );
					usage();
				}

				noop = 1 + crit;

561
#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
Howard Chu's avatar
Howard Chu committed
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
			} else if ( strcasecmp( control, "ppolicy" ) == 0 ) {
				if( ppolicy ) {
					fprintf( stderr, "ppolicy control previously specified\n");
					exit( EXIT_FAILURE );
				}
				if( cvalue != NULL ) {
					fprintf( stderr, "ppolicy: no control value expected\n" );
					usage();
				}
				if( crit ) {
					fprintf( stderr, "ppolicy: critical flag not allowed\n" );
					usage();
				}

				ppolicy = 1;
577
#endif
Howard Chu's avatar
Howard Chu committed
578

579
580
581
582
583
584
585
			} else if ( strcasecmp( control, "preread" ) == 0 ) {
				if( preread ) {
					fprintf( stderr, "preread control previously specified\n");
					exit( EXIT_FAILURE );
				}

				preread = 1 + crit;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
586
				preread_attrs = ber_strdup( cvalue );
587
588
589
590
591
592
593
594

			} else if ( strcasecmp( control, "postread" ) == 0 ) {
				if( postread ) {
					fprintf( stderr, "postread control previously specified\n");
					exit( EXIT_FAILURE );
				}

				postread = 1 + crit;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
595
				postread_attrs = ber_strdup( cvalue );
596

597
598
599
600
601
602
603
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
			} else if ( strcasecmp( control, "chaining" ) == 0 ) {
				chaining = 1 + crit;

				if ( cvalue != NULL ) {
					char	*continuation;

Pierangelo Masarati's avatar
Pierangelo Masarati committed
604
					continuation = strchr( cvalue, '/' );
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
					if ( continuation ) {
						/* FIXME: this makes sense only in searches */
						*continuation++ = '\0';
						if ( strcasecmp( continuation, "chainingPreferred" ) == 0 ) {
							chainingContinuation = LDAP_CHAINING_PREFERRED;
						} else if ( strcasecmp( continuation, "chainingRequired" ) == 0 ) {
							chainingContinuation = LDAP_CHAINING_REQUIRED;
						} else if ( strcasecmp( continuation, "referralsPreferred" ) == 0 ) {
							chainingContinuation = LDAP_REFERRALS_PREFERRED;
						} else if ( strcasecmp( continuation, "referralsRequired" ) == 0 ) {
							chainingContinuation = LDAP_REFERRALS_REQUIRED;
						} else {
							fprintf( stderr,
								"chaining behavior control "
								"continuation value \"%s\" invalid\n",
								continuation );
							exit( EXIT_FAILURE );
						}
					}
	
					if ( strcasecmp( cvalue, "chainingPreferred" ) == 0 ) {
						chainingResolve = LDAP_CHAINING_PREFERRED;
					} else if ( strcasecmp( cvalue, "chainingRequired" ) == 0 ) {
						chainingResolve = LDAP_CHAINING_REQUIRED;
					} else if ( strcasecmp( cvalue, "referralsPreferred" ) == 0 ) {
						chainingResolve = LDAP_REFERRALS_PREFERRED;
					} else if ( strcasecmp( cvalue, "referralsRequired" ) == 0 ) {
						chainingResolve = LDAP_REFERRALS_REQUIRED;
					} else {
						fprintf( stderr,
							"chaining behavior control "
							"resolve value \"%s\" invalid\n",
							cvalue);
						exit( EXIT_FAILURE );
					}
				}
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */

Quanah Gibson-Mount's avatar
cleanup    
Quanah Gibson-Mount committed
643
644
645
646
647
648
649
650
651
652
653
654
655
#ifdef LDAP_CONTROL_X_SESSION_TRACKING
			} else if ( strcasecmp( control, "sessiontracking" ) == 0 ) {
				if ( sessionTracking ) {
					fprintf( stderr, "%s: session tracking can be only specified once\n", prog );
					exit( EXIT_FAILURE );
				}
				sessionTracking = 1;
				if( crit ) {
					fprintf( stderr, "sessiontracking: critical flag not allowed\n" );
					usage();
				}
#endif /* LDAP_CONTROL_X_SESSION_TRACKING */

656
657
			/* this shouldn't go here, really; but it's a feature... */
			} else if ( strcasecmp( control, "abandon" ) == 0 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
658
				abcan = Intr_Abandon;
659
660
661
				if ( crit ) {
					gotintr = abcan;
				}
662
663

			} else if ( strcasecmp( control, "cancel" ) == 0 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
664
				abcan = Intr_Cancel;
665
666
667
668
669
				if ( crit ) {
					gotintr = abcan;
				}

			} else if ( strcasecmp( control, "ignore" ) == 0 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
670
				abcan = Intr_Ignore;
671
672
673
				if ( crit ) {
					gotintr = abcan;
				}
674

675
676
677
			} else if ( tool_is_oid( control ) ) {
				LDAPControl	*tmpctrls, ctrl;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
678
				tmpctrls = (LDAPControl *)ber_memrealloc( unknown_ctrls,
679
680
681
682
683
684
685
					(unknown_ctrls_num + 1)*sizeof( LDAPControl ) );
				if ( tmpctrls == NULL ) {
					fprintf( stderr, "%s: no memory?\n", prog );
					exit( EXIT_FAILURE );
				}
				unknown_ctrls = tmpctrls;
				ctrl.ldctl_oid = control;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
686
687
				/* don't free it */
				control = NULL;
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
				ctrl.ldctl_value.bv_val = NULL;
				ctrl.ldctl_value.bv_len = 0;
				ctrl.ldctl_iscritical = crit;

				if ( cvalue != NULL ) {
					struct berval	bv;
					size_t		len = strlen( cvalue );
					int		retcode;

					bv.bv_len = LUTIL_BASE64_DECODE_LEN( len );
					bv.bv_val = ber_memalloc( bv.bv_len + 1 );

					retcode = lutil_b64_pton( cvalue,
						(unsigned char *)bv.bv_val,
						bv.bv_len );

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
704
					if ( retcode == -1 || (unsigned) retcode > bv.bv_len ) {
705
706
707
708
709
710
711
712
713
714
715
716
						fprintf( stderr, "Unable to parse value of general control %s\n",
							control );
						usage();
					}

					bv.bv_len = retcode;
					ctrl.ldctl_value = bv;
				}

				unknown_ctrls[ unknown_ctrls_num ] = ctrl;
				unknown_ctrls_num++;

717
718
			} else {
				fprintf( stderr, "Invalid general control name: %s\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
719
					control );
720
721
				usage();
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
722
723
724
725
			if ( control ) {
				ber_memfree( control );	 
				control = NULL;
			}
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
			break;
		case 'f':	/* read from file */
			if( infile != NULL ) {
				fprintf( stderr, "%s: -f previously specified\n", prog );
				exit( EXIT_FAILURE );
			}
			infile = ber_strdup( optarg );
			break;
		case 'h':	/* ldap host */
			if( ldaphost != NULL ) {
				fprintf( stderr, "%s: -h previously specified\n", prog );
				exit( EXIT_FAILURE );
			}
			ldaphost = ber_strdup( optarg );
			break;
		case 'H':	/* ldap URI */
			if( ldapuri != NULL ) {
				fprintf( stderr, "%s: -H previously specified\n", prog );
				exit( EXIT_FAILURE );
			}
			ldapuri = ber_strdup( optarg );
			break;
		case 'I':
#ifdef HAVE_CYRUS_SASL
			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
				fprintf( stderr, "%s: incompatible previous "
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
752
753
					"authentication choice\n",
					prog );
754
755
756
757
758
759
760
				exit( EXIT_FAILURE );
			}
			authmethod = LDAP_AUTH_SASL;
			sasl_flags = LDAP_SASL_INTERACTIVE;
			break;
#else
			fprintf( stderr, "%s: was not compiled with SASL support\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
761
				prog );
762
763
764
765
			exit( EXIT_FAILURE );
#endif
		case 'M':
			/* enable Manage DSA IT */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
766
			manageDSAit++;
767
768
			break;
		case 'n':	/* print operations, don't actually do them */
769
			dont++;
770
			break;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
771
772
773
		case 'N':
			nocanon++;
			break;
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
		case 'o':
			control = ber_strdup( optarg );
			if ( (cvalue = strchr( control, '=' )) != NULL ) {
				*cvalue++ = '\0';
			}

			if ( strcasecmp( control, "nettimeout" ) == 0 ) {
				if( nettimeout.tv_sec != -1 ) {
					fprintf( stderr, "nettimeout option previously specified\n");
					exit( EXIT_FAILURE );
				}
				if( cvalue == NULL || cvalue[0] == '\0' ) {
					fprintf( stderr, "nettimeout: option value expected\n" );
					usage();
				}
		 		if ( strcasecmp( cvalue, "none" ) == 0 ) {
		 			nettimeout.tv_sec = 0;
		 		} else if ( strcasecmp( cvalue, "max" ) == 0 ) {
		 			nettimeout.tv_sec = LDAP_MAXINT;
		 		} else {
		 			ival = strtol( cvalue, &next, 10 );
		 			if ( next == NULL || next[0] != '\0' ) {
		 				fprintf( stderr,
		 					_("Unable to parse network timeout \"%s\"\n"), cvalue );
		 				exit( EXIT_FAILURE );
		 			}
		 			nettimeout.tv_sec = ival;
		 		}
		 		if( nettimeout.tv_sec < 0 || nettimeout.tv_sec > LDAP_MAXINT ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
803
804
		 			fprintf( stderr, _("%s: invalid network timeout (%ld) specified\n"),
		 				prog, (long)nettimeout.tv_sec );
805
806
	 				exit( EXIT_FAILURE );
 				}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824

			} else if ( strcasecmp( control, "ldif-wrap" ) == 0 ) {
				if ( cvalue == 0 ) {
					ldif_wrap = LDIF_LINE_WIDTH;

				} else if ( strcasecmp( cvalue, "no" ) == 0 ) {
					ldif_wrap = LDIF_LINE_WIDTH_MAX;

				} else {
					unsigned int u;
					if ( lutil_atou( &u, cvalue ) ) {
						fprintf( stderr,
							_("Unable to parse ldif-wrap=\"%s\"\n"), cvalue );
		 				exit( EXIT_FAILURE );
					}
					ldif_wrap = (ber_len_t)u;
				}

825
826
827
828
829
			} else {
				fprintf( stderr, "Invalid general option name: %s\n",
					control );
				usage();
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
830
			ber_memfree(control);
831
			break;
832
833
834
835
836
837
838
839
		case 'O':
#ifdef HAVE_CYRUS_SASL
			if( sasl_secprops != NULL ) {
				fprintf( stderr, "%s: -O previously specified\n", prog );
				exit( EXIT_FAILURE );
			}
			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
				fprintf( stderr, "%s: incompatible previous "
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
840
					"authentication choice\n", prog );
841
842
843
844
845
				exit( EXIT_FAILURE );
			}
			authmethod = LDAP_AUTH_SASL;
			sasl_secprops = ber_strdup( optarg );
#else
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
846
			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
847
848
849
850
851
852
853
854
			exit( EXIT_FAILURE );
#endif
			break;
		case 'p':
			if( ldapport ) {
				fprintf( stderr, "%s: -p previously specified\n", prog );
				exit( EXIT_FAILURE );
			}
855
856
857
858
859
860
			ival = strtol( optarg, &next, 10 );
			if ( next == NULL || next[0] != '\0' ) {
				fprintf( stderr, "%s: unable to parse port number \"%s\"\n", prog, optarg );
				exit( EXIT_FAILURE );
			}
			ldapport = ival;
861
862
			break;
		case 'P':
863
864
			ival = strtol( optarg, &next, 10 );
			if ( next == NULL || next[0] != '\0' ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
865
				fprintf( stderr, "%s: unable to parse protocol version \"%s\"\n", prog, optarg );
866
867
868
				exit( EXIT_FAILURE );
			}
			switch( ival ) {
869
			case 2:
870
				if( protocol == LDAP_VERSION3 ) {
871
					fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
872
						prog, protocol );
873
874
					exit( EXIT_FAILURE );
				}
875
				protocol = LDAP_VERSION2;
876
877
				break;
			case 3:
878
				if( protocol == LDAP_VERSION2 ) {
879
					fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
880
						prog, protocol );
881
882
					exit( EXIT_FAILURE );
				}
883
				protocol = LDAP_VERSION3;
884
885
886
				break;
			default:
				fprintf( stderr, "%s: protocol version should be 2 or 3\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
887
					prog );
888
889
890
891
892
893
894
				usage();
			}
			break;
		case 'Q':
#ifdef HAVE_CYRUS_SASL
			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
				fprintf( stderr, "%s: incompatible previous "
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
895
896
					"authentication choice\n",
					prog );
897
898
899
900
901
902
903
				exit( EXIT_FAILURE );
			}
			authmethod = LDAP_AUTH_SASL;
			sasl_flags = LDAP_SASL_QUIET;
			break;
#else
			fprintf( stderr, "%s: not compiled with SASL support\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
904
				prog );
905
906
907
908
909
910
911
912
913
914
			exit( EXIT_FAILURE );
#endif
		case 'R':
#ifdef HAVE_CYRUS_SASL
			if( sasl_realm != NULL ) {
				fprintf( stderr, "%s: -R previously specified\n", prog );
				exit( EXIT_FAILURE );
			}
			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
				fprintf( stderr, "%s: incompatible previous "
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
915
916
					"authentication choice\n",
					prog );
917
918
919
920
921
922
				exit( EXIT_FAILURE );
			}
			authmethod = LDAP_AUTH_SASL;
			sasl_realm = ber_strdup( optarg );
#else
			fprintf( stderr, "%s: not compiled with SASL support\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
923
				prog );
924
925
926
927
928
929
930
931
932
933
934
			exit( EXIT_FAILURE );
#endif
			break;
		case 'U':
#ifdef HAVE_CYRUS_SASL
			if( sasl_authc_id != NULL ) {
				fprintf( stderr, "%s: -U previously specified\n", prog );
				exit( EXIT_FAILURE );
			}
			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
				fprintf( stderr, "%s: incompatible previous "
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
935
936
					"authentication choice\n",
					prog );
937
938
939
940
941
942
				exit( EXIT_FAILURE );
			}
			authmethod = LDAP_AUTH_SASL;
			sasl_authc_id = ber_strdup( optarg );
#else
			fprintf( stderr, "%s: not compiled with SASL support\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
943
				prog );
944
945
946
947
			exit( EXIT_FAILURE );
#endif
			break;
		case 'v':	/* verbose mode */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
948
			verbose++;
949
			break;
950
951
952
		case 'V':	/* version */
			version++;
			break;
953
954
955
956
957
958
959
960
961
962
963
964
		case 'w':	/* password */
			passwd.bv_val = ber_strdup( optarg );
			{
				char* p;

				for( p = optarg; *p != '\0'; p++ ) {
					*p = '\0';
				}
			}
			passwd.bv_len = strlen( passwd.bv_val );
			break;
		case 'W':
Kurt Zeilenga's avatar
Kurt Zeilenga committed
965
			want_bindpw++;
966
967
968
969
970
971
972
973
974
975
976
			break;
		case 'y':
			pw_file = optarg;
			break;
		case 'Y':
#ifdef HAVE_CYRUS_SASL
			if( sasl_mech != NULL ) {
				fprintf( stderr, "%s: -Y previously specified\n", prog );
				exit( EXIT_FAILURE );
			}
			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
977
978
				fprintf( stderr,
					"%s: incompatible with authentication choice\n", prog );
979
980
981
982
983
				exit( EXIT_FAILURE );
			}
			authmethod = LDAP_AUTH_SASL;
			sasl_mech = ber_strdup( optarg );
#else
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
984
			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
985
986
987
988
989
990
			exit( EXIT_FAILURE );
#endif
			break;
		case 'x':
			if( authmethod != -1 && authmethod != LDAP_AUTH_SIMPLE ) {
				fprintf( stderr, "%s: incompatible with previous "
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
991
					"authentication choice\n", prog );
992
993
994
995
996
997
998
999
1000
1001
1002
1003
				exit( EXIT_FAILURE );
			}
			authmethod = LDAP_AUTH_SIMPLE;
			break;
		case 'X':
#ifdef HAVE_CYRUS_SASL
			if( sasl_authz_id != NULL ) {
				fprintf( stderr, "%s: -X previously specified\n", prog );
				exit( EXIT_FAILURE );
			}
			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
				fprintf( stderr, "%s: -X incompatible with "
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1004
					"authentication choice\n", prog );
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
				exit( EXIT_FAILURE );
			}
			authmethod = LDAP_AUTH_SASL;
			sasl_authz_id = ber_strdup( optarg );
#else
			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
			exit( EXIT_FAILURE );
#endif
			break;
		case 'Z':
#ifdef HAVE_TLS
1016
			use_tls++;
1017
1018
1019
1020
1021
1022
#else
			fprintf( stderr, "%s: not compiled with TLS support\n", prog );
			exit( EXIT_FAILURE );
#endif
			break;
		default:
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1023
			if( handle_private_option( i ) ) break;
1024
			fprintf( stderr, "%s: unrecognized option -%c\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1025
				prog, optopt );
1026
1027
			usage();
		}
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1028
	}
1029

1030
1031
1032
1033
1034
	{
		/* prevent bad linking */
		LDAPAPIInfo api;
		api.ldapai_info_version = LDAP_API_INFO_VERSION;

1035
1036
1037
		if ( ldap_get_option(NULL, LDAP_OPT_API_INFO, &api)
			!= LDAP_OPT_SUCCESS )
		{
1038
1039
1040
1041
1042
1043
			fprintf( stderr, "%s: ldap_get_option(API_INFO) failed\n", prog );
			exit( EXIT_FAILURE );
		}

		if (api.ldapai_info_version != LDAP_API_INFO_VERSION) {
			fprintf( stderr, "LDAP APIInfo version mismatch: "
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1044
				"library %d, header %d\n",
1045
1046
				api.ldapai_info_version, LDAP_API_INFO_VERSION );
			exit( EXIT_FAILURE );
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1047
		}
1048
1049
1050

		if( api.ldapai_api_version != LDAP_API_VERSION ) {
			fprintf( stderr, "LDAP API version mismatch: "
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1051
				"library %d, header %d\n",
1052
1053
1054
1055
1056
1057
				api.ldapai_api_version, LDAP_API_VERSION );
			exit( EXIT_FAILURE );
		}

		if( strcmp(api.ldapai_vendor_name, LDAP_VENDOR_NAME ) != 0 ) {
			fprintf( stderr, "LDAP vendor name mismatch: "
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1058
				"library %s, header %s\n",
1059
1060
1061
1062
1063
1064
				api.ldapai_vendor_name, LDAP_VENDOR_NAME );
			exit( EXIT_FAILURE );
		}

		if( api.ldapai_vendor_version != LDAP_VENDOR_VERSION ) {
			fprintf( stderr, "LDAP vendor version mismatch: "
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1065
				"library %d, header %d\n",
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
				api.ldapai_vendor_version, LDAP_VENDOR_VERSION );
			exit( EXIT_FAILURE );
		}

		if (version) {
			fprintf( stderr, "%s: %s\t(LDAP library: %s %d)\n",
				prog, __Version,
				LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION );
			if (version > 1) exit( EXIT_SUCCESS );
		}
Hallvard Furuseth's avatar
Hallvard Furuseth committed
1076
1077

		ldap_memfree( api.ldapai_vendor_name );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1078
		ber_memvfree( (void **)api.ldapai_extensions );
1079
1080
1081
1082
	}

	if (protocol == -1)
		protocol = LDAP_VERSION3;
1083

1084
	if (authmethod == -1 && protocol > LDAP_VERSION2) {
1085
#ifdef HAVE_CYRUS_SASL
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1086
1087
1088
1089
1090
		if ( binddn != NULL ) {
			authmethod = LDAP_AUTH_SIMPLE;
		} else {
			authmethod = LDAP_AUTH_SASL;
		}
1091
1092
1093
1094
1095
#else
		authmethod = LDAP_AUTH_SIMPLE;
#endif
	}

1096
1097
1098
1099
1100
1101
	if( ldapuri == NULL ) {
		if( ldapport && ( ldaphost == NULL )) {
			fprintf( stderr, "%s: -p without -h is invalid.\n", prog );
			exit( EXIT_FAILURE );
		}
	} else {
1102
1103
1104
1105
1106
1107
1108
1109
1110
		if( ldaphost != NULL ) {
			fprintf( stderr, "%s: -H incompatible with -h\n", prog );
			exit( EXIT_FAILURE );
		}
		if( ldapport ) {
			fprintf( stderr, "%s: -H incompatible with -p\n", prog );
			exit( EXIT_FAILURE );
		}
	}
1111

1112
	if( protocol == LDAP_VERSION2 ) {
1113
		if( assertctl || authzid || manageDIT || manageDSAit ||
1114
1115
1116
#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
			proxydn ||
#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
1117
1118
1119
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
			chaining ||
#endif
1120
1121
1122
#ifdef LDAP_CONTROL_X_SESSION_TRACKING
			sessionTracking ||
#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
1123
1124
			noop || ppolicy || preread || postread )
		{
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
			fprintf( stderr, "%s: -e/-M incompatible with LDAPv2\n", prog );
			exit( EXIT_FAILURE );
		}
#ifdef HAVE_TLS
		if( use_tls ) {
			fprintf( stderr, "%s: -Z incompatible with LDAPv2\n", prog );
			exit( EXIT_FAILURE );
		}
#endif
#ifdef HAVE_CYRUS_SASL
		if( authmethod == LDAP_AUTH_SASL ) {
			fprintf( stderr, "%s: -[IOQRUXY] incompatible with LDAPv2\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1137
				prog );
1138
1139
1140
1141
			exit( EXIT_FAILURE );
		}
#endif
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1142
1143
1144
1145
1146
1147

	if ( ( pw_file || want_bindpw ) && !BER_BVISNULL( &passwd ) ) {
		fprintf( stderr, "%s: -%c incompatible with -w\n",
			prog, ( pw_file ? 'y' : 'W' ) );
		exit( EXIT_FAILURE );
	}
1148
1149
1150
1151
}


LDAP *
1152
tool_conn_setup( int dont, void (*private_setup)( LDAP * ) )
1153
1154
1155
1156
1157
{
	LDAP *ld = NULL;

	if ( debug ) {
		if( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug )
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1158
1159
			!= LBER_OPT_SUCCESS )
		{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1160
1161
			fprintf( stderr,
				"Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug );
1162
1163
		}
		if( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug )
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1164
1165
			!= LDAP_OPT_SUCCESS )
		{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1166
1167
			fprintf( stderr,
				"Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug );
1168
1169
1170
1171
1172
1173
1174
		}
	}

#ifdef SIGPIPE
	(void) SIGNAL( SIGPIPE, SIG_IGN );
#endif

1175
1176
1177
1178
	if ( abcan ) {
		SIGNAL( SIGINT, do_sig );
	}

1179
	if ( !dont ) {
1180
1181
		int rc;

1182
		if( ( ldaphost != NULL || ldapport ) && ( ldapuri == NULL ) ) {
1183
1184
1185
			/* construct URL */
			LDAPURLDesc url;
			memset( &url, 0, sizeof(url));
1186

1187
1188
1189
1190
			url.lud_scheme = "ldap";
			url.lud_host = ldaphost;
			url.lud_port = ldapport;
			url.lud_scope = LDAP_SCOPE_DEFAULT;
1191

1192
			ldapuri = ldap_url_desc2str( &url );
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254

		} else if ( ldapuri != NULL ) {
			LDAPURLDesc	*ludlist, **ludp;
			char		**urls = NULL;
			int		nurls = 0;

			rc = ldap_url_parselist( &ludlist, ldapuri );
			if ( rc != LDAP_URL_SUCCESS ) {
				fprintf( stderr,
					"Could not parse LDAP URI(s)=%s (%d)\n",
					ldapuri, rc );
				exit( EXIT_FAILURE );
			}

			for ( ludp = &ludlist; *ludp != NULL; ) {
				LDAPURLDesc	*lud = *ludp;
				char		**tmp;

				if ( lud->lud_dn != NULL && lud->lud_dn[ 0 ] != '\0' &&
					( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) )
				{
					/* if no host but a DN is provided,
					 * use DNS SRV to gather the host list
					 * and turn it into a list of URIs
					 * using the scheme provided */
					char	*domain = NULL,
						*hostlist = NULL,
						**hosts = NULL;
					int	i,
						len_proto = strlen( lud->lud_scheme );

					if ( ldap_dn2domain( lud->lud_dn, &domain )
						|| domain == NULL )
					{
						fprintf( stderr,
							"DNS SRV: Could not turn "
							"DN=\"%s\" into a domain\n",
							lud->lud_dn );
						goto dnssrv_free;
					}
					
					rc = ldap_domain2hostlist( domain, &hostlist );
					if ( rc ) {
						fprintf( stderr,
							"DNS SRV: Could not turn "
							"domain=%s into a hostlist\n",
							domain );
						goto dnssrv_free;
					}

					hosts = ldap_str2charray( hostlist, " " );
					if ( hosts == NULL ) {
						fprintf( stderr,
							"DNS SRV: Could not parse "
							"hostlist=\"%s\"\n",
							hostlist );
						goto dnssrv_free;
					}

					for ( i = 0; hosts[ i ] != NULL; i++ )
						/* count'em */ ;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1255
					tmp = (char **)ber_memrealloc( urls, sizeof( char * ) * ( nurls + i + 1 ) );
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
					if ( tmp == NULL ) {
						fprintf( stderr,
							"DNS SRV: out of memory?\n" );
						goto dnssrv_free;
					}
					urls = tmp;
					urls[ nurls ] = NULL;

					for ( i = 0; hosts[ i ] != NULL; i++ ) {
						size_t	len = len_proto
							+ STRLENOF( "://" )
							+ strlen( hosts[ i ] )
							+ 1;

						urls[ nurls + i + 1 ] = NULL;
						urls[ nurls + i ] = (char *)malloc( sizeof( char ) * len );
						if ( urls[ nurls + i ] == NULL ) {
							fprintf( stderr,
								"DNS SRV: out of memory?\n" );
							goto dnssrv_free;
						}

						snprintf( urls[ nurls + i ], len, "%s://%s",
							lud->lud_scheme, hosts[ i ] );
					}
					nurls += i;

dnssrv_free:;
					ber_memvfree( (void **)hosts );
					ber_memfree( hostlist );
					ber_memfree( domain );

				} else {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1289
					tmp = (char **)ber_memrealloc( urls, sizeof( char * ) * ( nurls + 2 ) );
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
					if ( tmp == NULL ) {
						fprintf( stderr,
							"DNS SRV: out of memory?\n" );
						break;
					}
					urls = tmp;
					urls[ nurls + 1 ] = NULL;

					urls[ nurls ] = ldap_url_desc2str( lud );
					if ( urls[ nurls ] == NULL ) {
						fprintf( stderr,
							"DNS SRV: out of memory?\n" );
						break;
					}
					nurls++;
				}

				*ludp = lud->lud_next;

				lud->lud_next = NULL;
				ldap_free_urldesc( lud );
			}

			if ( ludlist != NULL ) {
				ldap_free_urllist( ludlist );
				exit( EXIT_FAILURE );

			} else if ( urls == NULL ) {
				exit( EXIT_FAILURE );
			}

			ldap_memfree( ldapuri );
			ldapuri = ldap_charray2str( urls, " " );
			ber_memvfree( (void **)urls );
1324
1325
1326
1327
1328
1329
1330
1331
1332
		}

		if ( verbose ) {
			fprintf( stderr, "ldap_initialize( %s )\n",
				ldapuri != NULL ? ldapuri : "<DEFAULT>" );
		}
		rc = ldap_initialize( &ld, ldapuri );
		if( rc != LDAP_SUCCESS ) {
			fprintf( stderr,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1333
				"Could not create LDAP session handle for URI=%s (%d): %s\n",
1334
				ldapuri, rc, ldap_err2string(rc) );
1335
			exit( EXIT_FAILURE );
1336
1337
		}

Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1338
		if( private_setup ) private_setup( ld );
1339

1340
		/* referrals: obsolete */
1341
		if( ldap_set_option( ld, LDAP_OPT_REFERRALS,
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1342
			referrals ? LDAP_OPT_ON : LDAP_OPT_OFF ) != LDAP_OPT_SUCCESS )
1343
1344
		{
			fprintf( stderr, "Could not set LDAP_OPT_REFERRALS %s\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1345
				referrals ? "on" : "off" );
1346
			tool_exit( ld, EXIT_FAILURE );
1347
1348
		}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1349
#ifdef HAVE_CYRUS_SASL
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1350
		/* canon */
1351
1352
1353
1354
1355
1356
1357
		if( nocanon ) {
			if( ldap_set_option( ld, LDAP_OPT_X_SASL_NOCANON,
				LDAP_OPT_ON ) != LDAP_OPT_SUCCESS )
			{
				fprintf( stderr, "Could not set LDAP_OPT_X_SASL_NOCANON on\n" );
				tool_exit( ld, EXIT_FAILURE );
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1358
		}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1359
#endif
1360
		if( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &protocol )
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1361
			!= LDAP_OPT_SUCCESS )
1362
1363
		{
			fprintf( stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1364
				protocol );
1365
			tool_exit( ld, EXIT_FAILURE );
1366
1367
		}

1368
1369
1370
		if ( use_tls ) {
			rc = ldap_start_tls_s( ld, NULL, NULL );
			if ( rc != LDAP_SUCCESS ) {
1371
1372
1373
1374
				char *msg=NULL;
				ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&msg);
				tool_perror( "ldap_start_tls", rc, NULL, NULL, msg, NULL );
				ldap_memfree(msg);
1375
				if ( use_tls > 1 ) {
1376
					tool_exit( ld, EXIT_FAILURE );
1377
				}
1378
1379
			}
		}
1380
1381
1382
1383
1384

		if ( nettimeout.tv_sec > 0 ) {
	 		if ( ldap_set_option( ld, LDAP_OPT_NETWORK_TIMEOUT, (void *) &nettimeout )
				!= LDAP_OPT_SUCCESS )
			{