1/* ldapcompare.c -- LDAP compare tool */
2/* $OpenLDAP$ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 1998-2011 The OpenLDAP Foundation.
6 * Portions Copyright 1998-2003 Kurt D. Zeilenga.
7 * Portions Copyright 1998-2001 Net Boolean Incorporated.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
19 * All rights reserved.
20 *
21 * Redistribution and use in source and binary forms are permitted
22 * provided that this notice is preserved and that due credit is given
23 * to the University of Michigan at Ann Arbor.  The name of the
24 * University may not be used to endorse or promote products derived
25 * from this software without specific prior written permission.  This
26 * software is provided ``as is'' without express or implied warranty.
27 */
28/* Portions Copyright 2002, F5 Networks, Inc, All rights reserved.
29 * This software is not subject to any license of F5 Networks.
30 * This is free software; you can redistribute and use it
31 * under the same terms as OpenLDAP itself.
32 */
33/* ACKNOWLEDGEMENTS:
34 * This work was originally developed by Jeff Costlow (F5 Networks)
35 * based, in part, on existing LDAP tools and adapted for inclusion
36 * into OpenLDAP Software by Kurt D. Zeilenga.
37 */
38
39#include "portable.h"
40
41#include <stdio.h>
42
43#include <ac/stdlib.h>
44
45#include <ac/ctype.h>
46#include <ac/string.h>
47#include <ac/unistd.h>
48#include <ac/errno.h>
49#include <ac/socket.h>
50#include <ac/time.h>
51#include <sys/stat.h>
52
53#ifdef HAVE_FCNTL_H
54#include <fcntl.h>
55#endif
56#ifdef HAVE_SYS_TYPES_H
57#include <sys/types.h>
58#endif
59#ifdef HAVE_IO_H
60#include <io.h>
61#endif
62
63#include <ldap.h>
64
65#include "lutil.h"
66#include "lutil_ldap.h"
67#include "ldap_defaults.h"
68
69#include "common.h"
70
71
72static int quiet = 0;
73
74
75void
76usage( void )
77{
78	fprintf( stderr, _("usage: %s [options] DN <attr:value|attr::b64value>\n"), prog);
79	fprintf( stderr, _("where:\n"));
80	fprintf( stderr, _("  DN\tDistinguished Name\n"));
81	fprintf( stderr, _("  attr\tassertion attribute\n"));
82	fprintf( stderr, _("  value\tassertion value\n"));
83	fprintf( stderr, _("  b64value\tbase64 encoding of assertion value\n"));
84
85	fprintf( stderr, _("Compare options:\n"));
86	fprintf( stderr, _("  -E [!]<ext>[=<extparam>] compare extensions (! indicates criticality)\n"));
87	fprintf( stderr, _("             !dontUseCopy                (Don't Use Copy)\n"));
88	fprintf( stderr, _("  -M         enable Manage DSA IT control (-MM to make critical)\n"));
89	fprintf( stderr, _("  -P version protocol version (default: 3)\n"));
90	fprintf( stderr, _("  -z         Quiet mode,"
91		" don't print anything, use return values\n"));
92	tool_common_usage();
93	exit( EXIT_FAILURE );
94}
95
96static int docompare LDAP_P((
97	LDAP *ld,
98	char *dn,
99	char *attr,
100	struct berval *bvalue,
101	int quiet,
102	LDAPControl **sctrls,
103	LDAPControl **cctrls));
104
105
106const char options[] = "z"
107	"Cd:D:e:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z";
108
109#ifdef LDAP_CONTROL_DONTUSECOPY
110int dontUseCopy = 0;
111#endif
112
113int
114handle_private_option( int i )
115{
116	char	*control, *cvalue;
117	int		crit;
118
119	switch ( i ) {
120	case 'E': /* compare extensions */
121		if( protocol == LDAP_VERSION2 ) {
122			fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
123				prog, protocol );
124			exit( EXIT_FAILURE );
125		}
126
127		/* should be extended to support comma separated list of
128		 *	[!]key[=value] parameters, e.g.  -E !foo,bar=567
129		 */
130
131		crit = 0;
132		cvalue = NULL;
133		if( optarg[0] == '!' ) {
134			crit = 1;
135			optarg++;
136		}
137
138		control = ber_strdup( optarg );
139		if ( (cvalue = strchr( control, '=' )) != NULL ) {
140			*cvalue++ = '\0';
141		}
142
143#ifdef LDAP_CONTROL_DONTUSECOPY
144		if ( strcasecmp( control, "dontUseCopy" ) == 0 ) {
145			if( dontUseCopy ) {
146				fprintf( stderr,
147					_("dontUseCopy control previously specified\n"));
148				exit( EXIT_FAILURE );
149			}
150			if( cvalue != NULL ) {
151				fprintf( stderr,
152					_("dontUseCopy: no control value expected\n") );
153				usage();
154			}
155			if( !crit ) {
156				fprintf( stderr,
157					_("dontUseCopy: critical flag required\n") );
158				usage();
159			}
160
161			dontUseCopy = 1 + crit;
162		} else
163#endif
164		{
165			fprintf( stderr,
166				_("Invalid compare extension name: %s\n"), control );
167			usage();
168		}
169		break;
170
171	case 'z':
172		quiet = 1;
173		break;
174
175	default:
176		return 0;
177	}
178	return 1;
179}
180
181
182int
183main( int argc, char **argv )
184{
185	char		*compdn = NULL, *attrs = NULL;
186	char		*sep;
187	int		rc;
188	LDAP		*ld = NULL;
189	struct berval	bvalue = { 0, NULL };
190	int		i = 0;
191	LDAPControl	c[1];
192
193
194	tool_init( TOOL_COMPARE );
195	prog = lutil_progname( "ldapcompare", argc, argv );
196
197	tool_args( argc, argv );
198
199	if ( argc - optind != 2 ) {
200		usage();
201	}
202
203	compdn = argv[optind++];
204	attrs = argv[optind++];
205
206	/* user passed in only 2 args, the last one better be in
207	 * the form attr:value or attr::b64value
208	 */
209	sep = strchr(attrs, ':');
210	if (!sep) {
211		usage();
212	}
213
214	*sep++='\0';
215	if ( *sep != ':' ) {
216		bvalue.bv_val = strdup( sep );
217		bvalue.bv_len = strlen( bvalue.bv_val );
218
219	} else {
220		/* it's base64 encoded. */
221		bvalue.bv_val = malloc( strlen( &sep[1] ));
222		bvalue.bv_len = lutil_b64_pton( &sep[1],
223			(unsigned char *) bvalue.bv_val, strlen( &sep[1] ));
224
225		if (bvalue.bv_len == (ber_len_t)-1) {
226			fprintf(stderr, _("base64 decode error\n"));
227			exit(-1);
228		}
229	}
230
231	ld = tool_conn_setup( 0, 0 );
232
233	tool_bind( ld );
234
235	if ( 0
236#ifdef LDAP_CONTROL_DONTUSECOPY
237		|| dontUseCopy
238#endif
239		)
240	{
241#ifdef LDAP_CONTROL_DONTUSECOPY
242		if ( dontUseCopy ) {
243			c[i].ldctl_oid = LDAP_CONTROL_DONTUSECOPY;
244			c[i].ldctl_value.bv_val = NULL;
245			c[i].ldctl_value.bv_len = 0;
246			c[i].ldctl_iscritical = dontUseCopy > 1;
247			i++;
248		}
249#endif
250	}
251
252	tool_server_controls( ld, c, i );
253
254	if ( verbose ) {
255		fprintf( stderr, _("DN:%s, attr:%s, value:%s\n"),
256			compdn, attrs, sep );
257	}
258
259	rc = docompare( ld, compdn, attrs, &bvalue, quiet, NULL, NULL );
260
261	free( bvalue.bv_val );
262
263	tool_exit( ld, rc );
264}
265
266
267static int docompare(
268	LDAP *ld,
269	char *dn,
270	char *attr,
271	struct berval *bvalue,
272	int quiet,
273	LDAPControl **sctrls,
274	LDAPControl **cctrls )
275{
276	int		rc, msgid, code;
277	LDAPMessage	*res;
278	char		*matcheddn;
279	char		*text;
280	char		**refs;
281	LDAPControl **ctrls = NULL;
282
283	if ( dont ) {
284		return LDAP_SUCCESS;
285	}
286
287	rc = ldap_compare_ext( ld, dn, attr, bvalue,
288		sctrls, cctrls, &msgid );
289	if ( rc == -1 ) {
290		return( rc );
291	}
292
293	for ( ; ; ) {
294		struct timeval	tv;
295
296		tv.tv_sec = 0;
297		tv.tv_usec = 100000;
298
299		if ( tool_check_abandon( ld, msgid ) ) {
300			return LDAP_CANCELLED;
301		}
302
303		rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
304		if ( rc < 0 ) {
305			tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
306			return rc;
307		}
308
309		if ( rc != 0 ) {
310			break;
311		}
312	}
313
314	rc = ldap_parse_result( ld, res, &code, &matcheddn, &text, &refs, &ctrls, 1 );
315
316	if( rc != LDAP_SUCCESS ) {
317		fprintf( stderr, "%s: ldap_parse_result: %s (%d)\n",
318			prog, ldap_err2string( rc ), rc );
319		return rc;
320	}
321
322	if ( !quiet && ( verbose || ( code != LDAP_SUCCESS && code != LDAP_COMPARE_TRUE && code != LDAP_COMPARE_FALSE )||
323		(matcheddn && *matcheddn) || (text && *text) || (refs && *refs) ) )
324	{
325		printf( _("Compare Result: %s (%d)\n"),
326			ldap_err2string( code ), code );
327
328		if( text && *text ) {
329			printf( _("Additional info: %s\n"), text );
330		}
331
332		if( matcheddn && *matcheddn ) {
333			printf( _("Matched DN: %s\n"), matcheddn );
334		}
335
336		if( refs ) {
337			int i;
338			for( i=0; refs[i]; i++ ) {
339				printf(_("Referral: %s\n"), refs[i] );
340			}
341		}
342	}
343
344	/* if we were told to be quiet, use the return value. */
345	if ( !quiet ) {
346		if ( code == LDAP_COMPARE_TRUE ) {
347			printf(_("TRUE\n"));
348		} else if ( code == LDAP_COMPARE_FALSE ) {
349			printf(_("FALSE\n"));
350		} else {
351			printf(_("UNDEFINED\n"));
352		}
353	}
354
355	if ( ctrls ) {
356		tool_print_ctrls( ld, ctrls );
357		ldap_controls_free( ctrls );
358	}
359
360	ber_memfree( text );
361	ber_memfree( matcheddn );
362	ber_memvfree( (void **) refs );
363
364	return( code );
365}
366
367