xref: /netbsd-src/external/bsd/openldap/dist/clients/tools/ldapvc.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: ldapvc.c,v 1.2 2021/08/14 16:14:49 christos Exp $	*/
2 
3 /* ldapvc.c -- a tool for verifying credentials */
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 2010 Kurt D. Zeilenga.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in the file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 /* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
20  * All rights reserved.
21  *
22  * Redistribution and use in source and binary forms are permitted
23  * provided that this notice is preserved and that due credit is given
24  * to the University of Michigan at Ann Arbor.  The name of the
25  * University may not be used to endorse or promote products derived
26  * from this software without specific prior written permission.  This
27  * software is provided ``as is'' without express or implied warranty.
28  */
29 /* ACKNOWLEDGEMENTS:
30  * This work was originally developed by Kurt D. Zeilenga for inclusion
31  * in OpenLDAP Software based, in part, on other client tools.
32  */
33 
34 #include <sys/cdefs.h>
35 __RCSID("$NetBSD: ldapvc.c,v 1.2 2021/08/14 16:14:49 christos Exp $");
36 
37 #include "portable.h"
38 
39 #include <stdio.h>
40 
41 #include <ac/stdlib.h>
42 
43 #include <ac/ctype.h>
44 #include <ac/socket.h>
45 #include <ac/string.h>
46 #include <ac/time.h>
47 #include <ac/unistd.h>
48 
49 #include <ldap.h>
50 #include "lutil.h"
51 #include "lutil_ldap.h"
52 #include "ldap_defaults.h"
53 
54 #include "common.h"
55 
56 static int req_authzid = 0;
57 static int req_pp = 0;
58 
59 #if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
60 #define LDAP_SASL_NONE (~0U)
61 static unsigned vc_sasl = LDAP_SASL_NONE;
62 static char *vc_sasl_realm = NULL;
63 static char *vc_sasl_authcid = NULL;
64 static char *vc_sasl_authzid = NULL;
65 static char *vc_sasl_mech = NULL;
66 static char *vc_sasl_secprops = NULL;
67 #endif
68 static char * dn = NULL;
69 static struct berval cred = {0, NULL};
70 
71 void
usage(void)72 usage( void )
73 {
74 	fprintf( stderr, _("Issue LDAP Verify Credentials operation to verify a user's credentials\n\n"));
75 	fprintf( stderr, _("usage: %s [options] [DN [cred]])\n"), prog);
76 	fprintf( stderr, _("where:\n"));
77 	fprintf( stderr, _("    DN\tDistinguished Name\n"));
78 	fprintf( stderr, _("    cred\tCredentials (prompt if not present)\n"));
79 	fprintf( stderr, _("options:\n"));
80 	fprintf( stderr, _("    -a\tRequest AuthzId\n"));
81 	fprintf( stderr, _("    -b\tRequest Password Policy Information\n"));
82 	fprintf( stderr, _("    -E sasl=(a[utomatic]|i[nteractive]|q[uiet]>\tSASL mode (defaults to automatic if any other -E option provided, otherwise none))\n"));
83 	fprintf( stderr, _("    -E mech=<mech>\tSASL mechanism (default "" e.g. Simple)\n"));
84 	fprintf( stderr, _("    -E realm=<realm>\tSASL Realm (defaults to none)\n"));
85 	fprintf( stderr, _("    -E authcid=<authcid>\tSASL Authentication Identity (defaults to USER)\n"));
86 	fprintf( stderr, _("    -E authzid=<authzid>\tSASL Authorization Identity (defaults to none)\n"));
87 	fprintf( stderr, _("    -E secprops=<secprops>\tSASL Security Properties (defaults to none)\n"));
88 	tool_common_usage();
89 	exit( EXIT_FAILURE );
90 }
91 
92 
93 const char options[] = "abE:"
94 	"d:D:e:h:H:InNO:o:p:QR:U:vVw:WxX:y:Y:Z";
95 
96 int
handle_private_option(int i)97 handle_private_option( int i )
98 {
99 	switch ( i ) {
100 		char	*control, *cvalue;
101 	case 'E': /* vc extension */
102 		if( protocol == LDAP_VERSION2 ) {
103 			fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
104 				prog, protocol );
105 			exit( EXIT_FAILURE );
106 		}
107 
108 		/* should be extended to support comma separated list of
109 		 *	[!]key[=value] parameters, e.g.  -E !foo,bar=567
110 		 */
111 
112 		cvalue = NULL;
113 		if( optarg[0] == '!' ) {
114 			optarg++;
115 		}
116 
117 		control = optarg;
118 		if ( (cvalue = strchr( control, '=' )) != NULL ) {
119 			*cvalue++ = '\0';
120 		}
121 
122 		if (strcasecmp(control, "sasl") == 0) {
123 #if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
124 			if (vc_sasl != LDAP_SASL_NONE) {
125 				fprintf(stderr,
126 				    _("SASL option previously specified\n"));
127 				exit(EXIT_FAILURE);
128 			}
129 			if (cvalue == NULL) {
130 				fprintf(stderr,
131 					_("missing mode in SASL option\n"));
132 				exit(EXIT_FAILURE);
133 			}
134 
135 			switch (*cvalue) {
136 			case 'a':
137 			case 'A':
138 				vc_sasl = LDAP_SASL_AUTOMATIC;
139 				break;
140 			case 'i':
141 			case 'I':
142 				vc_sasl = LDAP_SASL_INTERACTIVE;
143 				break;
144 			case 'q':
145 			case 'Q':
146 				vc_sasl = LDAP_SASL_QUIET;
147 				break;
148 			default:
149 				fprintf(stderr,
150 					_("unknown mode %s in SASL option\n"), cvalue);
151 				exit(EXIT_FAILURE);
152 			}
153 #else
154 			fprintf(stderr,
155 				_("%s: not compiled with SASL support\n"), prog);
156 			exit(EXIT_FAILURE);
157 #endif
158 
159 		} else if (strcasecmp(control, "mech") == 0) {
160 #if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
161 			if (vc_sasl_mech) {
162 				fprintf(stderr,
163 				    _("SASL mech previously specified\n"));
164 				exit(EXIT_FAILURE);
165 			}
166 			if (cvalue == NULL) {
167 				fprintf(stderr,
168 					_("missing mech in SASL option\n"));
169 				exit(EXIT_FAILURE);
170 			}
171 
172 			vc_sasl_mech = ber_strdup(cvalue);
173 #else
174 #endif
175 
176 		} else if (strcasecmp(control, "realm") == 0) {
177 #if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
178 			if (vc_sasl_realm) {
179 				fprintf(stderr,
180 				    _("SASL realm previously specified\n"));
181 				exit(EXIT_FAILURE);
182 			}
183 			if (cvalue == NULL) {
184 				fprintf(stderr,
185 					_("missing realm in SASL option\n"));
186 				exit(EXIT_FAILURE);
187 			}
188 
189 			vc_sasl_realm = ber_strdup(cvalue);
190 #else
191 			fprintf(stderr,
192 				_("%s: not compiled with SASL support\n"), prog);
193 			exit(EXIT_FAILURE);
194 #endif
195 
196 		} else if (strcasecmp(control, "authcid") == 0) {
197 #if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
198 			if (vc_sasl_authcid) {
199 				fprintf(stderr,
200 				    _("SASL authcid previously specified\n"));
201 				exit(EXIT_FAILURE);
202 			}
203 			if (cvalue == NULL) {
204 				fprintf(stderr,
205 					_("missing authcid in SASL option\n"));
206 				exit(EXIT_FAILURE);
207 			}
208 
209 			vc_sasl_authcid = ber_strdup(cvalue);
210 #else
211 			fprintf(stderr,
212 				_("%s: not compiled with SASL support\n"), prog);
213 			exit(EXIT_FAILURE);
214 #endif
215 
216 		} else if (strcasecmp(control, "authzid") == 0) {
217 #if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
218 			if (vc_sasl_authzid) {
219 				fprintf(stderr,
220 				    _("SASL authzid previously specified\n"));
221 				exit(EXIT_FAILURE);
222 			}
223 			if (cvalue == NULL) {
224 				fprintf(stderr,
225 					_("missing authzid in SASL option\n"));
226 				exit(EXIT_FAILURE);
227 			}
228 
229 			vc_sasl_authzid = ber_strdup(cvalue);
230 #else
231 			fprintf(stderr,
232 				_("%s: not compiled with SASL support\n"), prog);
233 			exit(EXIT_FAILURE);
234 #endif
235 
236 		} else if (strcasecmp(control, "secprops") == 0) {
237 #if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
238 			if (vc_sasl_secprops) {
239 				fprintf(stderr,
240 				    _("SASL secprops previously specified\n"));
241 				exit(EXIT_FAILURE);
242 			}
243 			if (cvalue == NULL) {
244 				fprintf(stderr,
245 					_("missing secprops in SASL option\n"));
246 				exit(EXIT_FAILURE);
247 			}
248 
249 			vc_sasl_secprops = ber_strdup(cvalue);
250 #else
251 			fprintf(stderr,
252 				_("%s: not compiled with SASL support\n"), prog);
253 			exit(EXIT_FAILURE);
254 #endif
255 
256 		} else {
257 		    fprintf( stderr, _("Invalid Verify Credentials extension name: %s\n"), control );
258 		    usage();
259 		}
260 		break;
261 
262 	case 'a':  /* request authzid */
263 		req_authzid++;
264 		break;
265 
266 	case 'b':  /* request authzid */
267 		req_pp++;
268 		break;
269 
270 	default:
271 		return 0;
272 	}
273 	return 1;
274 }
275 
276 
277 int
main(int argc,char * argv[])278 main( int argc, char *argv[] )
279 {
280 	int		rc;
281 	LDAP		*ld = NULL;
282 	char		*matcheddn = NULL, *text = NULL, **refs = NULL;
283 	int rcode;
284 	char * diag = NULL;
285 	struct berval	*scookie = NULL;
286 	struct berval	*scred = NULL;
287 	int		id, code = 0;
288 	LDAPMessage	*res;
289 	LDAPControl	**ctrls = NULL;
290 	LDAPControl	**vcctrls = NULL;
291 	int nvcctrls = 0;
292 
293 	tool_init( TOOL_VC );
294 	prog = lutil_progname( "ldapvc", argc, argv );
295 
296 	/* LDAPv3 only */
297 	protocol = LDAP_VERSION3;
298 
299 	tool_args( argc, argv );
300 
301 	if (argc - optind > 0) {
302 		dn = argv[optind++];
303 	}
304 	if (argc - optind > 0) {
305 		cred.bv_val = strdup(argv[optind++]);
306 		cred.bv_len = strlen(cred.bv_val);
307 	}
308 	if (argc - optind > 0) {
309 		usage();
310 	}
311 	if (dn
312 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE
313            && !vc_sasl_mech
314 #endif
315            && !cred.bv_val)
316 	{
317 		cred.bv_val = strdup(getpassphrase(_("User's password: ")));
318 	    cred.bv_len = strlen(cred.bv_val);
319 	}
320 
321 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE
322     if (vc_sasl_mech && (vc_sasl == LDAP_SASL_NONE)) {
323 		vc_sasl = LDAP_SASL_AUTOMATIC;
324 	}
325 #endif
326 
327 	ld = tool_conn_setup( 0, 0 );
328 
329 	tool_bind( ld );
330 
331 	if ( dont ) {
332 		rc = LDAP_SUCCESS;
333 		goto skip;
334 	}
335 
336 	tool_server_controls( ld, NULL, 0 );
337 
338     if (req_authzid) {
339 		vcctrls = (LDAPControl **) malloc(3*sizeof(LDAPControl *));
340 		vcctrls[nvcctrls] = (LDAPControl *) malloc(sizeof(LDAPControl));
341 		vcctrls[nvcctrls]->ldctl_oid = ldap_strdup(LDAP_CONTROL_AUTHZID_REQUEST);
342 		vcctrls[nvcctrls]->ldctl_iscritical = 0;
343 		vcctrls[nvcctrls]->ldctl_value.bv_val = NULL;
344 		vcctrls[nvcctrls]->ldctl_value.bv_len = 0;
345 		vcctrls[++nvcctrls] = NULL;
346     }
347 
348     if (req_pp) {
349 		if (!vcctrls) vcctrls = (LDAPControl **) malloc(3*sizeof(LDAPControl *));
350 		vcctrls[nvcctrls] = (LDAPControl *) malloc(sizeof(LDAPControl));
351 		vcctrls[nvcctrls]->ldctl_oid = ldap_strdup(LDAP_CONTROL_PASSWORDPOLICYREQUEST);
352 		vcctrls[nvcctrls]->ldctl_iscritical = 0;
353 		vcctrls[nvcctrls]->ldctl_value.bv_val = NULL;
354 		vcctrls[nvcctrls]->ldctl_value.bv_len = 0;
355 		vcctrls[++nvcctrls] = NULL;
356     }
357 
358 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE
359 #ifdef HAVE_CYRUS_SASL
360     if (vc_sasl_mech) {
361 		int msgid;
362 		void * defaults;
363 		void * context = NULL;
364 		const char *rmech = NULL;
365 
366 		defaults = lutil_sasl_defaults(ld,
367 			vc_sasl_mech,
368 			vc_sasl_realm,
369 			vc_sasl_authcid,
370 			cred.bv_val,
371 			sasl_authz_id);
372 
373 		do {
374 			rc = ldap_verify_credentials_interactive(ld, dn, vc_sasl_mech,
375 				vcctrls, NULL, NULL,
376 				vc_sasl, lutil_sasl_interact, defaults, context,
377 				res, &rmech, &msgid);
378 
379 			if (rc != LDAP_SASL_BIND_IN_PROGRESS) break;
380 
381 			ldap_msgfree(res);
382 
383 			if (ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &res) == -1 || !res) {
384 				ldap_get_option(ld, LDAP_OPT_RESULT_CODE, (void*) &rc);
385 				ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*) &text);
386 				tool_perror( "ldap_verify_credentials_interactive", rc, NULL, NULL, text, NULL);
387 				ldap_memfree(text);
388 				tool_exit(ld, rc);
389 			}
390 		} while (rc == LDAP_SASL_BIND_IN_PROGRESS);
391 
392 	    lutil_sasl_freedefs(defaults);
393 
394 	    if( rc != LDAP_SUCCESS ) {
395 			ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*) &text);
396 		    tool_perror( "ldap_verify_credentials", rc, NULL, NULL, text, NULL );
397 		    rc = EXIT_FAILURE;
398 		    goto skip;
399 	    }
400 
401 	} else
402 #endif
403 #endif
404     {
405 	    rc = ldap_verify_credentials( ld,
406 		    NULL,
407 		    dn, NULL, cred.bv_val ? &cred: NULL, vcctrls,
408 		    NULL, NULL, &id );
409 
410 	    if( rc != LDAP_SUCCESS ) {
411 			ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*) &text);
412 		    tool_perror( "ldap_verify_credentials", rc, NULL, NULL, text, NULL );
413 		    rc = EXIT_FAILURE;
414 		    goto skip;
415 	    }
416 
417 	    for ( ; ; ) {
418 		    struct timeval	tv;
419 
420 		    if ( tool_check_abandon( ld, id ) ) {
421 			    tool_exit( ld, LDAP_CANCELLED );
422 		    }
423 
424 		    tv.tv_sec = 0;
425 		    tv.tv_usec = 100000;
426 
427 		    rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
428 		    if ( rc < 0 ) {
429 			    tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
430 			    tool_exit( ld, rc );
431 		    }
432 
433 		    if ( rc != 0 ) {
434 			    break;
435 		    }
436 	    }
437 	}
438 
439 	ldap_controls_free(vcctrls);
440 	vcctrls = NULL;
441 
442 	rc = ldap_parse_result( ld, res,
443 		&code, &matcheddn, &text, &refs, &ctrls, 0 );
444 
445 	if (rc == LDAP_SUCCESS) rc = code;
446 
447 	if (rc != LDAP_SUCCESS) {
448 		tool_perror( "ldap_parse_result", rc, NULL, matcheddn, text, refs );
449 		rc = EXIT_FAILURE;
450 		goto skip;
451 	}
452 
453 	rc = ldap_parse_verify_credentials( ld, res, &rcode, &diag, &scookie, &scred, &vcctrls );
454 	ldap_msgfree(res);
455 
456 	if (rc != LDAP_SUCCESS) {
457 		tool_perror( "ldap_parse_verify_credentials", rc, NULL, NULL, NULL, NULL );
458 		rc = EXIT_FAILURE;
459 		goto skip;
460 	}
461 
462 	if (rcode != LDAP_SUCCESS) {
463 		printf(_("Failed: %s (%d)\n"), ldap_err2string(rcode), rcode);
464 	}
465 
466 	if (diag && *diag) {
467 	    printf(_("Diagnostic: %s\n"), diag);
468 	}
469 
470 	if (vcctrls) {
471 		tool_print_ctrls( ld, vcctrls );
472 	}
473 
474 skip:
475 	if ( verbose || code != LDAP_SUCCESS ||
476 		( matcheddn && *matcheddn ) || ( text && *text ) || refs || ctrls )
477 	{
478 		printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code );
479 
480 		if( text && *text ) {
481 			printf( _("Additional info: %s\n"), text );
482 		}
483 
484 		if( matcheddn && *matcheddn ) {
485 			printf( _("Matched DN: %s\n"), matcheddn );
486 		}
487 
488 		if( refs ) {
489 			int i;
490 			for( i=0; refs[i]; i++ ) {
491 				printf(_("Referral: %s\n"), refs[i] );
492 			}
493 		}
494 
495 		if (ctrls) {
496 			tool_print_ctrls( ld, ctrls );
497 			ldap_controls_free( ctrls );
498 		}
499 	}
500 
501 	ber_memfree( text );
502 	ber_memfree( matcheddn );
503 	ber_memvfree( (void **) refs );
504 	ber_bvfree( scookie );
505 	ber_bvfree( scred );
506 	ber_memfree( diag );
507 	free( cred.bv_val );
508 
509 	/* disconnect from server */
510 	tool_exit( ld, code == LDAP_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE );
511 }
512