1/*	$NetBSD: common.c,v 1.10 2021/08/14 16:14:49 christos Exp $	*/
2
3/* common.c - common routines for the ldap client tools */
4/* $OpenLDAP$ */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 1998-2021 The OpenLDAP Foundation.
8 * Portions Copyright 2003 Kurt D. Zeilenga.
9 * Portions Copyright 2003 IBM Corporation.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted only as authorized by the OpenLDAP
14 * Public License.
15 *
16 * A copy of this license is available in the file LICENSE in the
17 * top-level directory of the distribution or, alternatively, at
18 * <http://www.OpenLDAP.org/license.html>.
19 */
20/* ACKNOWLEDGEMENTS:
21 * This file was initially created by Hallvard B. Furuseth based (in
22 * part) upon argument parsing code for individual tools located in
23 * this directory.   Additional contributors include:
24 *   Kurt D. Zeilenga (additional common argument and control support)
25 */
26
27#include <sys/cdefs.h>
28__RCSID("$NetBSD: common.c,v 1.10 2021/08/14 16:14:49 christos Exp $");
29
30#include "portable.h"
31
32#include <stdio.h>
33
34#include <ac/stdlib.h>
35#include <ac/signal.h>
36#include <ac/string.h>
37#include <ac/ctype.h>
38#include <ac/unistd.h>
39#include <ac/errno.h>
40#include <ac/time.h>
41#include <ac/socket.h>
42
43#ifdef HAVE_CYRUS_SASL
44#ifdef HAVE_SASL_SASL_H
45#include <sasl/sasl.h>
46#else
47#include <sasl.h>
48#endif
49#endif
50
51#include <ldap.h>
52
53#include "ldif.h"
54#include "lutil.h"
55#include "lutil_ldap.h"
56#include "ldap_defaults.h"
57#include "ldap_pvt.h"
58#include "lber_pvt.h"
59
60#include "common.h"
61
62/* input-related vars */
63
64/* misc. parameters */
65tool_type_t	tool_type;
66int		contoper = 0;
67int		debug = 0;
68char		*infile = NULL;
69int		dont = 0;
70int		nocanon = 0;
71int		referrals = 0;
72int		verbose = 0;
73int		ldif = 0;
74ber_len_t	ldif_wrap = 0;
75char		*prog = NULL;
76
77/* connection */
78char		*ldapuri = NULL;
79int		use_tls = 0;
80int		protocol = -1;
81int		version = 0;
82
83/* authc/authz */
84int		authmethod = -1;
85char		*binddn = NULL;
86int		want_bindpw = 0;
87struct berval	passwd = { 0, NULL };
88char		*pw_file = NULL;
89#ifdef HAVE_CYRUS_SASL
90unsigned	sasl_flags = LDAP_SASL_AUTOMATIC;
91char		*sasl_realm = NULL;
92char		*sasl_authc_id = NULL;
93char		*sasl_authz_id = NULL;
94char		*sasl_mech = NULL;
95char		*sasl_secprops = NULL;
96#endif
97
98/* controls */
99int		assertctl;
100char		*assertion = NULL;
101struct berval	assertionvalue = BER_BVNULL;
102char		*authzid = NULL;
103int		authzcrit = 1;
104/* support deprecated early version of proxyAuthz */
105#define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ	"2.16.840.1.113730.3.4.12"
106#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
107char		*proxydn = NULL;
108#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
109int		manageDIT = 0;
110int		manageDSAit = 0;
111int		noop = 0;
112int		ppolicy = 0;
113int		preread = 0;
114static char	*preread_attrs = NULL;
115int		postread = 0;
116static char	*postread_attrs = NULL;
117ber_int_t	pr_morePagedResults = 1;
118struct berval	pr_cookie = { 0, NULL };
119#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
120int		chaining = 0;
121static int	chainingResolve = -1;
122static int	chainingContinuation = -1;
123#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
124#ifdef LDAP_CONTROL_X_SESSION_TRACKING
125static int	sessionTracking = 0;
126static char	*sessionTrackingName;
127struct berval	stValue;
128#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
129ber_int_t vlvPos;
130ber_int_t vlvCount;
131struct berval *vlvContext;
132static int	bauthzid;
133
134LDAPControl	*unknown_ctrls = NULL;
135int		unknown_ctrls_num = 0;
136
137/* options */
138struct timeval	nettimeout = { -1 , 0 };
139
140typedef int (*print_ctrl_fn)( LDAP *ld, LDAPControl *ctrl );
141
142static int print_preread( LDAP *ld, LDAPControl *ctrl );
143static int print_postread( LDAP *ld, LDAPControl *ctrl );
144static int print_paged_results( LDAP *ld, LDAPControl *ctrl );
145static int print_psearch( LDAP *ld, LDAPControl *ctrl );
146#ifdef LDAP_CONTROL_AUTHZID_RESPONSE
147static int print_authzid( LDAP *ld, LDAPControl *ctrl );
148#endif
149#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
150static int print_ppolicy( LDAP *ld, LDAPControl *ctrl );
151#endif
152static int print_sss( LDAP *ld, LDAPControl *ctrl );
153static int print_vlv( LDAP *ld, LDAPControl *ctrl );
154#ifdef LDAP_CONTROL_X_DEREF
155static int print_deref( LDAP *ld, LDAPControl *ctrl );
156#endif
157#ifdef LDAP_CONTROL_X_WHATFAILED
158static int print_whatfailed( LDAP *ld, LDAPControl *ctrl );
159#endif
160static int print_syncstate( LDAP *ld, LDAPControl *ctrl );
161static int print_syncdone( LDAP *ld, LDAPControl *ctrl );
162#ifdef LDAP_CONTROL_X_DIRSYNC
163static int print_dirsync( LDAP *ld, LDAPControl *ctrl );
164#endif
165#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
166static int print_account_usability( LDAP *ld, LDAPControl *ctrl );
167#endif
168#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED
169static int print_netscape_pwexpired( LDAP *ld, LDAPControl *ctrl );
170static int print_netscape_pwexpiring( LDAP *ld, LDAPControl *ctrl );
171#endif
172
173static struct tool_ctrls_t {
174	const char	*oid;
175	unsigned	mask;
176	print_ctrl_fn	func;
177} tool_ctrl_response[] = {
178	{ LDAP_CONTROL_PRE_READ,			TOOL_ALL,	print_preread },
179	{ LDAP_CONTROL_POST_READ,			TOOL_ALL,	print_postread },
180	{ LDAP_CONTROL_PAGEDRESULTS,			TOOL_SEARCH,	print_paged_results },
181	{ LDAP_CONTROL_PERSIST_ENTRY_CHANGE_NOTICE,			TOOL_SEARCH,	print_psearch },
182#ifdef LDAP_CONTROL_AUTHZID_RESPONSE
183	/* this is generally deprecated in favor of LDAP WhoAmI? operation, hence only supported as a VC inner control */
184	{ LDAP_CONTROL_AUTHZID_RESPONSE,		TOOL_VC,	print_authzid },
185#endif
186#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
187	{ LDAP_CONTROL_PASSWORDPOLICYRESPONSE,		TOOL_ALL,	print_ppolicy },
188#endif
189	{ LDAP_CONTROL_SORTRESPONSE,	TOOL_SEARCH,	print_sss },
190	{ LDAP_CONTROL_VLVRESPONSE,		TOOL_SEARCH,	print_vlv },
191#ifdef LDAP_CONTROL_X_DEREF
192	{ LDAP_CONTROL_X_DEREF,				TOOL_SEARCH,	print_deref },
193#endif
194#ifdef LDAP_CONTROL_X_WHATFAILED
195	{ LDAP_CONTROL_X_WHATFAILED,			TOOL_ALL,	print_whatfailed },
196#endif
197	{ LDAP_CONTROL_SYNC_STATE,			TOOL_SEARCH,	print_syncstate },
198	{ LDAP_CONTROL_SYNC_DONE,			TOOL_SEARCH,	print_syncdone },
199#ifdef LDAP_CONTROL_X_DIRSYNC
200	{ LDAP_CONTROL_X_DIRSYNC,			TOOL_SEARCH,	print_dirsync },
201#endif
202#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
203	{ LDAP_CONTROL_X_ACCOUNT_USABILITY,		TOOL_SEARCH,	print_account_usability },
204#endif
205#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED
206	{ LDAP_CONTROL_X_PASSWORD_EXPIRED,		TOOL_ALL,	print_netscape_pwexpired },
207	{ LDAP_CONTROL_X_PASSWORD_EXPIRING,		TOOL_ALL,	print_netscape_pwexpiring },
208#endif
209	{ NULL,						0,		NULL }
210};
211
212/* "features" */
213enum { Intr_None = 0, Intr_Abandon, Intr_Cancel, Intr_Ignore };
214static volatile sig_atomic_t	gotintr, abcan;
215
216int backlog;
217
218
219#ifdef LDAP_CONTROL_X_SESSION_TRACKING
220static int
221st_value( LDAP *ld, struct berval *value )
222{
223	char		*ip = NULL, *name = NULL;
224	struct berval	id = { 0 };
225	char		namebuf[ MAXHOSTNAMELEN ];
226
227	if ( gethostname( namebuf, sizeof( namebuf ) ) == 0 ) {
228		struct hostent	*h;
229		struct in_addr	addr;
230
231		name = namebuf;
232
233		h = gethostbyname( name );
234		if ( h != NULL ) {
235			AC_MEMCPY( &addr, h->h_addr, sizeof( addr ) );
236			ip = inet_ntoa( addr );
237		}
238	}
239
240	if ( sessionTrackingName != NULL ) {
241		ber_str2bv( sessionTrackingName , 0, 0, &id );
242	} else
243#ifdef HAVE_CYRUS_SASL
244	if ( sasl_authz_id != NULL ) {
245		ber_str2bv( sasl_authz_id, 0, 0, &id );
246
247	} else if ( sasl_authc_id != NULL ) {
248		ber_str2bv( sasl_authc_id, 0, 0, &id );
249
250	} else
251#endif /* HAVE_CYRUS_SASL */
252	if ( binddn != NULL ) {
253		ber_str2bv( binddn, 0, 0, &id );
254	}
255
256	if ( ldap_create_session_tracking_value( ld,
257		ip, name, LDAP_CONTROL_X_SESSION_TRACKING_USERNAME,
258		&id, &stValue ) )
259	{
260		fprintf( stderr, _("Session tracking control encoding error!\n") );
261		return -1;
262	}
263
264	return 0;
265}
266#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
267
268RETSIGTYPE
269do_sig( int sig )
270{
271	gotintr = abcan;
272}
273
274void
275tool_init( tool_type_t type )
276{
277	tool_type = type;
278	ldap_pvt_setlocale(LC_MESSAGES, "");
279	ldap_pvt_bindtextdomain(OPENLDAP_PACKAGE, LDAP_LOCALEDIR);
280	ldap_pvt_textdomain(OPENLDAP_PACKAGE);
281}
282
283void
284tool_destroy( void )
285{
286	static int destroyed;
287	if ( destroyed++ )
288		return;
289
290#ifdef HAVE_CYRUS_SASL
291	sasl_done();
292#endif
293#ifdef HAVE_TLS
294	ldap_pvt_tls_destroy();
295#endif
296
297	if ( ldapuri != NULL ) {
298		ber_memfree( ldapuri );
299		ldapuri = NULL;
300	}
301
302	if ( pr_cookie.bv_val != NULL ) {
303		ber_memfree( pr_cookie.bv_val );
304		BER_BVZERO( &pr_cookie );
305	}
306
307	if ( passwd.bv_val != NULL ) {
308		ber_memfree( passwd.bv_val );
309		BER_BVZERO( &passwd );
310	}
311
312#ifdef LDAP_CONTROL_X_SESSION_TRACKING
313	if ( !BER_BVISNULL( &stValue ) ) {
314		ber_memfree( stValue.bv_val );
315		BER_BVZERO( &stValue );
316	}
317
318#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
319}
320
321void
322tool_common_usage( void )
323{
324	static const char *const descriptions[] = {
325N_("  -d level   set LDAP debugging level to `level'\n"),
326N_("  -D binddn  bind DN\n"),
327N_("  -e [!]<ext>[=<extparam>] general extensions (! indicates criticality)\n")
328N_("             [!]assert=<filter>     (RFC 4528; a RFC 4515 Filter string)\n")
329N_("             [!]authzid=<authzid>   (RFC 4370; \"dn:<dn>\" or \"u:<user>\")\n")
330N_("             [!]bauthzid            (RFC 3829)\n")
331#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
332#if 0
333                 /* non-advertized support for proxyDN */
334N_("             [!]proxydn=<dn>        (a RFC 4514 DN string)\n")
335#endif
336#endif
337#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
338N_("             [!]chaining[=<resolveBehavior>[/<continuationBehavior>]]\n")
339N_("                     one of \"chainingPreferred\", \"chainingRequired\",\n")
340N_("                     \"referralsPreferred\", \"referralsRequired\"\n")
341#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
342N_("             [!]manageDSAit         (RFC 3296)\n")
343N_("             [!]noop\n")
344#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
345N_("             ppolicy\n")
346#endif
347N_("             [!]postread[=<attrs>]  (RFC 4527; comma-separated attr list)\n")
348N_("             [!]preread[=<attrs>]   (RFC 4527; comma-separated attr list)\n")
349N_("             [!]relax\n")
350#ifdef LDAP_CONTROL_X_SESSION_TRACKING
351N_("             [!]sessiontracking[=<username>]\n")
352#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
353N_("             abandon, cancel, ignore (SIGINT sends abandon/cancel,\n"
354   "             or ignores response; if critical, doesn't wait for SIGINT.\n"
355   "             not really controls)\n")
356N_("  -H URI     LDAP Uniform Resource Identifier(s)\n"),
357N_("  -I         use SASL Interactive mode\n"),
358N_("  -n         show what would be done but don't actually do it\n"),
359N_("  -N         do not use reverse DNS to canonicalize SASL host name\n"),
360N_("  -O props   SASL security properties\n"),
361N_("  -o <opt>[=<optparam>] any libldap ldap.conf options, plus\n"),
362N_("             ldif_wrap=<width> (in columns, or \"no\" for no wrapping)\n"),
363N_("             nettimeout=<timeout> (in seconds, or \"none\" or \"max\")\n"),
364N_("  -Q         use SASL Quiet mode\n"),
365N_("  -R realm   SASL realm\n"),
366N_("  -U authcid SASL authentication identity\n"),
367N_("  -v         run in verbose mode (diagnostics to standard output)\n"),
368N_("  -V         print version info (-VV only)\n"),
369N_("  -w passwd  bind password (for simple authentication)\n"),
370N_("  -W         prompt for bind password\n"),
371N_("  -x         Simple authentication\n"),
372N_("  -X authzid SASL authorization identity (\"dn:<dn>\" or \"u:<user>\")\n"),
373N_("  -y file    Read password from file\n"),
374N_("  -Y mech    SASL mechanism\n"),
375N_("  -Z         Start TLS request (-ZZ to require successful response)\n"),
376NULL
377	};
378	const char *const *cpp;
379
380	fputs( _("Common options:\n"), stderr );
381	for( cpp = descriptions; *cpp != NULL; cpp++ ) {
382		if( strchr( options, (*cpp)[3] ) || (*cpp)[3] == ' ' ) {
383			fputs( _(*cpp), stderr );
384		}
385	}
386
387	tool_destroy();
388}
389
390void tool_perror(
391	const char *func,
392	int err,
393	const char *extra,
394	const char *matched,
395	const char *info,
396	char **refs )
397{
398	fprintf( stderr, "%s: %s (%d)%s\n",
399		func, ldap_err2string( err ), err, extra ? extra : "" );
400
401	if ( matched && *matched ) {
402		fprintf( stderr, _("\tmatched DN: %s\n"), matched );
403	}
404
405	if ( info && *info ) {
406		fprintf( stderr, _("\tadditional info: %s\n"), info );
407	}
408
409	if ( refs && *refs ) {
410		int i;
411		fprintf( stderr, _("\treferrals:\n") );
412		for( i=0; refs[i]; i++ ) {
413			fprintf( stderr, "\t\t%s\n", refs[i] );
414		}
415	}
416}
417
418
419void
420tool_args( int argc, char **argv )
421{
422	int i;
423
424	while (( i = getopt( argc, argv, options )) != EOF ) {
425		int crit, ival;
426		char *control, *cvalue, *next;
427		switch( i ) {
428		case 'c':	/* continuous operation mode */
429			contoper++;
430			break;
431		case 'C':	/* referrals: obsolete */
432			referrals++;
433			break;
434		case 'd':
435			ival = strtol( optarg, &next, 10 );
436			if (next == NULL || next[0] != '\0') {
437				fprintf( stderr, "%s: unable to parse debug value \"%s\"\n", prog, optarg);
438				exit(EXIT_FAILURE);
439			}
440			debug |= ival;
441			break;
442		case 'D':	/* bind DN */
443			if( binddn != NULL ) {
444				fprintf( stderr, "%s: -D previously specified\n", prog );
445				exit( EXIT_FAILURE );
446			}
447			binddn = optarg;
448			break;
449		case 'e':	/* general extensions (controls and such) */
450			/* should be extended to support comma separated list of
451			 *	[!]key[=value] parameters, e.g.  -e !foo,bar=567
452			 */
453
454			crit = 0;
455			cvalue = NULL;
456			while ( optarg[0] == '!' ) {
457				crit++;
458				optarg++;
459			}
460
461			control = optarg;
462			if ( (cvalue = strchr( control, '=' )) != NULL ) {
463				*cvalue++ = '\0';
464			}
465
466			if ( strcasecmp( control, "assert" ) == 0 ) {
467				if( assertctl ) {
468					fprintf( stderr, "assert control previously specified\n");
469					exit( EXIT_FAILURE );
470				}
471				if( cvalue == NULL ) {
472					fprintf( stderr, "assert: control value expected\n" );
473					usage();
474				}
475
476				assertctl = 1 + crit;
477
478				assert( assertion == NULL );
479				assertion = cvalue;
480
481			} else if ( strcasecmp( control, "authzid" ) == 0 ) {
482				if( authzid != NULL ) {
483					fprintf( stderr, "authzid control previously specified\n");
484					exit( EXIT_FAILURE );
485				}
486#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
487				if( proxydn != NULL ) {
488					fprintf( stderr, "authzid control incompatible with proxydn\n");
489					exit( EXIT_FAILURE );
490				}
491#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
492				if( cvalue == NULL ) {
493					fprintf( stderr, "authzid: control value expected\n" );
494					usage();
495				}
496				if( !crit ) {
497					fprintf( stderr, "authzid: must be marked critical\n" );
498					usage();
499				} else if ( crit > 1 ) {
500					/* purposely flag proxied authorization
501					 * as non-critical, to test DSA */
502					authzcrit = 0;
503				}
504
505				assert( authzid == NULL );
506				authzid = cvalue;
507
508#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
509			} else if ( strcasecmp( control, "proxydn" ) == 0 ) {
510				if( proxydn != NULL ) {
511					fprintf( stderr, "proxydn control previously specified\n");
512					exit( EXIT_FAILURE );
513				}
514				if( authzid != NULL ) {
515					fprintf( stderr, "proxydn control incompatible with authzid\n");
516					exit( EXIT_FAILURE );
517				}
518				if( cvalue == NULL ) {
519					fprintf( stderr, "proxydn: control value expected\n" );
520					usage();
521				}
522				if( !crit ) {
523					fprintf( stderr, "proxydn: must be marked critical\n" );
524					usage();
525				} else if ( crit > 1 ) {
526					/* purposely flag proxied authorization
527					 * as non-critical, to test DSA */
528					authzcrit = 0;
529				}
530
531				assert( proxydn == NULL );
532				proxydn = cvalue;
533#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
534
535			} else if ( strcasecmp( control, "bauthzid" ) == 0 ) {
536				if( bauthzid ) {
537					fprintf( stderr, "bauthzid control previously specified\n");
538					exit( EXIT_FAILURE );
539				}
540				if( cvalue != NULL ) {
541					fprintf( stderr, "bauthzid: no control value expected\n" );
542					usage();
543				}
544				bauthzid = 1 + crit;
545
546			} else if ( ( strcasecmp( control, "relax" ) == 0 ) ||
547				( strcasecmp( control, "manageDIT" ) == 0 ) )
548			{
549				if( manageDIT ) {
550					fprintf( stderr,
551						"relax control previously specified\n");
552					exit( EXIT_FAILURE );
553				}
554				if( cvalue != NULL ) {
555					fprintf( stderr,
556						"relax: no control value expected\n" );
557					usage();
558				}
559
560				manageDIT = 1 + crit;
561
562			} else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
563				if( manageDSAit ) {
564					fprintf( stderr,
565						"manageDSAit control previously specified\n");
566					exit( EXIT_FAILURE );
567				}
568				if( cvalue != NULL ) {
569					fprintf( stderr,
570						"manageDSAit: no control value expected\n" );
571					usage();
572				}
573
574				manageDSAit = 1 + crit;
575
576			} else if ( strcasecmp( control, "noop" ) == 0 ) {
577				if( noop ) {
578					fprintf( stderr, "noop control previously specified\n");
579					exit( EXIT_FAILURE );
580				}
581				if( cvalue != NULL ) {
582					fprintf( stderr, "noop: no control value expected\n" );
583					usage();
584				}
585
586				noop = 1 + crit;
587
588#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
589			} else if ( strcasecmp( control, "ppolicy" ) == 0 ) {
590				if( ppolicy ) {
591					fprintf( stderr, "ppolicy control previously specified\n");
592					exit( EXIT_FAILURE );
593				}
594				if( cvalue != NULL ) {
595					fprintf( stderr, "ppolicy: no control value expected\n" );
596					usage();
597				}
598				if( crit ) {
599					fprintf( stderr, "ppolicy: critical flag not allowed\n" );
600					usage();
601				}
602
603				ppolicy = 1;
604#endif
605
606			} else if ( strcasecmp( control, "preread" ) == 0 ) {
607				if( preread ) {
608					fprintf( stderr, "preread control previously specified\n");
609					exit( EXIT_FAILURE );
610				}
611
612				preread = 1 + crit;
613				preread_attrs = cvalue;
614
615			} else if ( strcasecmp( control, "postread" ) == 0 ) {
616				if( postread ) {
617					fprintf( stderr, "postread control previously specified\n");
618					exit( EXIT_FAILURE );
619				}
620
621				postread = 1 + crit;
622				postread_attrs = cvalue;
623
624#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
625			} else if ( strcasecmp( control, "chaining" ) == 0 ) {
626				if ( chaining ) {
627					fprintf( stderr, "chaining control previously specified\n");
628					exit( EXIT_FAILURE );
629				}
630
631				chaining = 1 + crit;
632
633				if ( cvalue != NULL ) {
634					char	*continuation;
635
636					continuation = strchr( cvalue, '/' );
637					if ( continuation ) {
638						/* FIXME: this makes sense only in searches */
639						*continuation++ = '\0';
640						if ( strcasecmp( continuation, "chainingPreferred" ) == 0 ) {
641							chainingContinuation = LDAP_CHAINING_PREFERRED;
642						} else if ( strcasecmp( continuation, "chainingRequired" ) == 0 ) {
643							chainingContinuation = LDAP_CHAINING_REQUIRED;
644						} else if ( strcasecmp( continuation, "referralsPreferred" ) == 0 ) {
645							chainingContinuation = LDAP_REFERRALS_PREFERRED;
646						} else if ( strcasecmp( continuation, "referralsRequired" ) == 0 ) {
647							chainingContinuation = LDAP_REFERRALS_REQUIRED;
648						} else {
649							fprintf( stderr,
650								"chaining behavior control "
651								"continuation value \"%s\" invalid\n",
652								continuation );
653							exit( EXIT_FAILURE );
654						}
655					}
656
657					if ( strcasecmp( cvalue, "chainingPreferred" ) == 0 ) {
658						chainingResolve = LDAP_CHAINING_PREFERRED;
659					} else if ( strcasecmp( cvalue, "chainingRequired" ) == 0 ) {
660						chainingResolve = LDAP_CHAINING_REQUIRED;
661					} else if ( strcasecmp( cvalue, "referralsPreferred" ) == 0 ) {
662						chainingResolve = LDAP_REFERRALS_PREFERRED;
663					} else if ( strcasecmp( cvalue, "referralsRequired" ) == 0 ) {
664						chainingResolve = LDAP_REFERRALS_REQUIRED;
665					} else {
666						fprintf( stderr,
667							"chaining behavior control "
668							"resolve value \"%s\" invalid\n",
669							cvalue);
670						exit( EXIT_FAILURE );
671					}
672				}
673#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
674
675#ifdef LDAP_CONTROL_X_SESSION_TRACKING
676			} else if ( strcasecmp( control, "sessiontracking" ) == 0 ) {
677				if ( sessionTracking ) {
678					fprintf( stderr, "%s: session tracking can be only specified once\n", prog );
679					exit( EXIT_FAILURE );
680				}
681				sessionTracking = 1;
682				if ( crit ) {
683					fprintf( stderr, "sessiontracking: critical flag not allowed\n" );
684					usage();
685				}
686				if ( cvalue ) {
687					sessionTrackingName = cvalue;
688				}
689#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
690
691			/* this shouldn't go here, really; but it's a feature... */
692			} else if ( strcasecmp( control, "abandon" ) == 0 ) {
693				abcan = Intr_Abandon;
694				if ( crit ) {
695					gotintr = abcan;
696				}
697
698			} else if ( strcasecmp( control, "cancel" ) == 0 ) {
699				abcan = Intr_Cancel;
700				if ( crit ) {
701					gotintr = abcan;
702				}
703
704			} else if ( strcasecmp( control, "ignore" ) == 0 ) {
705				abcan = Intr_Ignore;
706				if ( crit ) {
707					gotintr = abcan;
708				}
709
710			} else if ( strcasecmp( control, "backlog" ) == 0 ) {
711				/* special search: accumulate lots of responses
712				 * but don't read any, force slapd writer to wait.
713				 * Then abandon the search and issue a new one.
714				 */
715				backlog = 1;
716
717			} else if ( tool_is_oid( control ) ) {
718				LDAPControl	*tmpctrls, ctrl;
719
720				if ( unknown_ctrls != NULL ) {
721					int i;
722					for ( i = 0; unknown_ctrls[ i ].ldctl_oid != NULL; i++ ) {
723						if ( strcmp( control, unknown_ctrls[ i ].ldctl_oid ) == 0 ) {
724							fprintf( stderr, "%s control previously specified\n", control );
725							exit( EXIT_FAILURE );
726						}
727					}
728				}
729
730				tmpctrls = (LDAPControl *)ber_memrealloc( unknown_ctrls,
731					(unknown_ctrls_num + 1)*sizeof( LDAPControl ) );
732				if ( tmpctrls == NULL ) {
733					fprintf( stderr, "%s: no memory?\n", prog );
734					exit( EXIT_FAILURE );
735				}
736				unknown_ctrls = tmpctrls;
737				ctrl.ldctl_oid = control;
738				ctrl.ldctl_value.bv_val = NULL;
739				ctrl.ldctl_value.bv_len = 0;
740				ctrl.ldctl_iscritical = crit;
741
742				if ( cvalue != NULL ) {
743					struct berval	bv;
744					size_t		len = strlen( cvalue );
745					int		retcode;
746
747					bv.bv_len = LUTIL_BASE64_DECODE_LEN( len );
748					bv.bv_val = ber_memalloc( bv.bv_len + 1 );
749
750					retcode = lutil_b64_pton( cvalue,
751						(unsigned char *)bv.bv_val,
752						bv.bv_len );
753
754					if ( retcode == -1 || (unsigned) retcode > bv.bv_len ) {
755						fprintf( stderr, "Unable to parse value of general control %s\n",
756							control );
757						usage();
758					}
759
760					bv.bv_len = retcode;
761					ctrl.ldctl_value = bv;
762				}
763
764				/* don't free it */
765				control = NULL;
766				unknown_ctrls[ unknown_ctrls_num ] = ctrl;
767				unknown_ctrls_num++;
768
769			} else {
770				fprintf( stderr, "Invalid general control name: %s\n",
771					control );
772				usage();
773			}
774			break;
775		case 'f':	/* read from file */
776			if( infile != NULL ) {
777				fprintf( stderr, "%s: -f previously specified\n", prog );
778				exit( EXIT_FAILURE );
779			}
780			infile = optarg;
781			break;
782		case 'H':	/* ldap URI */
783			if( ldapuri != NULL ) {
784				fprintf( stderr, "%s: -H previously specified\n", prog );
785				exit( EXIT_FAILURE );
786			}
787			ldapuri = ber_strdup( optarg );
788			break;
789		case 'I':
790#ifdef HAVE_CYRUS_SASL
791			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
792				fprintf( stderr, "%s: incompatible previous "
793					"authentication choice\n",
794					prog );
795				exit( EXIT_FAILURE );
796			}
797			authmethod = LDAP_AUTH_SASL;
798			sasl_flags = LDAP_SASL_INTERACTIVE;
799			break;
800#else
801			fprintf( stderr, "%s: was not compiled with SASL support\n",
802				prog );
803			exit( EXIT_FAILURE );
804#endif
805		case 'M':
806			/* enable Manage DSA IT */
807			manageDSAit++;
808			break;
809		case 'n':	/* print operations, don't actually do them */
810			dont++;
811			break;
812		case 'N':
813			nocanon++;
814			break;
815		case 'o':
816			control = optarg;
817			if ( (cvalue = strchr( control, '=' )) != NULL ) {
818				*cvalue++ = '\0';
819			}
820			for ( next=control; *next; next++ ) {
821				if ( *next == '-' ) {
822					*next = '_';
823				}
824			}
825
826			if ( strcasecmp( control, "nettimeout" ) == 0 ) {
827				if( nettimeout.tv_sec != -1 ) {
828					fprintf( stderr, "nettimeout option previously specified\n");
829					exit( EXIT_FAILURE );
830				}
831				if( cvalue == NULL || cvalue[0] == '\0' ) {
832					fprintf( stderr, "nettimeout: option value expected\n" );
833					usage();
834				}
835		 		if ( strcasecmp( cvalue, "none" ) == 0 ) {
836		 			nettimeout.tv_sec = 0;
837		 		} else if ( strcasecmp( cvalue, "max" ) == 0 ) {
838		 			nettimeout.tv_sec = LDAP_MAXINT;
839		 		} else {
840		 			ival = strtol( cvalue, &next, 10 );
841		 			if ( next == NULL || next[0] != '\0' ) {
842		 				fprintf( stderr,
843		 					_("Unable to parse network timeout \"%s\"\n"), cvalue );
844		 				exit( EXIT_FAILURE );
845		 			}
846		 			nettimeout.tv_sec = ival;
847		 		}
848		 		if( nettimeout.tv_sec < 0 || nettimeout.tv_sec > LDAP_MAXINT ) {
849		 			fprintf( stderr, _("%s: invalid network timeout (%ld) specified\n"),
850		 				prog, (long)nettimeout.tv_sec );
851	 				exit( EXIT_FAILURE );
852 				}
853
854			} else if ( strcasecmp( control, "ldif_wrap" ) == 0 ) {
855				if ( cvalue == 0 ) {
856					ldif_wrap = LDIF_LINE_WIDTH;
857
858				} else if ( strcasecmp( cvalue, "no" ) == 0 ) {
859					ldif_wrap = LDIF_LINE_WIDTH_MAX;
860
861				} else {
862					unsigned int u;
863					if ( lutil_atou( &u, cvalue ) ) {
864						fprintf( stderr,
865							_("Unable to parse ldif_wrap=\"%s\"\n"), cvalue );
866		 				exit( EXIT_FAILURE );
867					}
868					ldif_wrap = (ber_len_t)u;
869				}
870
871			} else if ( ldap_pvt_conf_option( control, cvalue, 1 ) ) {
872				fprintf( stderr, "Invalid general option name: %s\n",
873					control );
874				usage();
875			}
876			break;
877		case 'O':
878#ifdef HAVE_CYRUS_SASL
879			if( sasl_secprops != NULL ) {
880				fprintf( stderr, "%s: -O previously specified\n", prog );
881				exit( EXIT_FAILURE );
882			}
883			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
884				fprintf( stderr, "%s: incompatible previous "
885					"authentication choice\n", prog );
886				exit( EXIT_FAILURE );
887			}
888			authmethod = LDAP_AUTH_SASL;
889			sasl_secprops = optarg;
890#else
891			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
892			exit( EXIT_FAILURE );
893#endif
894			break;
895		case 'P':
896			ival = strtol( optarg, &next, 10 );
897			if ( next == NULL || next[0] != '\0' ) {
898				fprintf( stderr, "%s: unable to parse protocol version \"%s\"\n", prog, optarg );
899				exit( EXIT_FAILURE );
900			}
901			switch( ival ) {
902			case 2:
903				if( protocol == LDAP_VERSION3 ) {
904					fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
905						prog, protocol );
906					exit( EXIT_FAILURE );
907				}
908				protocol = LDAP_VERSION2;
909				break;
910			case 3:
911				if( protocol == LDAP_VERSION2 ) {
912					fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
913						prog, protocol );
914					exit( EXIT_FAILURE );
915				}
916				protocol = LDAP_VERSION3;
917				break;
918			default:
919				fprintf( stderr, "%s: protocol version should be 2 or 3\n",
920					prog );
921				usage();
922			}
923			break;
924		case 'Q':
925#ifdef HAVE_CYRUS_SASL
926			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
927				fprintf( stderr, "%s: incompatible previous "
928					"authentication choice\n",
929					prog );
930				exit( EXIT_FAILURE );
931			}
932			authmethod = LDAP_AUTH_SASL;
933			sasl_flags = LDAP_SASL_QUIET;
934			break;
935#else
936			fprintf( stderr, "%s: not compiled with SASL support\n",
937				prog );
938			exit( EXIT_FAILURE );
939#endif
940		case 'R':
941#ifdef HAVE_CYRUS_SASL
942			if( sasl_realm != NULL ) {
943				fprintf( stderr, "%s: -R previously specified\n", prog );
944				exit( EXIT_FAILURE );
945			}
946			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
947				fprintf( stderr, "%s: incompatible previous "
948					"authentication choice\n",
949					prog );
950				exit( EXIT_FAILURE );
951			}
952			authmethod = LDAP_AUTH_SASL;
953			sasl_realm = optarg;
954#else
955			fprintf( stderr, "%s: not compiled with SASL support\n",
956				prog );
957			exit( EXIT_FAILURE );
958#endif
959			break;
960		case 'U':
961#ifdef HAVE_CYRUS_SASL
962			if( sasl_authc_id != NULL ) {
963				fprintf( stderr, "%s: -U previously specified\n", prog );
964				exit( EXIT_FAILURE );
965			}
966			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
967				fprintf( stderr, "%s: incompatible previous "
968					"authentication choice\n",
969					prog );
970				exit( EXIT_FAILURE );
971			}
972			authmethod = LDAP_AUTH_SASL;
973			sasl_authc_id = optarg;
974#else
975			fprintf( stderr, "%s: not compiled with SASL support\n",
976				prog );
977			exit( EXIT_FAILURE );
978#endif
979			break;
980		case 'v':	/* verbose mode */
981			verbose++;
982			break;
983		case 'V':	/* version */
984			version++;
985			break;
986		case 'w':	/* password */
987			passwd.bv_val = ber_strdup( optarg );
988			{
989				char* p;
990
991				for( p = optarg; *p != '\0'; p++ ) {
992					*p = '\0';
993				}
994			}
995			passwd.bv_len = strlen( passwd.bv_val );
996			break;
997		case 'W':
998			want_bindpw++;
999			break;
1000		case 'y':
1001			pw_file = optarg;
1002			break;
1003		case 'Y':
1004#ifdef HAVE_CYRUS_SASL
1005			if( sasl_mech != NULL ) {
1006				fprintf( stderr, "%s: -Y previously specified\n", prog );
1007				exit( EXIT_FAILURE );
1008			}
1009			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
1010				fprintf( stderr,
1011					"%s: incompatible with authentication choice\n", prog );
1012				exit( EXIT_FAILURE );
1013			}
1014			authmethod = LDAP_AUTH_SASL;
1015			sasl_mech = optarg;
1016#else
1017			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
1018			exit( EXIT_FAILURE );
1019#endif
1020			break;
1021		case 'x':
1022			if( authmethod != -1 && authmethod != LDAP_AUTH_SIMPLE ) {
1023				fprintf( stderr, "%s: incompatible with previous "
1024					"authentication choice\n", prog );
1025				exit( EXIT_FAILURE );
1026			}
1027			authmethod = LDAP_AUTH_SIMPLE;
1028			break;
1029		case 'X':
1030#ifdef HAVE_CYRUS_SASL
1031			if( sasl_authz_id != NULL ) {
1032				fprintf( stderr, "%s: -X previously specified\n", prog );
1033				exit( EXIT_FAILURE );
1034			}
1035			if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
1036				fprintf( stderr, "%s: -X incompatible with "
1037					"authentication choice\n", prog );
1038				exit( EXIT_FAILURE );
1039			}
1040			authmethod = LDAP_AUTH_SASL;
1041			sasl_authz_id = optarg;
1042#else
1043			fprintf( stderr, "%s: not compiled with SASL support\n", prog );
1044			exit( EXIT_FAILURE );
1045#endif
1046			break;
1047		case 'Z':
1048#ifdef HAVE_TLS
1049			use_tls++;
1050#else
1051			fprintf( stderr, "%s: not compiled with TLS support\n", prog );
1052			exit( EXIT_FAILURE );
1053#endif
1054			break;
1055		default:
1056			if( handle_private_option( i ) ) break;
1057			fprintf( stderr, "%s: unrecognized option -%c\n",
1058				prog, optopt );
1059			usage();
1060		}
1061	}
1062
1063	{
1064		/* prevent bad linking */
1065		LDAPAPIInfo api;
1066		api.ldapai_info_version = LDAP_API_INFO_VERSION;
1067
1068		if ( ldap_get_option(NULL, LDAP_OPT_API_INFO, &api)
1069			!= LDAP_OPT_SUCCESS )
1070		{
1071			fprintf( stderr, "%s: ldap_get_option(API_INFO) failed\n", prog );
1072			exit( EXIT_FAILURE );
1073		}
1074
1075		if (api.ldapai_info_version != LDAP_API_INFO_VERSION) {
1076			fprintf( stderr, "LDAP APIInfo version mismatch: "
1077				"library %d, header %d\n",
1078				api.ldapai_info_version, LDAP_API_INFO_VERSION );
1079			exit( EXIT_FAILURE );
1080		}
1081
1082		if( api.ldapai_api_version != LDAP_API_VERSION ) {
1083			fprintf( stderr, "LDAP API version mismatch: "
1084				"library %d, header %d\n",
1085				api.ldapai_api_version, LDAP_API_VERSION );
1086			exit( EXIT_FAILURE );
1087		}
1088
1089		if( strcmp(api.ldapai_vendor_name, LDAP_VENDOR_NAME ) != 0 ) {
1090			fprintf( stderr, "LDAP vendor name mismatch: "
1091				"library %s, header %s\n",
1092				api.ldapai_vendor_name, LDAP_VENDOR_NAME );
1093			exit( EXIT_FAILURE );
1094		}
1095
1096		if( api.ldapai_vendor_version != LDAP_VENDOR_VERSION ) {
1097			fprintf( stderr, "LDAP vendor version mismatch: "
1098				"library %d, header %d\n",
1099				api.ldapai_vendor_version, LDAP_VENDOR_VERSION );
1100			exit( EXIT_FAILURE );
1101		}
1102
1103		if (version) {
1104			fprintf( stderr, "%s: %s\t(LDAP library: %s %d)\n",
1105				prog, __Version,
1106				LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION );
1107			if (version > 1) exit( EXIT_SUCCESS );
1108		}
1109
1110		ldap_memfree( api.ldapai_vendor_name );
1111		ber_memvfree( (void **)api.ldapai_extensions );
1112	}
1113
1114	if (protocol == -1)
1115		protocol = LDAP_VERSION3;
1116
1117	if (authmethod == -1 && protocol > LDAP_VERSION2) {
1118#ifdef HAVE_CYRUS_SASL
1119		if ( binddn != NULL ) {
1120			authmethod = LDAP_AUTH_SIMPLE;
1121		} else {
1122			authmethod = LDAP_AUTH_SASL;
1123		}
1124#else
1125		authmethod = LDAP_AUTH_SIMPLE;
1126#endif
1127	}
1128
1129	if( protocol == LDAP_VERSION2 ) {
1130		if( assertctl || authzid || manageDIT || manageDSAit ||
1131#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
1132			proxydn ||
1133#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
1134#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1135			chaining ||
1136#endif
1137#ifdef LDAP_CONTROL_X_SESSION_TRACKING
1138			sessionTracking ||
1139#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
1140			noop || ppolicy || preread || postread )
1141		{
1142			fprintf( stderr, "%s: -e/-M incompatible with LDAPv2\n", prog );
1143			exit( EXIT_FAILURE );
1144		}
1145#ifdef HAVE_TLS
1146		if( use_tls ) {
1147			fprintf( stderr, "%s: -Z incompatible with LDAPv2\n", prog );
1148			exit( EXIT_FAILURE );
1149		}
1150#endif
1151#ifdef HAVE_CYRUS_SASL
1152		if( authmethod == LDAP_AUTH_SASL ) {
1153			fprintf( stderr, "%s: -[IOQRUXY] incompatible with LDAPv2\n",
1154				prog );
1155			exit( EXIT_FAILURE );
1156		}
1157#endif
1158	}
1159
1160	if ( ( pw_file || want_bindpw ) && !BER_BVISNULL( &passwd ) ) {
1161		fprintf( stderr, "%s: -%c incompatible with -w\n",
1162			prog, ( pw_file ? 'y' : 'W' ) );
1163		exit( EXIT_FAILURE );
1164	}
1165}
1166
1167
1168LDAP *
1169tool_conn_setup( int dont, void (*private_setup)( LDAP * ) )
1170{
1171	LDAP *ld = NULL;
1172
1173	if ( debug ) {
1174		if( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug )
1175			!= LBER_OPT_SUCCESS )
1176		{
1177			fprintf( stderr,
1178				"Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug );
1179		}
1180		if( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug )
1181			!= LDAP_OPT_SUCCESS )
1182		{
1183			fprintf( stderr,
1184				"Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug );
1185		}
1186	}
1187
1188#ifdef SIGPIPE
1189	(void) SIGNAL( SIGPIPE, SIG_IGN );
1190#endif
1191
1192	if ( abcan ) {
1193		SIGNAL( SIGINT, do_sig );
1194	}
1195
1196	if ( !dont ) {
1197		int rc;
1198
1199		if ( ldapuri != NULL ) {
1200			LDAPURLDesc	*ludlist, **ludp;
1201			char		**urls = NULL;
1202			int		nurls = 0;
1203
1204			rc = ldap_url_parselist( &ludlist, ldapuri );
1205			if ( rc != LDAP_URL_SUCCESS ) {
1206				fprintf( stderr,
1207					"Could not parse LDAP URI(s)=%s (%d)\n",
1208					ldapuri, rc );
1209				exit( EXIT_FAILURE );
1210			}
1211
1212			for ( ludp = &ludlist; *ludp != NULL; ) {
1213				LDAPURLDesc	*lud = *ludp;
1214				char		**tmp;
1215
1216				if ( lud->lud_dn != NULL && lud->lud_dn[ 0 ] != '\0' &&
1217					( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) )
1218				{
1219					/* if no host but a DN is provided,
1220					 * use DNS SRV to gather the host list
1221					 * and turn it into a list of URIs
1222					 * using the scheme provided */
1223					char	*domain = NULL,
1224						*hostlist = NULL,
1225						**hosts = NULL;
1226					int	i,
1227						len_proto = strlen( lud->lud_scheme );
1228
1229					if ( ldap_dn2domain( lud->lud_dn, &domain )
1230						|| domain == NULL )
1231					{
1232						fprintf( stderr,
1233							"DNS SRV: Could not turn "
1234							"DN=\"%s\" into a domain\n",
1235							lud->lud_dn );
1236						goto dnssrv_free;
1237					}
1238
1239					rc = ldap_domain2hostlist( domain, &hostlist );
1240					if ( rc ) {
1241						fprintf( stderr,
1242							"DNS SRV: Could not turn "
1243							"domain=%s into a hostlist\n",
1244							domain );
1245						goto dnssrv_free;
1246					}
1247
1248					hosts = ldap_str2charray( hostlist, " " );
1249					if ( hosts == NULL ) {
1250						fprintf( stderr,
1251							"DNS SRV: Could not parse "
1252							"hostlist=\"%s\"\n",
1253							hostlist );
1254						goto dnssrv_free;
1255					}
1256
1257					for ( i = 0; hosts[ i ] != NULL; i++ )
1258						/* count'em */ ;
1259
1260					tmp = (char **)ber_memrealloc( urls, sizeof( char * ) * ( nurls + i + 1 ) );
1261					if ( tmp == NULL ) {
1262						fprintf( stderr,
1263							"DNS SRV: out of memory?\n" );
1264						goto dnssrv_free;
1265					}
1266					urls = tmp;
1267					urls[ nurls ] = NULL;
1268
1269					for ( i = 0; hosts[ i ] != NULL; i++ ) {
1270						size_t	len = len_proto
1271							+ STRLENOF( "://" )
1272							+ strlen( hosts[ i ] )
1273							+ 1;
1274
1275						urls[ nurls + i + 1 ] = NULL;
1276						urls[ nurls + i ] = (char *)malloc( sizeof( char ) * len );
1277						if ( urls[ nurls + i ] == NULL ) {
1278							fprintf( stderr,
1279								"DNS SRV: out of memory?\n" );
1280							goto dnssrv_free;
1281						}
1282
1283						snprintf( urls[ nurls + i ], len, "%s://%s",
1284							lud->lud_scheme, hosts[ i ] );
1285					}
1286					nurls += i;
1287
1288dnssrv_free:;
1289					ber_memvfree( (void **)hosts );
1290					ber_memfree( hostlist );
1291					ber_memfree( domain );
1292
1293				} else {
1294					tmp = (char **)ber_memrealloc( urls, sizeof( char * ) * ( nurls + 2 ) );
1295					if ( tmp == NULL ) {
1296						fprintf( stderr,
1297							"DNS SRV: out of memory?\n" );
1298						break;
1299					}
1300					urls = tmp;
1301					urls[ nurls + 1 ] = NULL;
1302
1303					urls[ nurls ] = ldap_url_desc2str( lud );
1304					if ( urls[ nurls ] == NULL ) {
1305						fprintf( stderr,
1306							"DNS SRV: out of memory?\n" );
1307						break;
1308					}
1309					nurls++;
1310				}
1311
1312				*ludp = lud->lud_next;
1313
1314				lud->lud_next = NULL;
1315				ldap_free_urldesc( lud );
1316			}
1317
1318			if ( ludlist != NULL ) {
1319				ldap_free_urllist( ludlist );
1320				exit( EXIT_FAILURE );
1321
1322			} else if ( urls == NULL ) {
1323				exit( EXIT_FAILURE );
1324			}
1325
1326			ldap_memfree( ldapuri );
1327			ldapuri = ldap_charray2str( urls, " " );
1328			ber_memvfree( (void **)urls );
1329		}
1330
1331		if ( verbose ) {
1332			fprintf( stderr, "ldap_initialize( %s )\n",
1333				ldapuri != NULL ? ldapuri : "<DEFAULT>" );
1334		}
1335		rc = ldap_initialize( &ld, ldapuri );
1336		if( rc != LDAP_SUCCESS ) {
1337			fprintf( stderr,
1338				"Could not create LDAP session handle for URI=%s (%d): %s\n",
1339				ldapuri, rc, ldap_err2string(rc) );
1340			exit( EXIT_FAILURE );
1341		}
1342
1343		if( private_setup ) private_setup( ld );
1344
1345		/* referrals: obsolete */
1346		if( ldap_set_option( ld, LDAP_OPT_REFERRALS,
1347			referrals ? LDAP_OPT_ON : LDAP_OPT_OFF ) != LDAP_OPT_SUCCESS )
1348		{
1349			fprintf( stderr, "Could not set LDAP_OPT_REFERRALS %s\n",
1350				referrals ? "on" : "off" );
1351			tool_exit( ld, EXIT_FAILURE );
1352		}
1353
1354#ifdef HAVE_CYRUS_SASL
1355		/* canon */
1356		if( nocanon ) {
1357			if( ldap_set_option( ld, LDAP_OPT_X_SASL_NOCANON,
1358				LDAP_OPT_ON ) != LDAP_OPT_SUCCESS )
1359			{
1360				fprintf( stderr, "Could not set LDAP_OPT_X_SASL_NOCANON on\n" );
1361				tool_exit( ld, EXIT_FAILURE );
1362			}
1363		}
1364#endif
1365		if( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &protocol )
1366			!= LDAP_OPT_SUCCESS )
1367		{
1368			fprintf( stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n",
1369				protocol );
1370			tool_exit( ld, EXIT_FAILURE );
1371		}
1372
1373#ifdef HAVE_TLS
1374		if ( use_tls ) {
1375			rc = ldap_start_tls_s( ld, NULL, NULL );
1376			if ( rc != LDAP_SUCCESS ) {
1377				char *msg=NULL;
1378				ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&msg);
1379				tool_perror( "ldap_start_tls", rc, NULL, NULL, msg, NULL );
1380				ldap_memfree(msg);
1381				if ( use_tls > 1 || rc < 0 ) {
1382					tool_exit( ld, EXIT_FAILURE );
1383				}
1384			}
1385		}
1386#endif
1387
1388		if ( nettimeout.tv_sec > 0 ) {
1389	 		if ( ldap_set_option( ld, LDAP_OPT_NETWORK_TIMEOUT, (void *) &nettimeout )
1390				!= LDAP_OPT_SUCCESS )
1391			{
1392		 		fprintf( stderr, "Could not set LDAP_OPT_NETWORK_TIMEOUT %ld\n",
1393					(long)nettimeout.tv_sec );
1394	 			tool_exit( ld, EXIT_FAILURE );
1395			}
1396		}
1397	}
1398
1399	return ld;
1400}
1401
1402
1403void
1404tool_bind( LDAP *ld )
1405{
1406	LDAPControl	**sctrlsp = NULL;
1407	LDAPControl	*sctrls[4];
1408	LDAPControl	sctrl[3];
1409	int		nsctrls = 0;
1410
1411	int rc, msgid;
1412	LDAPMessage *result = NULL;
1413
1414	int err;
1415	char *matched = NULL;
1416	char *info = NULL;
1417	char **refs = NULL;
1418	LDAPControl **ctrls = NULL;
1419	char msgbuf[256];
1420
1421	msgbuf[0] = 0;
1422
1423#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
1424	if ( ppolicy ) {
1425		LDAPControl c;
1426		c.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
1427		c.ldctl_value.bv_val = NULL;
1428		c.ldctl_value.bv_len = 0;
1429		c.ldctl_iscritical = 0;
1430		sctrl[nsctrls] = c;
1431		sctrls[nsctrls] = &sctrl[nsctrls];
1432		sctrls[++nsctrls] = NULL;
1433	}
1434#endif
1435
1436	if ( bauthzid ) {
1437		LDAPControl c;
1438
1439		c.ldctl_oid = LDAP_CONTROL_AUTHZID_REQUEST;
1440		c.ldctl_iscritical = bauthzid > 1;
1441		BER_BVZERO( &c.ldctl_value );
1442
1443		sctrl[nsctrls] = c;
1444		sctrls[nsctrls] = &sctrl[nsctrls];
1445		sctrls[++nsctrls] = NULL;
1446	}
1447
1448#ifdef LDAP_CONTROL_X_SESSION_TRACKING
1449	if ( sessionTracking ) {
1450		LDAPControl c;
1451
1452		if ( BER_BVISNULL( &stValue) && st_value( ld, &stValue ) ) {
1453			tool_exit( ld, EXIT_FAILURE );
1454		}
1455
1456		c.ldctl_oid = LDAP_CONTROL_X_SESSION_TRACKING;
1457		c.ldctl_iscritical = 0;
1458		c.ldctl_value = stValue;
1459
1460		sctrl[nsctrls] = c;
1461		sctrls[nsctrls] = &sctrl[nsctrls];
1462		sctrls[++nsctrls] = NULL;
1463	}
1464#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
1465
1466	if ( nsctrls ) {
1467		sctrlsp = sctrls;
1468	}
1469
1470	assert( nsctrls < (int) (sizeof(sctrls)/sizeof(sctrls[0])) );
1471
1472	if ( pw_file || want_bindpw ) {
1473		assert( passwd.bv_val == NULL && passwd.bv_len == 0 );
1474
1475		if ( pw_file ) {
1476			if ( lutil_get_filed_password( pw_file, &passwd ) ) {
1477				tool_exit( ld, EXIT_FAILURE );
1478			}
1479
1480		} else {
1481			char *pw = getpassphrase( _("Enter LDAP Password: ") );
1482			if ( pw ) {
1483				passwd.bv_val = ber_strdup( pw );
1484				passwd.bv_len = strlen( passwd.bv_val );
1485			}
1486		}
1487	}
1488
1489	if ( authmethod == LDAP_AUTH_SASL ) {
1490#ifdef HAVE_CYRUS_SASL
1491		void *defaults;
1492		const char *rmech = NULL;
1493
1494		if( sasl_secprops != NULL ) {
1495			rc = ldap_set_option( ld, LDAP_OPT_X_SASL_SECPROPS,
1496				(void *) sasl_secprops );
1497
1498			if( rc != LDAP_OPT_SUCCESS ) {
1499				fprintf( stderr,
1500					"Could not set LDAP_OPT_X_SASL_SECPROPS: %s\n",
1501					sasl_secprops );
1502				tool_exit( ld, LDAP_LOCAL_ERROR );
1503			}
1504		}
1505
1506		defaults = lutil_sasl_defaults( ld,
1507			sasl_mech,
1508			sasl_realm,
1509			sasl_authc_id,
1510			passwd.bv_val,
1511			sasl_authz_id );
1512
1513		do {
1514			rc = ldap_sasl_interactive_bind( ld, binddn, sasl_mech,
1515				sctrlsp, NULL, sasl_flags, lutil_sasl_interact, defaults,
1516				result, &rmech, &msgid );
1517
1518			if ( rc != LDAP_SASL_BIND_IN_PROGRESS )
1519				break;
1520
1521			ldap_msgfree( result );
1522
1523			if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
1524				ldap_get_option( ld, LDAP_OPT_RESULT_CODE, (void*)&err );
1525				ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&info );
1526				tool_perror( "ldap_sasl_interactive_bind",
1527					err, NULL, NULL, info, NULL );
1528				ldap_memfree( info );
1529				tool_exit( ld, err );
1530			}
1531		} while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
1532
1533		lutil_sasl_freedefs( defaults );
1534
1535		if ( rc != LDAP_SUCCESS ) {
1536			ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&info );
1537			tool_perror( "ldap_sasl_interactive_bind",
1538				rc, NULL, NULL, info, NULL );
1539			ldap_memfree( info );
1540			tool_exit( ld, rc );
1541		}
1542#else
1543		fprintf( stderr, "%s: not compiled with SASL support\n", prog );
1544		tool_exit( ld, LDAP_NOT_SUPPORTED );
1545#endif
1546	} else {
1547		/* simple bind */
1548		rc = ldap_sasl_bind( ld, binddn, LDAP_SASL_SIMPLE, &passwd,
1549			sctrlsp, NULL, &msgid );
1550		if ( msgid == -1 ) {
1551			tool_perror( "ldap_sasl_bind(SIMPLE)", rc,
1552				NULL, NULL, NULL, NULL );
1553			tool_exit( ld, rc );
1554		}
1555
1556		rc = ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result );
1557		if ( rc == -1 ) {
1558			tool_perror( "ldap_result", -1, NULL, NULL, NULL, NULL );
1559			tool_exit( ld, LDAP_LOCAL_ERROR );
1560		}
1561
1562		if ( rc == 0 ) {
1563			tool_perror( "ldap_result", LDAP_TIMEOUT, NULL, NULL, NULL, NULL );
1564			tool_exit( ld, LDAP_LOCAL_ERROR );
1565		}
1566	}
1567
1568	if ( result ) {
1569		rc = ldap_parse_result( ld, result, &err, &matched, &info, &refs,
1570		                        &ctrls, 1 );
1571		if ( rc != LDAP_SUCCESS ) {
1572			tool_perror( "ldap_bind parse result", rc, NULL, matched, info, refs );
1573			tool_exit( ld, LDAP_LOCAL_ERROR );
1574		}
1575	}
1576
1577#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
1578	if ( ctrls && ppolicy ) {
1579		LDAPControl *ctrl;
1580		int expire, grace, len = 0;
1581		LDAPPasswordPolicyError pErr = -1;
1582
1583		ctrl = ldap_control_find( LDAP_CONTROL_PASSWORDPOLICYRESPONSE,
1584			ctrls, NULL );
1585
1586		if ( ctrl && ldap_parse_passwordpolicy_control( ld, ctrl,
1587			&expire, &grace, &pErr ) == LDAP_SUCCESS )
1588		{
1589			if ( pErr != PP_noError ){
1590				msgbuf[0] = ';';
1591				msgbuf[1] = ' ';
1592				strcpy( msgbuf+2, ldap_passwordpolicy_err2txt( pErr ));
1593				len = strlen( msgbuf );
1594			}
1595			if ( expire >= 0 ) {
1596				sprintf( msgbuf+len,
1597					" (Password expires in %d seconds)",
1598					expire );
1599			} else if ( grace >= 0 ) {
1600				sprintf( msgbuf+len,
1601					" (Password expired, %d grace logins remain)",
1602					grace );
1603			}
1604		}
1605	}
1606#endif
1607
1608	if ( ctrls && bauthzid ) {
1609		LDAPControl *ctrl;
1610
1611		ctrl = ldap_control_find( LDAP_CONTROL_AUTHZID_RESPONSE,
1612			ctrls, NULL );
1613		if ( ctrl ) {
1614			LDAPControl *ctmp[2];
1615			ctmp[0] = ctrl;
1616			ctmp[1] = NULL;
1617			tool_print_ctrls( ld, ctmp );
1618		}
1619	}
1620
1621#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED
1622	if ( ctrls ) {
1623		LDAPControl *ctrl;
1624		ctrl = ldap_control_find( LDAP_CONTROL_X_PASSWORD_EXPIRED,
1625			ctrls, NULL );
1626		if ( !ctrl )
1627			ctrl = ldap_control_find( LDAP_CONTROL_X_PASSWORD_EXPIRING,
1628				ctrls, NULL );
1629		if ( ctrl ) {
1630			LDAPControl *ctmp[2];
1631			ctmp[0] = ctrl;
1632			ctmp[1] = NULL;
1633			tool_print_ctrls( ld, ctmp );
1634		}
1635	}
1636#endif
1637
1638	if ( ctrls ) {
1639		ldap_controls_free( ctrls );
1640	}
1641
1642	if ( err != LDAP_SUCCESS
1643		|| msgbuf[0]
1644		|| ( matched && matched[ 0 ] )
1645		|| ( info && info[ 0 ] )
1646		|| refs )
1647	{
1648		tool_perror( "ldap_bind", err, msgbuf, matched, info, refs );
1649
1650		if( matched ) ber_memfree( matched );
1651		if( info ) ber_memfree( info );
1652		if( refs ) ber_memvfree( (void **)refs );
1653
1654		if ( err != LDAP_SUCCESS ) tool_exit( ld, err );
1655	}
1656}
1657
1658void
1659tool_unbind( LDAP *ld )
1660{
1661	int err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL );
1662
1663	if ( err != LDAP_OPT_SUCCESS ) {
1664		fprintf( stderr, "Could not unset controls\n");
1665	}
1666
1667	(void) ldap_unbind_ext( ld, NULL, NULL );
1668}
1669
1670void
1671tool_exit( LDAP *ld, int status )
1672{
1673	if ( ld != NULL ) {
1674		tool_unbind( ld );
1675	}
1676	tool_destroy();
1677	exit( status );
1678}
1679
1680
1681/* Set server controls.  Add controls extra_c[0..count-1], if set. */
1682void
1683tool_server_controls( LDAP *ld, LDAPControl *extra_c, int count )
1684{
1685	int i = 0, j, crit = 0, err;
1686	LDAPControl c[16], **ctrls;
1687
1688	if ( ! ( assertctl
1689		|| authzid
1690#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
1691		|| proxydn
1692#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
1693		|| manageDIT
1694		|| manageDSAit
1695		|| noop
1696#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
1697		|| ppolicy
1698#endif
1699		|| preread
1700		|| postread
1701#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1702		|| chaining
1703#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1704#ifdef LDAP_CONTROL_X_SESSION_TRACKING
1705		|| sessionTracking
1706#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
1707		|| count
1708		|| unknown_ctrls_num ) )
1709	{
1710		return;
1711	}
1712
1713	ctrls = (LDAPControl**) malloc(sizeof(c) + (count + unknown_ctrls_num + 1)*sizeof(LDAPControl*));
1714	if ( ctrls == NULL ) {
1715		fprintf( stderr, "No memory\n" );
1716		tool_exit( ld, EXIT_FAILURE );
1717	}
1718
1719	if ( assertctl ) {
1720		if ( BER_BVISNULL( &assertionvalue ) ) {
1721			err = ldap_create_assertion_control_value( ld,
1722				assertion, &assertionvalue );
1723			if ( err ) {
1724				fprintf( stderr,
1725					"Unable to create assertion value "
1726					"\"%s\" (%d)\n", assertion, err );
1727			}
1728		}
1729
1730		c[i].ldctl_oid = LDAP_CONTROL_ASSERT;
1731		c[i].ldctl_value = assertionvalue;
1732		c[i].ldctl_iscritical = assertctl > 1;
1733		ctrls[i] = &c[i];
1734		i++;
1735	}
1736
1737	if ( authzid ) {
1738		c[i].ldctl_value.bv_val = authzid;
1739		c[i].ldctl_value.bv_len = strlen( authzid );
1740		c[i].ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
1741		c[i].ldctl_iscritical = authzcrit;
1742		ctrls[i] = &c[i];
1743		i++;
1744	}
1745
1746#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
1747	/* NOTE: doesn't need an extra count because it's incompatible
1748	 * with authzid */
1749	if ( proxydn ) {
1750		BerElementBuffer berbuf;
1751		BerElement *ber = (BerElement *)&berbuf;
1752
1753		ber_init2( ber, NULL, LBER_USE_DER );
1754
1755		if ( ber_printf( ber, "s", proxydn ) == -1 ) {
1756			tool_exit( ld, EXIT_FAILURE );
1757		}
1758
1759		if ( ber_flatten2( ber, &c[i].ldctl_value, 0 ) == -1 ) {
1760			tool_exit( ld, EXIT_FAILURE );
1761		}
1762
1763		c[i].ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ;
1764		c[i].ldctl_iscritical = authzcrit;
1765		ctrls[i] = &c[i];
1766		i++;
1767	}
1768#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
1769
1770	if ( manageDIT ) {
1771		c[i].ldctl_oid = LDAP_CONTROL_MANAGEDIT;
1772		BER_BVZERO( &c[i].ldctl_value );
1773		c[i].ldctl_iscritical = manageDIT > 1;
1774		ctrls[i] = &c[i];
1775		i++;
1776	}
1777
1778	if ( manageDSAit ) {
1779		c[i].ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
1780		BER_BVZERO( &c[i].ldctl_value );
1781		c[i].ldctl_iscritical = manageDSAit > 1;
1782		ctrls[i] = &c[i];
1783		i++;
1784	}
1785
1786	if ( noop ) {
1787		c[i].ldctl_oid = LDAP_CONTROL_NOOP;
1788		BER_BVZERO( &c[i].ldctl_value );
1789		c[i].ldctl_iscritical = noop > 1;
1790		ctrls[i] = &c[i];
1791		i++;
1792	}
1793
1794#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
1795	if ( ppolicy ) {
1796		c[i].ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
1797		BER_BVZERO( &c[i].ldctl_value );
1798		c[i].ldctl_iscritical = 0;
1799		ctrls[i] = &c[i];
1800		i++;
1801	}
1802#endif
1803
1804	if ( preread ) {
1805		BerElementBuffer berbuf;
1806		BerElement *ber = (BerElement *)&berbuf;
1807		char **attrs = NULL;
1808
1809		if( preread_attrs ) {
1810			attrs = ldap_str2charray( preread_attrs, "," );
1811		}
1812
1813		ber_init2( ber, NULL, LBER_USE_DER );
1814
1815		if( ber_printf( ber, "{v}", attrs ) == -1 ) {
1816			fprintf( stderr, "preread attrs encode failed.\n" );
1817			tool_exit( ld, EXIT_FAILURE );
1818		}
1819
1820		err = ber_flatten2( ber, &c[i].ldctl_value, 0 );
1821		if( err < 0 ) {
1822			fprintf( stderr, "preread flatten failed (%d)\n", err );
1823			tool_exit( ld, EXIT_FAILURE );
1824		}
1825
1826		c[i].ldctl_oid = LDAP_CONTROL_PRE_READ;
1827		c[i].ldctl_iscritical = preread > 1;
1828		ctrls[i] = &c[i];
1829		i++;
1830
1831		if( attrs ) ldap_charray_free( attrs );
1832	}
1833
1834	if ( postread ) {
1835		BerElementBuffer berbuf;
1836		BerElement *ber = (BerElement *)&berbuf;
1837		char **attrs = NULL;
1838
1839		if( postread_attrs ) {
1840			attrs = ldap_str2charray( postread_attrs, "," );
1841		}
1842
1843		ber_init2( ber, NULL, LBER_USE_DER );
1844
1845		if( ber_printf( ber, "{v}", attrs ) == -1 ) {
1846			fprintf( stderr, "postread attrs encode failed.\n" );
1847			tool_exit( ld, EXIT_FAILURE );
1848		}
1849
1850		err = ber_flatten2( ber, &c[i].ldctl_value, 0 );
1851		if( err < 0 ) {
1852			fprintf( stderr, "postread flatten failed (%d)\n", err );
1853			tool_exit( ld, EXIT_FAILURE );
1854		}
1855
1856		c[i].ldctl_oid = LDAP_CONTROL_POST_READ;
1857		c[i].ldctl_iscritical = postread > 1;
1858		ctrls[i] = &c[i];
1859		i++;
1860
1861		if( attrs ) ldap_charray_free( attrs );
1862	}
1863
1864#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1865	if ( chaining ) {
1866		if ( chainingResolve > -1 ) {
1867			BerElementBuffer berbuf;
1868			BerElement *ber = (BerElement *)&berbuf;
1869
1870			ber_init2( ber, NULL, LBER_USE_DER );
1871
1872			err = ber_printf( ber, "{e" /* } */, chainingResolve );
1873		    	if ( err == -1 ) {
1874				ber_free( ber, 1 );
1875				fprintf( stderr, _("Chaining behavior control encoding error!\n") );
1876				tool_exit( ld, EXIT_FAILURE );
1877			}
1878
1879			if ( chainingContinuation > -1 ) {
1880				err = ber_printf( ber, "e", chainingContinuation );
1881		    		if ( err == -1 ) {
1882					ber_free( ber, 1 );
1883					fprintf( stderr, _("Chaining behavior control encoding error!\n") );
1884					tool_exit( ld, EXIT_FAILURE );
1885				}
1886			}
1887
1888			err = ber_printf( ber, /* { */ "N}" );
1889		    	if ( err == -1 ) {
1890				ber_free( ber, 1 );
1891				fprintf( stderr, _("Chaining behavior control encoding error!\n") );
1892				tool_exit( ld, EXIT_FAILURE );
1893			}
1894
1895			if ( ber_flatten2( ber, &c[i].ldctl_value, 0 ) == -1 ) {
1896				tool_exit( ld, EXIT_FAILURE );
1897			}
1898
1899		} else {
1900			BER_BVZERO( &c[i].ldctl_value );
1901		}
1902
1903		c[i].ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1904		c[i].ldctl_iscritical = chaining > 1;
1905		ctrls[i] = &c[i];
1906		i++;
1907	}
1908#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1909
1910#ifdef LDAP_CONTROL_X_SESSION_TRACKING
1911	if ( sessionTracking ) {
1912		if ( BER_BVISNULL( &stValue ) && st_value( ld, &stValue ) ) {
1913			tool_exit( ld, EXIT_FAILURE );
1914		}
1915
1916		c[i].ldctl_oid = LDAP_CONTROL_X_SESSION_TRACKING;
1917		c[i].ldctl_iscritical = 0;
1918		c[i].ldctl_value = stValue;
1919
1920		ctrls[i] = &c[i];
1921		i++;
1922	}
1923#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
1924
1925	while ( count-- ) {
1926		ctrls[i++] = extra_c++;
1927	}
1928	for ( count = 0; count < unknown_ctrls_num; count++ ) {
1929		ctrls[i++] = &unknown_ctrls[count];
1930	}
1931	ctrls[i] = NULL;
1932
1933	err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls );
1934
1935	if ( err != LDAP_OPT_SUCCESS ) {
1936		for ( j = 0; j < i; j++ ) {
1937			if ( ctrls[j]->ldctl_iscritical ) crit = 1;
1938		}
1939		fprintf( stderr, "Could not set %scontrols\n",
1940			crit ? "critical " : "" );
1941	}
1942
1943 	free( ctrls );
1944	if ( crit ) {
1945		tool_exit( ld, EXIT_FAILURE );
1946	}
1947}
1948
1949int
1950tool_check_abandon( LDAP *ld, int msgid )
1951{
1952	int	rc;
1953	LDAPControl *sctrls[1] = { NULL };
1954
1955	switch ( gotintr ) {
1956	case Intr_Cancel:
1957		rc = ldap_cancel_s( ld, msgid, sctrls, NULL );
1958		fprintf( stderr, "got interrupt, cancel got %d: %s\n",
1959				rc, ldap_err2string( rc ) );
1960		return -1;
1961
1962	case Intr_Abandon:
1963		rc = ldap_abandon_ext( ld, msgid, sctrls, NULL );
1964		fprintf( stderr, "got interrupt, abandon got %d: %s\n",
1965				rc, ldap_err2string( rc ) );
1966		return -1;
1967
1968	case Intr_Ignore:
1969		/* just unbind, ignoring the request */
1970		return -1;
1971	}
1972
1973	return 0;
1974}
1975
1976static int
1977print_prepostread( LDAP *ld, LDAPControl *ctrl, struct berval *what)
1978{
1979	BerElement	*ber;
1980	struct berval	bv;
1981
1982	tool_write_ldif( LDIF_PUT_COMMENT, "==> ",
1983		what->bv_val, what->bv_len );
1984	ber = ber_init( &ctrl->ldctl_value );
1985	if ( ber == NULL ) {
1986		/* error? */
1987		return 1;
1988
1989	} else if ( ber_scanf( ber, "{m{" /*}}*/, &bv ) == LBER_ERROR ) {
1990		/* error? */
1991		return 1;
1992
1993	} else {
1994		tool_write_ldif( LDIF_PUT_VALUE, "dn", bv.bv_val, bv.bv_len );
1995
1996		while ( ber_scanf( ber, "{m" /*}*/, &bv ) != LBER_ERROR ) {
1997			int		i;
1998			BerVarray	vals = NULL;
1999			char		*str = NULL;
2000
2001			if ( ber_scanf( ber, "[W]", &vals ) == LBER_ERROR ||
2002				vals == NULL )
2003			{
2004				/* error? */
2005				return 1;
2006			}
2007
2008			if ( ldif ) {
2009				char *ptr;
2010
2011				str = malloc( bv.bv_len + STRLENOF(": ") + 1 );
2012
2013				ptr = str;
2014				ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
2015				ptr = lutil_strcopy( ptr, ": " );
2016			}
2017
2018			for ( i = 0; vals[ i ].bv_val != NULL; i++ ) {
2019				tool_write_ldif(
2020					ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
2021					ldif ? str : bv.bv_val, vals[ i ].bv_val, vals[ i ].bv_len );
2022			}
2023
2024			ber_bvarray_free( vals );
2025			if ( str ) free( str );
2026		}
2027	}
2028
2029	if ( ber != NULL ) {
2030		ber_free( ber, 1 );
2031	}
2032
2033	tool_write_ldif( LDIF_PUT_COMMENT, "<== ",
2034		what->bv_val, what->bv_len );
2035
2036	return 0;
2037}
2038
2039static int
2040print_preread( LDAP *ld, LDAPControl *ctrl )
2041{
2042	static struct berval what = BER_BVC( "preread" );
2043
2044	return print_prepostread( ld, ctrl, &what );
2045}
2046
2047static int
2048print_postread( LDAP *ld, LDAPControl *ctrl )
2049{
2050	static struct berval what = BER_BVC( "postread" );
2051
2052	return print_prepostread( ld, ctrl, &what );
2053}
2054
2055static int
2056print_paged_results( LDAP *ld, LDAPControl *ctrl )
2057{
2058	ber_int_t estimate;
2059
2060	/* note: pr_cookie is being malloced; it's freed
2061	 * the next time the control is sent, but the last
2062	 * time it's not; we don't care too much, because
2063	 * the last time an empty value is returned... */
2064	if ( ldap_parse_pageresponse_control( ld, ctrl, &estimate, &pr_cookie )
2065		!= LDAP_SUCCESS )
2066	{
2067		/* error? */
2068		return 1;
2069
2070	} else {
2071		char	buf[ BUFSIZ ], *ptr = buf;
2072		int plen;
2073
2074		if ( estimate > 0 ) {
2075			plen = sprintf( buf, "estimate=%d cookie=", estimate );
2076		} else {
2077			plen = sprintf( buf, "cookie=" );
2078		}
2079
2080		if ( pr_cookie.bv_len > 0 ) {
2081			struct berval	bv;
2082
2083			bv.bv_len = LUTIL_BASE64_ENCODE_LEN(
2084				pr_cookie.bv_len ) + 1;
2085			ptr = ber_memalloc( bv.bv_len + 1 + plen );
2086			bv.bv_val = ptr + plen;
2087
2088			strcpy( ptr, buf );
2089
2090			bv.bv_len = lutil_b64_ntop(
2091				(unsigned char *) pr_cookie.bv_val,
2092				pr_cookie.bv_len,
2093				bv.bv_val, bv.bv_len );
2094
2095			pr_morePagedResults = 1;
2096			plen += bv.bv_len;
2097		}
2098
2099		tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
2100			ldif ? "pagedresults: " : "pagedresults",
2101			ptr, plen );
2102
2103		if ( ptr != buf )
2104			ber_memfree( ptr );
2105	}
2106
2107	return 0;
2108}
2109
2110static int
2111print_psearch( LDAP *ld, LDAPControl *ctrl )
2112{
2113	int rc;
2114	int chgtype;
2115	int chgpres;
2116	long chgnum;
2117	struct berval prevdn;
2118
2119	rc = ldap_parse_entrychange_control( ld, ctrl, &chgtype, &prevdn,
2120		&chgpres, &chgnum );
2121	if ( rc == LDAP_SUCCESS ) {
2122		char buf[ BUFSIZ ];
2123		char *ptr = buf;
2124		int blen = sizeof(buf), len;
2125
2126		switch( chgtype ) {
2127		case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD:
2128			len = snprintf( ptr, blen, "add" );
2129			ptr += len;
2130			blen -= len;
2131			break;
2132		case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_DELETE:
2133			len = snprintf( ptr, blen, "delete" );
2134			ptr += len;
2135			blen -= len;
2136			break;
2137		case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_MODIFY:
2138			len = snprintf( ptr, blen, "modify" );
2139			ptr += len;
2140			blen -= len;
2141			break;
2142		case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_RENAME:
2143			len = snprintf( ptr, blen, "moddn" );
2144			ptr += len;
2145			blen -= len;
2146			if ( prevdn.bv_val != NULL ) {
2147				len = snprintf( ptr, blen, " prevdn %s", prevdn.bv_val );
2148				ptr += len;
2149				blen -= len;
2150			}
2151			break;
2152		}
2153		if ( chgpres ) {
2154			len = snprintf( ptr, blen, " changeNumber %ld", chgnum) ;
2155			ptr += len;
2156			blen -= len;
2157		}
2158
2159		tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
2160			ldif ? "persistentSearch: " : "persistentSearch", buf, len );
2161	}
2162
2163	return rc;
2164}
2165
2166static int
2167print_sss( LDAP *ld, LDAPControl *ctrl )
2168{
2169	int rc;
2170	ber_int_t err;
2171	char *attr;
2172
2173	rc = ldap_parse_sortresponse_control( ld, ctrl, &err, &attr );
2174	if ( rc == LDAP_SUCCESS ) {
2175		char buf[ BUFSIZ ];
2176		rc = snprintf( buf, sizeof(buf), "(%d) %s%s%s",
2177			err, ldap_err2string(err), attr ? " " : "", attr ? attr : "" );
2178
2179		tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
2180			ldif ? "sortResult: " : "sortResult", buf, rc );
2181	}
2182
2183	return rc;
2184}
2185
2186static int
2187print_vlv( LDAP *ld, LDAPControl *ctrl )
2188{
2189	int rc;
2190	ber_int_t err;
2191	struct berval bv;
2192
2193	rc = ldap_parse_vlvresponse_control( ld, ctrl, &vlvPos, &vlvCount,
2194		&vlvContext, &err );
2195	if ( rc == LDAP_SUCCESS ) {
2196		char buf[ BUFSIZ ];
2197
2198		if ( vlvContext && vlvContext->bv_len > 0 ) {
2199			bv.bv_len = LUTIL_BASE64_ENCODE_LEN(
2200				vlvContext->bv_len ) + 1;
2201			bv.bv_val = ber_memalloc( bv.bv_len + 1 );
2202
2203			bv.bv_len = lutil_b64_ntop(
2204				(unsigned char *) vlvContext->bv_val,
2205				vlvContext->bv_len,
2206				bv.bv_val, bv.bv_len );
2207		} else {
2208			bv.bv_val = "";
2209			bv.bv_len = 0;
2210		}
2211
2212		rc = snprintf( buf, sizeof(buf), "pos=%d count=%d context=%s (%d) %s",
2213			vlvPos, vlvCount, bv.bv_val,
2214			err, ldap_err2string(err));
2215
2216		if ( bv.bv_len )
2217			ber_memfree( bv.bv_val );
2218
2219		tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
2220			ldif ? "vlvResult" : "vlvResult", buf, rc );
2221	}
2222
2223	return rc;
2224}
2225
2226#ifdef LDAP_CONTROL_X_DEREF
2227static int
2228print_deref( LDAP *ld, LDAPControl *ctrl )
2229{
2230	LDAPDerefRes    *drhead = NULL, *dr;
2231	int		rc;
2232
2233	rc = ldap_parse_derefresponse_control( ld, ctrl, &drhead );
2234	if ( rc != LDAP_SUCCESS ) {
2235		return rc;
2236	}
2237
2238	for ( dr = drhead; dr != NULL; dr = dr->next ) {
2239		LDAPDerefVal	*dv;
2240		ber_len_t	len;
2241		char		*buf, *ptr;
2242
2243		len = strlen( dr->derefAttr ) + STRLENOF(": ");
2244
2245		for ( dv = dr->attrVals; dv != NULL; dv = dv->next ) {
2246			if ( dv->vals != NULL ) {
2247				int j;
2248				ber_len_t tlen = strlen(dv->type);
2249
2250				for ( j = 0; dv->vals[ j ].bv_val != NULL; j++ ) {
2251					len += STRLENOF("<:=>;") + tlen + 4*((dv->vals[ j ].bv_len - 1)/3 + 1);
2252				}
2253			}
2254		}
2255		len += dr->derefVal.bv_len + STRLENOF("\n");
2256		buf = ldap_memalloc( len + 1 );
2257		if ( buf == NULL ) {
2258			rc = LDAP_NO_MEMORY;
2259			goto done;
2260		}
2261
2262		ptr = buf;
2263		ptr = lutil_strcopy( ptr, dr->derefAttr );
2264		*ptr++ = ':';
2265		*ptr++ = ' ';
2266		for ( dv = dr->attrVals; dv != NULL; dv = dv->next ) {
2267			if ( dv->vals != NULL ) {
2268				int j;
2269				for ( j = 0; dv->vals[ j ].bv_val != NULL; j++ ) {
2270					int k = ldif_is_not_printable( dv->vals[ j ].bv_val, dv->vals[ j ].bv_len );
2271
2272					*ptr++ = '<';
2273					ptr = lutil_strcopy( ptr, dv->type );
2274					if ( k ) {
2275						*ptr++ = ':';
2276					}
2277					*ptr++ = '=';
2278					if ( k ) {
2279						k = lutil_b64_ntop(
2280							(unsigned char *) dv->vals[ j ].bv_val,
2281							dv->vals[ j ].bv_len,
2282							ptr, buf + len - ptr );
2283						assert( k >= 0 );
2284						ptr += k;
2285
2286					} else {
2287						ptr = lutil_memcopy( ptr, dv->vals[ j ].bv_val, dv->vals[ j ].bv_len );
2288					}
2289					*ptr++ = '>';
2290					*ptr++ = ';';
2291				}
2292			}
2293		}
2294		ptr = lutil_strncopy( ptr, dr->derefVal.bv_val, dr->derefVal.bv_len );
2295		*ptr = '\0';
2296		assert( ptr <= buf + len );
2297
2298		tool_write_ldif( LDIF_PUT_COMMENT, NULL, buf, ptr - buf);
2299
2300		ldap_memfree( buf );
2301	}
2302
2303	rc = LDAP_SUCCESS;
2304
2305done:;
2306	ldap_derefresponse_free( drhead );
2307
2308	return rc;
2309}
2310#endif
2311
2312#ifdef LDAP_CONTROL_X_WHATFAILED
2313static int
2314print_whatfailed( LDAP *ld, LDAPControl *ctrl )
2315{
2316	BerElement *ber;
2317	ber_tag_t tag;
2318	ber_len_t siz;
2319	BerVarray bva = NULL;
2320
2321	/* Create a BerElement from the berval returned in the control. */
2322	ber = ber_init( &ctrl->ldctl_value );
2323
2324	if ( ber == NULL ) {
2325		return LDAP_NO_MEMORY;
2326	}
2327
2328	siz = sizeof(struct berval);
2329	tag = ber_scanf( ber, "[M]", &bva, &siz, 0 );
2330	if ( tag != LBER_ERROR ) {
2331		int i;
2332
2333		tool_write_ldif( LDIF_PUT_COMMENT, " what failed:", NULL, 0 );
2334
2335		for ( i = 0; bva[i].bv_val != NULL; i++ ) {
2336			tool_write_ldif( LDIF_PUT_COMMENT, NULL, bva[i].bv_val, bva[i].bv_len );
2337		}
2338
2339		ldap_memfree( bva );
2340	}
2341
2342        ber_free( ber, 1 );
2343
2344
2345	return 0;
2346}
2347#endif
2348
2349static int
2350print_syncstate( LDAP *ld, LDAPControl *ctrl )
2351{
2352	struct berval syncUUID, syncCookie = BER_BVNULL;
2353	char buf[LDAP_LUTIL_UUIDSTR_BUFSIZE], *uuidstr = "(UUID malformed)";
2354	BerElement *ber;
2355	ber_tag_t tag;
2356	ber_int_t state;
2357	int rc;
2358
2359	if ( ldif ) {
2360		return 0;
2361	}
2362
2363	/* Create a BerElement from the berval returned in the control. */
2364	ber = ber_init( &ctrl->ldctl_value );
2365
2366	if ( ber == NULL ) {
2367		return LDAP_NO_MEMORY;
2368	}
2369
2370	if ( ber_scanf( ber, "{em", &state, &syncUUID ) == LBER_ERROR ) {
2371		ber_free( ber, 1 );
2372		return 1;
2373	}
2374
2375	tag = ber_get_stringbv( ber, &syncCookie, 0 );
2376
2377	rc = lutil_uuidstr_from_normalized(
2378			syncUUID.bv_val, syncUUID.bv_len,
2379			buf, LDAP_LUTIL_UUIDSTR_BUFSIZE );
2380
2381	if ( rc > 0 && rc < LDAP_LUTIL_UUIDSTR_BUFSIZE ) {
2382		uuidstr = buf;
2383	}
2384
2385	switch ( state ) {
2386		case LDAP_SYNC_PRESENT:
2387			printf(_("# SyncState control, UUID %s present\n"), uuidstr);
2388			break;
2389		case LDAP_SYNC_ADD:
2390			printf(_("# SyncState control, UUID %s added\n"), uuidstr);
2391			break;
2392		case LDAP_SYNC_MODIFY:
2393			printf(_("# SyncState control, UUID %s modified\n"), uuidstr);
2394			break;
2395		case LDAP_SYNC_DELETE:
2396			printf(_("# SyncState control, UUID %s deleted\n"), uuidstr);
2397			break;
2398		default:
2399			ber_free( ber, 1 );
2400			return 1;
2401	}
2402
2403	if ( tag != LBER_ERROR ) {
2404		if ( ldif_is_not_printable( syncCookie.bv_val, syncCookie.bv_len ) ) {
2405			struct berval bv;
2406
2407			bv.bv_len = LUTIL_BASE64_ENCODE_LEN( syncCookie.bv_len ) + 1;
2408			bv.bv_val = ber_memalloc( bv.bv_len + 1 );
2409
2410			bv.bv_len = lutil_b64_ntop(
2411					(unsigned char *) syncCookie.bv_val, syncCookie.bv_len,
2412					bv.bv_val, bv.bv_len );
2413
2414			printf(_("# cookie:: %s\n"), bv.bv_val );
2415			ber_memfree( bv.bv_val );
2416		} else {
2417			printf(_("# cookie: %s\n"), syncCookie.bv_val );
2418		}
2419	}
2420
2421	ber_free( ber, 1 );
2422	return 0;
2423}
2424
2425static int
2426print_syncdone( LDAP *ld, LDAPControl *ctrl )
2427{
2428	BerElement *ber;
2429	struct berval cookie = BER_BVNULL;
2430	ber_len_t len;
2431	ber_int_t refreshDeletes = 0;
2432
2433	if ( ldif ) {
2434		return 0;
2435	}
2436
2437	/* Create a BerElement from the berval returned in the control. */
2438	ber = ber_init( &ctrl->ldctl_value );
2439
2440	if ( ber == NULL ) {
2441		return LDAP_NO_MEMORY;
2442	}
2443
2444	ber_skip_tag( ber, &len );
2445	if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
2446		ber_scanf( ber, "m", &cookie );
2447	}
2448	if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
2449		ber_scanf( ber, "b", &refreshDeletes );
2450	}
2451
2452	printf(_("# SyncDone control refreshDeletes=%d\n"), refreshDeletes ? 1 : 0 );
2453
2454	if ( !BER_BVISNULL( &cookie ) ) {
2455		if ( ldif_is_not_printable( cookie.bv_val, cookie.bv_len ) ) {
2456			struct berval bv;
2457
2458			bv.bv_len = LUTIL_BASE64_ENCODE_LEN( cookie.bv_len ) + 1;
2459			bv.bv_val = ber_memalloc( bv.bv_len + 1 );
2460
2461			bv.bv_len = lutil_b64_ntop(
2462					(unsigned char *) cookie.bv_val, cookie.bv_len,
2463					bv.bv_val, bv.bv_len );
2464
2465			printf(_("# cookie:: %s\n"), bv.bv_val );
2466			ber_memfree( bv.bv_val );
2467		} else {
2468			printf(_("# cookie: %s\n"), cookie.bv_val );
2469		}
2470	}
2471
2472	ber_free( ber, 1 );
2473	return 0;
2474}
2475
2476#ifdef LDAP_CONTROL_X_DIRSYNC
2477static int
2478print_dirsync( LDAP *ld, LDAPControl *ctrl )
2479{
2480	int rc, continueFlag;
2481	struct berval cookie;
2482
2483	rc = ldap_parse_dirsync_control( ld, ctrl,
2484		&continueFlag, &cookie );
2485	if ( rc == LDAP_SUCCESS ) {
2486		printf(_("# DirSync control continueFlag=%d\n"), continueFlag );
2487		if ( !BER_BVISNULL( &cookie )) {
2488			if ( ldif_is_not_printable( cookie.bv_val, cookie.bv_len ) ) {
2489				struct berval bv;
2490
2491				bv.bv_len = LUTIL_BASE64_ENCODE_LEN( cookie.bv_len ) + 1;
2492				bv.bv_val = ber_memalloc( bv.bv_len + 1 );
2493
2494				bv.bv_len = lutil_b64_ntop(
2495						(unsigned char *) cookie.bv_val, cookie.bv_len,
2496						bv.bv_val, bv.bv_len );
2497
2498				printf(_("# cookie:: %s\n"), bv.bv_val );
2499				ber_memfree( bv.bv_val );
2500			} else {
2501				printf(_("# cookie: %s\n"), cookie.bv_val );
2502			}
2503		}
2504	}
2505	return rc;
2506}
2507#endif
2508
2509#ifdef LDAP_CONTROL_AUTHZID_RESPONSE
2510static int
2511print_authzid( LDAP *ld, LDAPControl *ctrl )
2512{
2513	if ( ctrl->ldctl_value.bv_len ) {
2514		tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
2515			ldif ? "authzid: " : "authzid",
2516		ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len );
2517	} else {
2518		tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
2519			ldif ? "authzid: " : "authzid",
2520			"anonymous",  STRLENOF("anonymous") );
2521	}
2522
2523	return 0;
2524}
2525#endif
2526
2527#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
2528static int
2529print_ppolicy( LDAP *ld, LDAPControl *ctrl )
2530{
2531	int expire = 0, grace = 0, rc;
2532	LDAPPasswordPolicyError	pperr;
2533
2534	rc = ldap_parse_passwordpolicy_control( ld, ctrl,
2535		&expire, &grace, &pperr );
2536	if ( rc == LDAP_SUCCESS ) {
2537		char	buf[ BUFSIZ ], *ptr = buf;
2538
2539		if ( expire != -1 ) {
2540			ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
2541				"expire=%d", expire );
2542		}
2543
2544		if ( grace != -1 ) {
2545			ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
2546				"%sgrace=%d", ptr == buf ? "" : " ", grace );
2547		}
2548
2549		if ( pperr != PP_noError ) {
2550			ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
2551				"%serror=%d (%s)", ptr == buf ? "" : " ",
2552				pperr,
2553				ldap_passwordpolicy_err2txt( pperr ) );
2554		}
2555
2556		tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
2557			ldif ? "ppolicy: " : "ppolicy", buf, ptr - buf );
2558	}
2559
2560	return rc;
2561}
2562#endif
2563
2564#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED
2565static int
2566print_netscape_pwexpired( LDAP *ld, LDAPControl *ctrl )
2567{
2568	printf(_("# PasswordExpired control\n") );
2569	return 0;
2570}
2571
2572static int
2573print_netscape_pwexpiring( LDAP *ld, LDAPControl *ctrl )
2574{
2575	long expiring = 0;
2576	int rc;
2577
2578	rc = ldap_parse_password_expiring_control( ld, ctrl, &expiring );
2579	if ( rc == LDAP_SUCCESS ) {
2580		printf(_("# PasswordExpiring control seconds=%ld\n"), expiring );
2581	}
2582	return rc;
2583}
2584#endif
2585
2586#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
2587static int
2588print_account_usability( LDAP *ld, LDAPControl *ctrl )
2589{
2590	LDAPAccountUsability usability;
2591	ber_int_t available = 0;
2592	int rc;
2593
2594	rc = ldap_parse_accountusability_control( ld, ctrl, &available, &usability );
2595	if ( rc == LDAP_SUCCESS ) {
2596		char	buf[ BUFSIZ ], *ptr = buf;
2597
2598		ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
2599			   "%savailable", available ? "" : "not " );
2600		if ( available ) {
2601			if ( usability.seconds_remaining == -1 ) {
2602				ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
2603					" and does not expire" );
2604			} else {
2605				ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
2606					" expire=%d", usability.seconds_remaining );
2607			}
2608		} else {
2609			int added = 0;
2610			ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
2611				" (" /* ')' */ );
2612
2613			if ( usability.more_info.inactive ) {
2614				ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
2615					"inactive " );
2616				added++;
2617			}
2618			if ( usability.more_info.reset ) {
2619				ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
2620					"reset " );
2621				added++;
2622			}
2623			if ( usability.more_info.expired ) {
2624				ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
2625					"expired " );
2626				added++;
2627			}
2628
2629			if ( added ) {
2630				ptr[-1] = ')';
2631				*ptr++ = ' ';
2632			} else {
2633				*(--ptr) = '\0';
2634			}
2635
2636			if ( usability.more_info.remaining_grace != -1 ) {
2637				ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
2638					"grace=%d ", usability.more_info.remaining_grace );
2639			}
2640
2641			if ( usability.more_info.seconds_before_unlock != -1 ) {
2642				ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
2643					"seconds_before_unlock=%d ", usability.more_info.seconds_before_unlock );
2644			}
2645
2646			*(--ptr) = '\0';
2647		}
2648
2649		tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
2650			ldif ? "accountUsability: " : "accountUsability", buf, ptr - buf );
2651	}
2652
2653	return rc;
2654}
2655#endif
2656
2657void tool_print_ctrls(
2658	LDAP		*ld,
2659	LDAPControl	**ctrls )
2660{
2661	int	i;
2662	char	*ptr;
2663
2664	for ( i = 0; ctrls[i] != NULL; i++ ) {
2665		/* control: OID criticality base64value */
2666		struct berval b64 = BER_BVNULL;
2667		ber_len_t len;
2668		char *str;
2669		int j;
2670
2671		/* FIXME: there might be cases where a control has NULL OID;
2672		 * this makes little sense, especially when returned by the
2673		 * server, but libldap happily allows it */
2674		if ( ctrls[i]->ldctl_oid == NULL ) {
2675			continue;
2676		}
2677
2678		len = ldif ? 2 : 0;
2679		len += strlen( ctrls[i]->ldctl_oid );
2680
2681		/* add enough for space after OID and the critical value itself */
2682		len += ctrls[i]->ldctl_iscritical
2683			? sizeof("true") : sizeof("false");
2684
2685		/* convert to base64 */
2686		if ( !BER_BVISNULL( &ctrls[i]->ldctl_value ) ) {
2687			b64.bv_len = LUTIL_BASE64_ENCODE_LEN(
2688				ctrls[i]->ldctl_value.bv_len ) + 1;
2689			b64.bv_val = ber_memalloc( b64.bv_len + 1 );
2690
2691			b64.bv_len = lutil_b64_ntop(
2692				(unsigned char *) ctrls[i]->ldctl_value.bv_val,
2693				ctrls[i]->ldctl_value.bv_len,
2694				b64.bv_val, b64.bv_len );
2695		}
2696
2697		if ( b64.bv_len ) {
2698			len += 1 + b64.bv_len;
2699		}
2700
2701		ptr = str = malloc( len + 1 );
2702		if ( ldif ) {
2703			ptr = lutil_strcopy( ptr, ": " );
2704		}
2705		ptr = lutil_strcopy( ptr, ctrls[i]->ldctl_oid );
2706		ptr = lutil_strcopy( ptr, ctrls[i]->ldctl_iscritical
2707			? " true" : " false" );
2708
2709		if ( b64.bv_len ) {
2710			ptr = lutil_strcopy( ptr, " " );
2711			ptr = lutil_strcopy( ptr, b64.bv_val );
2712		}
2713
2714		if ( ldif < 2 ) {
2715			tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
2716				"control", str, len );
2717		}
2718
2719		free( str );
2720		if ( b64.bv_len ) {
2721			ber_memfree( b64.bv_val );
2722		}
2723
2724		/* known controls */
2725		for ( j = 0; tool_ctrl_response[j].oid != NULL; j++ ) {
2726			if ( strcmp( tool_ctrl_response[j].oid, ctrls[i]->ldctl_oid ) == 0 ) {
2727				if ( !(tool_ctrl_response[j].mask & tool_type )) {
2728					/* this control should not appear
2729					 * with this tool; warning? */
2730				}
2731				break;
2732			}
2733		}
2734
2735		if ( tool_ctrl_response[j].oid != NULL && tool_ctrl_response[j].func ) {
2736			(void)tool_ctrl_response[j].func( ld, ctrls[i] );
2737		}
2738	}
2739}
2740
2741int
2742tool_write_ldif( int type, char *name, char *value, ber_len_t vallen )
2743{
2744	char	*ldif;
2745
2746	if (( ldif = ldif_put_wrap( type, name, value, vallen, ldif_wrap )) == NULL ) {
2747		return( -1 );
2748	}
2749
2750	fputs( ldif, stdout );
2751	ber_memfree( ldif );
2752
2753	return( 0 );
2754}
2755
2756int
2757tool_is_oid( const char *s )
2758{
2759	int		first = 1;
2760
2761	if ( !isdigit( (unsigned char) s[ 0 ] ) ) {
2762		return 0;
2763	}
2764
2765	for ( ; s[ 0 ]; s++ ) {
2766		if ( s[ 0 ] == '.' ) {
2767			if ( s[ 1 ] == '\0' ) {
2768				return 0;
2769			}
2770			first = 1;
2771			continue;
2772		}
2773
2774		if ( !isdigit( (unsigned char) s[ 0 ] ) ) {
2775			return 0;
2776		}
2777
2778		if ( first == 1 && s[ 0 ] == '0' && s[ 1 ] != '.' ) {
2779			return 0;
2780		}
2781		first = 0;
2782	}
2783
2784	return 1;
2785}
2786