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
56static int req_authzid = 0;
57static int req_pp = 0;
58
59#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
60#define LDAP_SASL_NONE (~0U)
61static unsigned vc_sasl = LDAP_SASL_NONE;
62static char *vc_sasl_realm = NULL;
63static char *vc_sasl_authcid = NULL;
64static char *vc_sasl_authzid = NULL;
65static char *vc_sasl_mech = NULL;
66static char *vc_sasl_secprops = NULL;
67#endif
68static char * dn = NULL;
69static struct berval cred = {0, NULL};
70
71void
72usage( 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
93const char options[] = "abE:"
94	"d:D:e:h:H:InNO:o:p:QR:U:vVw:WxX:y:Y:Z";
95
96int
97handle_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
277int
278main( 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
474skip:
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