1/*	$NetBSD: ldappasswd.c,v 1.3 2021/08/14 16:14:49 christos Exp $	*/
2
3/* ldappasswd -- a tool for change LDAP passwords */
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 1998-2003 Kurt D. Zeilenga.
9 * Portions Copyright 1998-2001 Net Boolean Incorporated.
10 * Portions Copyright 2001-2003 IBM Corporation.
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted only as authorized by the OpenLDAP
15 * Public License.
16 *
17 * A copy of this license is available in the file LICENSE in the
18 * top-level directory of the distribution or, alternatively, at
19 * <http://www.OpenLDAP.org/license.html>.
20 */
21/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
22 * All rights reserved.
23 *
24 * Redistribution and use in source and binary forms are permitted
25 * provided that this notice is preserved and that due credit is given
26 * to the University of Michigan at Ann Arbor.  The name of the
27 * University may not be used to endorse or promote products derived
28 * from this software without specific prior written permission.  This
29 * software is provided ``as is'' without express or implied warranty.
30 */
31/* ACKNOWLEDGEMENTS:
32 * The original ldappasswd(1) tool was developed by Dave Storey (F5
33 * Network), based on other OpenLDAP client tools (which are, of
34 * course, based on U-MICH LDAP).  This version was rewritten
35 * by Kurt D. Zeilenga (based on other OpenLDAP client tools).
36 */
37
38#include <sys/cdefs.h>
39__RCSID("$NetBSD: ldappasswd.c,v 1.3 2021/08/14 16:14:49 christos Exp $");
40
41#include "portable.h"
42
43#include <stdio.h>
44
45#include <ac/stdlib.h>
46
47#include <ac/ctype.h>
48#include <ac/socket.h>
49#include <ac/string.h>
50#include <ac/time.h>
51#include <ac/unistd.h>
52
53#include <ldap.h>
54#include "lutil.h"
55#include "lutil_ldap.h"
56#include "ldap_defaults.h"
57
58#include "common.h"
59
60
61static struct berval newpw = { 0, NULL };
62static struct berval oldpw = { 0, NULL };
63
64static int   want_newpw = 0;
65static int   want_oldpw = 0;
66
67static char *oldpwfile = NULL;
68static char *newpwfile = NULL;
69
70void
71usage( void )
72{
73	fprintf( stderr, _("Change password of an LDAP user\n\n"));
74	fprintf( stderr,_("usage: %s [options] [user]\n"), prog);
75	fprintf( stderr, _("  user: the authentication identity, commonly a DN\n"));
76	fprintf( stderr, _("Password change options:\n"));
77	fprintf( stderr, _("  -a secret  old password\n"));
78	fprintf( stderr, _("  -A         prompt for old password\n"));
79	fprintf( stderr, _("  -t file    read file for old password\n"));
80	fprintf( stderr, _("  -s secret  new password\n"));
81	fprintf( stderr, _("  -S         prompt for new password\n"));
82	fprintf( stderr, _("  -T file    read file for new password\n"));
83	tool_common_usage();
84	exit( EXIT_FAILURE );
85}
86
87
88const char options[] = "a:As:St:T:"
89	"d:D:e:h:H:InNO:o:p:QR:U:vVw:WxX:y:Y:Z";
90
91int
92handle_private_option( int i )
93{
94	switch ( i ) {
95#if 0
96	case 'E': /* passwd extensions */ {
97		int		crit;
98		char	*control, *cvalue;
99		if( protocol == LDAP_VERSION2 ) {
100			fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
101			         prog, protocol );
102			exit( EXIT_FAILURE );
103		}
104
105		/* should be extended to support comma separated list of
106		 *	[!]key[=value] parameters, e.g.  -E !foo,bar=567
107		 */
108
109		crit = 0;
110		cvalue = NULL;
111		if( optarg[0] == '!' ) {
112			crit = 1;
113			optarg++;
114		}
115
116		control = optarg;
117		if ( (cvalue = strchr( control, '=' )) != NULL ) {
118			*cvalue++ = '\0';
119		}
120		fprintf( stderr, _("Invalid passwd extension name: %s\n"), control );
121		usage();
122		}
123#endif
124
125	case 'a':	/* old password (secret) */
126		oldpw.bv_val = strdup( optarg );
127		{
128			char* p;
129			for( p = optarg; *p != '\0'; p++ ) {
130				*p = '\0';
131			}
132		}
133		oldpw.bv_len = strlen( oldpw.bv_val );
134		break;
135
136	case 'A':	/* prompt for old password */
137		want_oldpw++;
138		break;
139
140	case 's':	/* new password (secret) */
141		newpw.bv_val = strdup( optarg );
142		{
143			char* p;
144			for( p = optarg; *p != '\0'; p++ ) {
145				*p = '\0';
146			}
147		}
148		newpw.bv_len = strlen( newpw.bv_val );
149		break;
150
151	case 'S':	/* prompt for user password */
152		want_newpw++;
153		break;
154
155	case 't':
156		oldpwfile = optarg;
157		break;
158
159	case 'T':
160		newpwfile = optarg;
161		break;
162
163	default:
164		return 0;
165	}
166	return 1;
167}
168
169
170int
171main( int argc, char *argv[] )
172{
173	int rc;
174	char	*user = NULL;
175
176	LDAP	       *ld = NULL;
177	struct berval bv = {0, NULL};
178	BerElement  *ber = NULL;
179
180	int id, code = LDAP_OTHER;
181	LDAPMessage *res;
182	char *matcheddn = NULL, *text = NULL, **refs = NULL;
183	char	*retoid = NULL;
184	struct berval *retdata = NULL;
185	LDAPControl **ctrls = NULL;
186
187    tool_init( TOOL_PASSWD );
188	prog = lutil_progname( "ldappasswd", argc, argv );
189
190	/* LDAPv3 only */
191	protocol = LDAP_VERSION3;
192
193	tool_args( argc, argv );
194
195	if( argc - optind > 1 ) {
196		usage();
197	} else if ( argc - optind == 1 ) {
198		user = strdup( argv[optind] );
199	} else {
200		user = NULL;
201	}
202
203	if( oldpwfile ) {
204		rc = lutil_get_filed_password( oldpwfile, &oldpw );
205		if( rc ) {
206			rc = EXIT_FAILURE;
207			goto done;
208		}
209	}
210
211	if( want_oldpw && oldpw.bv_val == NULL ) {
212		/* prompt for old password */
213		char *ckoldpw;
214		oldpw.bv_val = strdup(getpassphrase(_("Old password: ")));
215		ckoldpw = getpassphrase(_("Re-enter old password: "));
216
217		if( oldpw.bv_val == NULL || ckoldpw == NULL ||
218			strcmp( oldpw.bv_val, ckoldpw ))
219		{
220			fprintf( stderr, _("passwords do not match\n") );
221			rc = EXIT_FAILURE;
222			goto done;
223		}
224
225		oldpw.bv_len = strlen( oldpw.bv_val );
226	}
227
228	if( newpwfile ) {
229		rc = lutil_get_filed_password( newpwfile, &newpw );
230		if( rc ) {
231			rc = EXIT_FAILURE;
232			goto done;
233		}
234	}
235
236	if( want_newpw && newpw.bv_val == NULL ) {
237		/* prompt for new password */
238		char *cknewpw;
239		newpw.bv_val = strdup(getpassphrase(_("New password: ")));
240		cknewpw = getpassphrase(_("Re-enter new password: "));
241
242		if( newpw.bv_val == NULL || cknewpw == NULL ||
243			strcmp( newpw.bv_val, cknewpw ))
244		{
245			fprintf( stderr, _("passwords do not match\n") );
246			rc = EXIT_FAILURE;
247			goto done;
248		}
249
250		newpw.bv_len = strlen( newpw.bv_val );
251	}
252
253	ld = tool_conn_setup( 0, 0 );
254
255	tool_bind( ld );
256
257	if( user != NULL || oldpw.bv_val != NULL || newpw.bv_val != NULL ) {
258		/* build the password modify request data */
259		ber = ber_alloc_t( LBER_USE_DER );
260
261		if( ber == NULL ) {
262			perror( "ber_alloc_t" );
263			rc = EXIT_FAILURE;
264			goto done;
265		}
266
267		ber_printf( ber, "{" /*}*/ );
268
269		if( user != NULL ) {
270			ber_printf( ber, "ts",
271				LDAP_TAG_EXOP_MODIFY_PASSWD_ID, user );
272			free(user);
273		}
274
275		if( oldpw.bv_val != NULL ) {
276			ber_printf( ber, "tO",
277				LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, &oldpw );
278			free(oldpw.bv_val);
279		}
280
281		if( newpw.bv_val != NULL ) {
282			ber_printf( ber, "tO",
283				LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, &newpw );
284			free(newpw.bv_val);
285		}
286
287		ber_printf( ber, /*{*/ "N}" );
288
289		rc = ber_flatten2( ber, &bv, 0 );
290
291		if( rc < 0 ) {
292			perror( "ber_flatten2" );
293			rc = EXIT_FAILURE;
294			goto done;
295		}
296	}
297
298	if ( dont ) {
299		rc = LDAP_SUCCESS;
300		goto done;
301	}
302
303	tool_server_controls( ld, NULL, 0);
304
305	rc = ldap_extended_operation( ld,
306		LDAP_EXOP_MODIFY_PASSWD, bv.bv_val ? &bv : NULL,
307		NULL, NULL, &id );
308
309	ber_free( ber, 1 );
310
311	if( rc != LDAP_SUCCESS ) {
312		tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL );
313		rc = EXIT_FAILURE;
314		goto done;
315	}
316
317	for ( ; ; ) {
318		struct timeval	tv;
319
320		if ( tool_check_abandon( ld, id ) ) {
321			tool_exit( ld, LDAP_CANCELLED );
322		}
323
324		tv.tv_sec = 0;
325		tv.tv_usec = 100000;
326
327		rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
328		if ( rc < 0 ) {
329			tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
330			tool_exit( ld, rc );
331		}
332
333		if ( rc != 0 ) {
334			break;
335		}
336	}
337
338	rc = ldap_parse_result( ld, res,
339		&code, &matcheddn, &text, &refs, &ctrls, 0 );
340	if( rc != LDAP_SUCCESS ) {
341		tool_perror( "ldap_parse_result", rc, NULL, NULL, NULL, NULL );
342		rc = EXIT_FAILURE;
343		goto done;
344	}
345
346	rc = ldap_parse_extended_result( ld, res, &retoid, &retdata, 1 );
347	if( rc != LDAP_SUCCESS ) {
348		tool_perror( "ldap_parse_extended_result", rc, NULL, NULL, NULL, NULL );
349		rc = EXIT_FAILURE;
350		goto done;
351	}
352
353	if( retdata != NULL ) {
354		ber_tag_t tag;
355		char *s;
356		ber = ber_init( retdata );
357
358		if( ber == NULL ) {
359			perror( "ber_init" );
360			rc = EXIT_FAILURE;
361			goto done;
362		}
363
364		/* we should check the tag */
365		tag = ber_scanf( ber, "{a}", &s);
366
367		if( tag == LBER_ERROR ) {
368			perror( "ber_scanf" );
369		} else {
370			printf(_("New password: %s\n"), s);
371			ber_memfree( s );
372		}
373
374		ber_free( ber, 1 );
375
376	} else if ( code == LDAP_SUCCESS && newpw.bv_val == NULL ) {
377		tool_perror( "ldap_parse_extended_result", LDAP_DECODING_ERROR,
378			" new password expected", NULL, NULL, NULL );
379	}
380
381	if( verbose || code != LDAP_SUCCESS ||
382		( matcheddn && *matcheddn ) || ( text && *text ) || refs || ctrls )
383	{
384		printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code );
385
386		if( text && *text ) {
387			printf( _("Additional info: %s\n"), text );
388		}
389
390		if( matcheddn && *matcheddn ) {
391			printf( _("Matched DN: %s\n"), matcheddn );
392		}
393
394		if( refs ) {
395			int i;
396			for( i=0; refs[i]; i++ ) {
397				printf(_("Referral: %s\n"), refs[i] );
398			}
399		}
400
401		if( ctrls ) {
402			tool_print_ctrls( ld, ctrls );
403			ldap_controls_free( ctrls );
404		}
405	}
406
407	ber_memfree( text );
408	ber_memfree( matcheddn );
409	ber_memvfree( (void **) refs );
410	ber_memfree( retoid );
411	ber_bvfree( retdata );
412
413	rc = ( code == LDAP_SUCCESS ) ? EXIT_SUCCESS : EXIT_FAILURE;
414
415done:
416	/* disconnect from server */
417	tool_exit( ld, rc );
418}
419