common.c 54.8 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/>.
 *
5
 * Copyright 1998-2009 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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
int		referrals = 0;
int		verbose = 0;
int		ldif = 0;
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;
85
#ifdef HAVE_CYRUS_SASL
86
87
88
89
90
91
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;
92
93
#endif

94
95
96
/* controls */
int		assertctl;
char		*assertion = NULL;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
97
struct berval	assertionvalue = BER_BVNULL;
98
char		*authzid = NULL;
99
100
101
102
103
/* 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 */
104
105
106
107
108
109
110
111
112
113
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 };
114
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
115
116
117
int		chaining = 0;
static int	chainingResolve = -1;
static int	chainingContinuation = -1;
118
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
119
120
121
122
#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
123
124
125
ber_int_t vlvPos;
ber_int_t vlvCount;
struct berval *vlvContext;
126
127
128

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

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

133
134
135
136
137
138
139
140
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
141
static int print_sss( LDAP *ld, LDAPControl *ctrl );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
142
static int print_vlv( LDAP *ld, LDAPControl *ctrl );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
143
144
145
#ifdef LDAP_CONTROL_X_DEREF
static int print_deref( LDAP *ld, LDAPControl *ctrl );
#endif
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
146
147
148
#ifdef LDAP_CONTROL_X_WHATFAILED
static int print_whatfailed( LDAP *ld, LDAPControl *ctrl );
#endif
149
150
151
152
153
154
155
156
157
158
159
160

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
161
	{ LDAP_CONTROL_SORTRESPONSE,	TOOL_SEARCH,	print_sss },
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
162
	{ LDAP_CONTROL_VLVRESPONSE,		TOOL_SEARCH,	print_vlv },
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
163
164
#ifdef LDAP_CONTROL_X_DEREF
	{ LDAP_CONTROL_X_DEREF,				TOOL_SEARCH,	print_deref },
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
165
166
167
#endif
#ifdef LDAP_CONTROL_X_WHATFAILED
	{ LDAP_CONTROL_X_WHATFAILED,			TOOL_ALL,	print_whatfailed },
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
168
#endif
169
170
171
172
	{ NULL,						0,		NULL }
};

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

176
177
178
179
180
181
182
183
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

#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 */

223
224
225
226
227
228
RETSIGTYPE
do_sig( int sig )
{
	gotintr = abcan;
}

229
void
230
tool_init( tool_type_t type )
231
{
232
	tool_type = type;
233
234
235
	ldap_pvt_setlocale(LC_MESSAGES, "");
	ldap_pvt_bindtextdomain(OPENLDAP_PACKAGE, LDAP_LOCALEDIR);
	ldap_pvt_textdomain(OPENLDAP_PACKAGE);
236
237
}

238
239
240
241
242
243
244
245
246
void
tool_destroy( void )
{
#ifdef HAVE_CYRUS_SASL
	sasl_done();
#endif
#ifdef HAVE_TLS
	ldap_pvt_tls_destroy();
#endif
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
247
248
249
250
251
252
253
254
255
256
257

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

	if ( pr_cookie.bv_val != NULL ) {
		ber_memfree( pr_cookie.bv_val );
		pr_cookie.bv_val = NULL;
		pr_cookie.bv_len = 0;
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
258
259
260
261
262

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

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
263
#if 0	/* not yet */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
264
265
266
	if ( passwd.bv_val != NULL ) {
		ber_memfree( passwd.bv_val );
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
267
#endif
268
269
}

270
271
272
273
void
tool_common_usage( void )
{
	static const char *const descriptions[] = {
274
275
N_("  -d level   set LDAP debugging level to `level'\n"),
N_("  -D binddn  bind DN\n"),
276
N_("  -e [!]<ext>[=<extparam>] general extensions (! indicates criticality)\n")
277
278
N_("             [!]assert=<filter>     (RFC 4528; a RFC 4515 Filter string)\n")
N_("             [!]authzid=<authzid>   (RFC 4370; \"dn:<dn>\" or \"u:<user>\")\n")
279
280
281
#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
#if 0
                 /* non-advertized support for proxyDN */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
282
N_("             [!]proxydn=<dn>        (a RFC 4514 DN string)\n")
283
284
#endif
#endif
285
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
Pierangelo Masarati's avatar
Pierangelo Masarati committed
286
N_("             [!]chaining[=<resolveBehavior>[/<continuationBehavior>]]\n")
287
288
289
N_("                     one of \"chainingPreferred\", \"chainingRequired\",\n")
N_("                     \"referralsPreferred\", \"referralsRequired\"\n")
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
290
N_("             [!]manageDSAit         (RFC 3296)\n")
291
292
293
294
N_("             [!]noop\n")
#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
N_("             ppolicy\n")
#endif
295
296
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
297
N_("             [!]relax\n")
298
299
300
#ifdef LDAP_CONTROL_X_SESSION_TRACKING
N_("             [!]sessiontracking\n")
#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
301
302
303
N_("             abandon, cancel, ignore (SIGINT sends abandon/cancel,\n"
   "             or ignores response; if critical, doesn't wait for SIGINT.\n"
   "             not really controls)\n")
304
N_("  -h host    LDAP server\n"),
305
N_("  -H URI     LDAP Uniform Resource Identifier(s)\n"),
306
307
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
308
N_("  -N         do not use reverse DNS to canonicalize SASL host name\n"),
309
N_("  -O props   SASL security properties\n"),
310
311
N_("  -o <opt>[=<optparam] general options\n"),
N_("             nettimeout=<timeout> (in seconds, or \"none\" or \"max\")\n"),
312
313
314
315
316
317
318
319
320
321
322
323
324
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"),
325
326
NULL
	};
327
	const char *const *cpp;
328

329
	fputs( _("Common options:\n"), stderr );
330
	for( cpp = descriptions; *cpp != NULL; cpp++ ) {
331
		if( strchr( options, (*cpp)[3] ) || (*cpp)[3] == ' ' ) {
332
			fputs( _(*cpp), stderr );
333
334
		}
	}
335
336
}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
337
void tool_perror(
338
	const char *func,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
339
	int err,
340
341
342
	const char *extra,
	const char *matched,
	const char *info,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
343
344
	char **refs )
{
345
346
	fprintf( stderr, "%s: %s (%d)%s\n",
		func, ldap_err2string( err ), err, extra ? extra : "" );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

	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] );
		}
	}
}

365
366
367
368
369

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

	while (( i = getopt( argc, argv, options )) != EOF ) {
372
373
		int crit, ival;
		char *control, *cvalue, *next;
374
375
		switch( i ) {
		case 'c':	/* continuous operation mode */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
376
			contoper++;
377
378
			break;
		case 'C':
Kurt Zeilenga's avatar
Kurt Zeilenga committed
379
			referrals++;
380
381
			break;
		case 'd':
382
383
384
385
386
387
			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;
388
389
390
391
392
393
394
395
			break;
		case 'D':	/* bind DN */
			if( binddn != NULL ) {
				fprintf( stderr, "%s: -D previously specified\n", prog );
				exit( EXIT_FAILURE );
			}
			binddn = ber_strdup( optarg );
			break;
396
		case 'e':	/* general extensions (controls and such) */
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
			/* 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';
			}

413
			if ( strcasecmp( control, "assert" ) == 0 ) {
414
				if( assertctl ) {
415
416
417
418
419
420
421
422
					fprintf( stderr, "assert control previously specified\n");
					exit( EXIT_FAILURE );
				}
				if( cvalue == NULL ) {
					fprintf( stderr, "assert: control value expected\n" );
					usage();
				}

423
424
				assertctl = 1 + crit;

425
426
427
428
				assert( assertion == NULL );
				assertion = cvalue;

			} else if ( strcasecmp( control, "authzid" ) == 0 ) {
429
430
431
432
				if( authzid != NULL ) {
					fprintf( stderr, "authzid control previously specified\n");
					exit( EXIT_FAILURE );
				}
433
434
435
436
437
438
#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 */
439
440
441
442
443
444
445
446
447
448
449
450
				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 );
				authzid = cvalue;

451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
#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 );
				proxydn = cvalue;
#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
474
475
476
			} else if ( ( strcasecmp( control, "relax" ) == 0 ) ||
				( strcasecmp( control, "manageDIT" ) == 0 ) )
			{
477
478
				if( manageDIT ) {
					fprintf( stderr,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
479
						"relax control previously specified\n");
480
481
482
483
					exit( EXIT_FAILURE );
				}
				if( cvalue != NULL ) {
					fprintf( stderr,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
484
						"relax: no control value expected\n" );
485
486
487
488
489
					usage();
				}

				manageDIT = 1 + crit;

490
491
492
			} else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
				if( manageDSAit ) {
					fprintf( stderr,
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
493
						"manageDSAit control previously specified\n");
494
495
496
497
					exit( EXIT_FAILURE );
				}
				if( cvalue != NULL ) {
					fprintf( stderr,
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
498
						"manageDSAit: no control value expected\n" );
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
					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;

516
#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
Howard Chu's avatar
Howard Chu committed
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
			} 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;
532
#endif
Howard Chu's avatar
Howard Chu committed
533

534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
			} else if ( strcasecmp( control, "preread" ) == 0 ) {
				if( preread ) {
					fprintf( stderr, "preread control previously specified\n");
					exit( EXIT_FAILURE );
				}

				preread = 1 + crit;
				preread_attrs = cvalue;

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

				postread = 1 + crit;
				postread_attrs = cvalue;

552
553
554
555
556
557
558
#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
559
					continuation = strchr( cvalue, '/' );
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
					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 */

598
599
			/* 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
600
				abcan = Intr_Abandon;
601
602
603
				if ( crit ) {
					gotintr = abcan;
				}
604
605

			} else if ( strcasecmp( control, "cancel" ) == 0 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
606
				abcan = Intr_Cancel;
607
608
609
610
611
				if ( crit ) {
					gotintr = abcan;
				}

			} else if ( strcasecmp( control, "ignore" ) == 0 ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
612
				abcan = Intr_Ignore;
613
614
615
				if ( crit ) {
					gotintr = abcan;
				}
616

617
618
619
620
621
622
623
624
625
626
627
628
#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;
#endif /* LDAP_CONTROL_X_SESSION_TRACKING */

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

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
629
				tmpctrls = (LDAPControl *)ber_memrealloc( unknown_ctrls,
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
					(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;
				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
653
					if ( retcode == -1 || (unsigned) retcode > bv.bv_len ) {
654
655
656
657
658
659
660
661
662
663
664
665
						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++;

666
667
			} else {
				fprintf( stderr, "Invalid general control name: %s\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
668
					control );
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
				usage();
			}
			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
697
698
					"authentication choice\n",
					prog );
699
700
701
702
703
704
705
				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
706
				prog );
707
708
709
710
			exit( EXIT_FAILURE );
#endif
		case 'M':
			/* enable Manage DSA IT */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
711
			manageDSAit++;
712
713
			break;
		case 'n':	/* print operations, don't actually do them */
714
			dont++;
715
			break;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
716
717
718
		case 'N':
			nocanon++;
			break;
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
		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
748
749
		 			fprintf( stderr, _("%s: invalid network timeout (%ld) specified\n"),
		 				prog, (long)nettimeout.tv_sec );
750
751
752
753
754
755
756
757
	 				exit( EXIT_FAILURE );
 				}
			} else {
				fprintf( stderr, "Invalid general option name: %s\n",
					control );
				usage();
			}
			break;
758
759
760
761
762
763
764
765
		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
766
					"authentication choice\n", prog );
767
768
769
770
771
				exit( EXIT_FAILURE );
			}
			authmethod = LDAP_AUTH_SASL;
			sasl_secprops = ber_strdup( optarg );
#else
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
772
			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
773
774
775
776
777
778
779
780
			exit( EXIT_FAILURE );
#endif
			break;
		case 'p':
			if( ldapport ) {
				fprintf( stderr, "%s: -p previously specified\n", prog );
				exit( EXIT_FAILURE );
			}
781
782
783
784
785
786
			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;
787
788
			break;
		case 'P':
789
790
			ival = strtol( optarg, &next, 10 );
			if ( next == NULL || next[0] != '\0' ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
791
				fprintf( stderr, "%s: unable to parse protocol version \"%s\"\n", prog, optarg );
792
793
794
				exit( EXIT_FAILURE );
			}
			switch( ival ) {
795
			case 2:
796
				if( protocol == LDAP_VERSION3 ) {
797
					fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
798
						prog, protocol );
799
800
					exit( EXIT_FAILURE );
				}
801
				protocol = LDAP_VERSION2;
802
803
				break;
			case 3:
804
				if( protocol == LDAP_VERSION2 ) {
805
					fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
806
						prog, protocol );
807
808
					exit( EXIT_FAILURE );
				}
809
				protocol = LDAP_VERSION3;
810
811
812
				break;
			default:
				fprintf( stderr, "%s: protocol version should be 2 or 3\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
813
					prog );
814
815
816
817
818
819
820
				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
821
822
					"authentication choice\n",
					prog );
823
824
825
826
827
828
829
				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
830
				prog );
831
832
833
834
835
836
837
838
839
840
			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
841
842
					"authentication choice\n",
					prog );
843
844
845
846
847
848
				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
849
				prog );
850
851
852
853
854
855
856
857
858
859
860
			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
861
862
					"authentication choice\n",
					prog );
863
864
865
866
867
868
				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
869
				prog );
870
871
872
873
			exit( EXIT_FAILURE );
#endif
			break;
		case 'v':	/* verbose mode */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
874
			verbose++;
875
			break;
876
877
878
		case 'V':	/* version */
			version++;
			break;
879
880
881
882
883
884
885
886
887
888
889
890
		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
891
			want_bindpw++;
892
893
894
895
896
897
898
899
900
901
902
			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
903
904
				fprintf( stderr,
					"%s: incompatible with authentication choice\n", prog );
905
906
907
908
909
				exit( EXIT_FAILURE );
			}
			authmethod = LDAP_AUTH_SASL;
			sasl_mech = ber_strdup( optarg );
#else
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
910
			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
911
912
913
914
915
916
			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
917
					"authentication choice\n", prog );
918
919
920
921
922
923
924
925
926
927
928
929
				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
930
					"authentication choice\n", prog );
931
932
933
934
935
936
937
938
939
940
941
				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
942
			use_tls++;
943
944
945
946
947
948
#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
949
			if( handle_private_option( i ) ) break;
950
			fprintf( stderr, "%s: unrecognized option -%c\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
951
				prog, optopt );
952
953
			usage();
		}
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
954
	}
955

956
957
958
959
960
	{
		/* prevent bad linking */
		LDAPAPIInfo api;
		api.ldapai_info_version = LDAP_API_INFO_VERSION;

961
962
963
		if ( ldap_get_option(NULL, LDAP_OPT_API_INFO, &api)
			!= LDAP_OPT_SUCCESS )
		{
964
965
966
967
968
969
			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
970
				"library %d, header %d\n",
971
972
				api.ldapai_info_version, LDAP_API_INFO_VERSION );
			exit( EXIT_FAILURE );
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
973
		}
974
975
976

		if( api.ldapai_api_version != LDAP_API_VERSION ) {
			fprintf( stderr, "LDAP API version mismatch: "
Kurt Zeilenga's avatar
Kurt Zeilenga committed
977
				"library %d, header %d\n",
978
979
980
981
982
983
				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
984
				"library %s, header %s\n",
985
986
987
988
989
990
				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
991
				"library %d, header %d\n",
992
993
994
995
996
997
998
999
1000
				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 );