]> git.sur5r.net Git - openldap/blobdiff - clients/tools/ldappasswd.c
fix previous commit (ITS#3905)
[openldap] / clients / tools / ldappasswd.c
index 26a621c98826d13bcb59f24b5071aaff758c1b50..31556695c0f310e4bbeeb7a6566bfc41ff59d7ec 100644 (file)
-/*
- *     Copyright 1998, David E. Storey, All rights reserved.
- *     This software is not subject to any license of The Murphy Group, Inc.
- *     or George Mason University.
+/* ldappasswd -- a tool for change LDAP passwords */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- *     Redistribution and use in source and binary forms are permitted only
- *     as authorized by the OpenLDAP Public License.  A copy of this
- *     license is available at http://www.OpenLDAP.org/license.html or
- *     in file LICENSE in the top-level directory of the distribution.
+ * Copyright 1998-2005 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 1998-2001 Net Boolean Incorporated.
+ * Portions Copyright 2001-2003 IBM Corporation.
+ * All rights reserved.
  *
- *     ldappasswd.c - program to modify passwords in an LDAP tree
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
  *
- *     Author: David E. Storey <dave@tamos.net>
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.  This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * The original ldappasswd(1) tool was developed by Dave Storey (F5
+ * Network), based on other OpenLDAP client tools (which are, of
+ * course, based on U-MICH LDAP).  This version was rewritten
+ * by Kurt D. Zeilenga (based on other OpenLDAP client tools).
  */
 
 #include "portable.h"
 
-#include <ctype.h>
 #include <stdio.h>
-#include <stdlib.h>
-#include <sys/time.h>
 
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
 #include <ac/string.h>
+#include <ac/time.h>
 #include <ac/unistd.h>
 
-#include <lber.h>
 #include <ldap.h>
-#include <lutil.h>
-#include <lutil_md5.h>
-#include <lutil_sha1.h>
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
 
-#include "ldapconfig.h"
+#include "common.h"
 
-/* local macros */
-#define CEILING(x)     ((double)x > (int)x ? (int)x + 1 : (int)x)
-#define STRDUP(x)      (x ? strcpy(malloc(strlen(x) + 1), x) : NULL)
 
-#define LDAP_PASSWD_ATTRIB "userPassword"
-#define LDAP_PASSWD_CONF   DEFAULT_SYSCONFDIR"/passwd.conf"
+static struct berval newpw = { 0, NULL };
+static struct berval oldpw = { 0, NULL };
 
-#define HS_NONE  0
-#define HS_PLAIN 1
-#define HS_CONV  2
+static int   want_newpw = 0;
+static int   want_oldpw = 0;
 
-typedef enum
-{
-       HASHTYPE_NONE,
-       HASHTYPE_CRYPT,
-       HASHTYPE_MD5,
-       HASHTYPE_SMD5,
-       HASHTYPE_SHA1,
-       HASHTYPE_SSHA1
-}
-HashTypes;
-
-typedef struct salt_t
-{
-       unsigned char  *salt;
-       unsigned int    len;
-}
-Salt;
+static char *oldpwfile = NULL;
+static char *newpwfile = NULL;
 
-typedef struct hash_t
+void
+usage( void )
 {
-       char           *name;
-       unsigned int    namesz;
-       char           *(*func) (const char *, Salt *);
-       unsigned char   takes_salt;
-       HashTypes       type;
-       HashTypes       type_salted;
-       unsigned int    default_salt_len;
+       fprintf( stderr, _("Change password of an LDAP user\n\n"));
+       fprintf( stderr,_("usage: %s [options] [user]\n"), prog);
+       fprintf( stderr, _("  user: the authentication identity, commonly a DN\n"));
+       fprintf( stderr, _("Password change options:\n"));
+       fprintf( stderr, _("  -a secret  old password\n"));
+       fprintf( stderr, _("  -A         prompt for old password\n"));
+       fprintf( stderr, _("  -t file    read file for old password\n"));
+       fprintf( stderr, _("  -s secret  new password\n"));
+       fprintf( stderr, _("  -S         prompt for new password\n"));
+       fprintf( stderr, _("  -T file    read file for new password\n"));
+       tool_common_usage();
+       exit( EXIT_FAILURE );
 }
-Hash;
-
-static int     noupdates = 0;
-static int     verbose = 0;
-static int     want_entryhash = 0;
-static int     auto_gen_pw = 0;
-
-/*** functions ***/
-
-/*
- * pw_encode() essentially base64 encodes a password and it's salt
- */
 
-char *
-pw_encode (unsigned char *passwd, Salt * salt, unsigned int len)
-{
-       int             salted = salt && salt->salt && salt->len;
-       int             b64_len = 0;
-       char           *base64digest = NULL;
-       unsigned char  *npasswd = passwd;
-
-       if (salted)
-       {
-               npasswd = (unsigned char *)malloc (len + salt->len);
-               memcpy (npasswd, passwd, len);
-               memcpy (&npasswd[len], salt->salt, salt->len);
-               len += salt->len;
-       }
-
-       b64_len = CEILING (len / 3) * 4 + 1;
-       base64digest = (char *)malloc (b64_len);
-       if (lutil_b64_ntop (npasswd, len, base64digest, b64_len) < 0)
-       {
-               free (base64digest);
-               base64digest = NULL;
-       }
 
-       if (salted)
-               free (npasswd);
+const char options[] = "a:As:St:T:"
+       "d:D:e:h:H:InO:p:QR:U:vVw:WxX:y:Y:Z";
 
-       return (base64digest);
-}
-
-/*
- * if you'd like to write a better salt generator, please, be my guest.
- */
-
-void
-make_salt (Salt * salt, unsigned int len)
+int
+handle_private_option( int i )
 {
-       struct timeval  tv;
+       switch ( i ) {
+#if 0
+       case 'E': /* passwd extensions */ {
+               int             crit;
+               char    *control, *cvalue;
+               if( protocol == LDAP_VERSION2 ) {
+                       fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
+                                prog, protocol );
+                       exit( EXIT_FAILURE );
+               }
 
-       if (!salt)
-               return;
+               /* should be extended to support comma separated list of
+                *      [!]key[=value] parameters, e.g.  -E !foo,bar=567
+                */
 
-       /* seed random number generator */
-       gettimeofday (&tv, NULL);
-       srand (tv.tv_usec);
+               crit = 0;
+               cvalue = NULL;
+               if( optarg[0] == '!' ) {
+                       crit = 1;
+                       optarg++;
+               }
 
-       salt->len = len;
-       salt->salt = (unsigned char *)malloc (len);
+               control = strdup( optarg );
+               if ( (cvalue = strchr( control, '=' )) != NULL ) {
+                       *cvalue++ = '\0';
+               }
+               fprintf( stderr, _("Invalid passwd extension name: %s\n"), control );
+               usage();
+               }
+#endif
 
-       for (len = 0; len < salt->len; len++)
-               salt->salt[len] = (tv.tv_usec ^ rand ()) & 0xff;
-}
+       case 'a':       /* old password (secret) */
+               oldpw.bv_val = strdup( optarg );
+               {
+                       char* p;
+                       for( p = optarg; *p != '\0'; p++ ) {
+                               *p = '\0';
+                       }
+               }
+               oldpw.bv_len = strlen( oldpw.bv_val );
+               break;
 
-/*
- * password generator
- */
+       case 'A':       /* prompt for old password */
+               want_oldpw++;
+               break;
 
-char *
-gen_pass (unsigned int len)
-{
-       const unsigned char autogen[] =
-               "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890.,";
-       int             i;
-       Salt            salt = {NULL, 0};
+       case 's':       /* new password (secret) */
+               newpw.bv_val = strdup (optarg);
+               {
+                       char* p;
+                       for( p = optarg; *p != '\0'; p++ ) {
+                               *p = '\0';
+                       }
+               }
+               newpw.bv_len = strlen( newpw.bv_val );
+               break;
 
-       make_salt (&salt, len);
-       for (i = 0; i < len; i++)
-               salt.salt[i] = autogen[salt.salt[i] % (sizeof (autogen) - 1)];
+       case 'S':       /* prompt for user password */
+               want_newpw++;
+               break;
 
-       return ((char *)salt.salt);
-}
+       case 't':
+               oldpwfile = optarg;
+               break;
 
-#ifdef SLAPD_CLEARTEXT
-char *
-hash_none (const char *pw_in, Salt * salt)
-{
-       return (STRDUP (pw_in));
-}
-#endif
+       case 'T':
+               newpwfile = optarg;
+               break;
 
-#ifdef SLAPD_CRYPT
-char *
-hash_crypt (const char *pw_in, Salt * salt)
-{
-       const unsigned char crypt64[] =
-               "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890./";
-       char   *crypted_pw = NULL;
-       Salt    lsalt;
-
-       if (salt && salt->salt && strlen ((char *)salt->salt) >= 2)
-       {
-               /* sanity check */
-               if (!(isalnum(salt->salt[0]) || salt->salt[0] == '.' || salt->salt[0] == '/'))
-                       salt->salt[0] = crypt64[salt->salt[0] % (sizeof (crypt64) - 1)];
-               if (!(isalnum(salt->salt[1]) || salt->salt[1] == '.' || salt->salt[1] == '/'))
-                       salt->salt[1] = crypt64[salt->salt[1] % (sizeof (crypt64) - 1)];
-
-               crypted_pw = crypt (pw_in, (char *)salt->salt);
-       }
-       else
-       {
-               make_salt (&lsalt, 2);
-               lsalt.salt[0] = crypt64[lsalt.salt[0] % (sizeof (crypt64) - 1)];
-               lsalt.salt[1] = crypt64[lsalt.salt[1] % (sizeof (crypt64) - 1)];
-               crypted_pw = crypt (pw_in, (char *)lsalt.salt);
-               free (lsalt.salt);
+       default:
+               return 0;
        }
-       return (STRDUP (crypted_pw));
+       return 1;
 }
-#endif
 
-char *
-hash_md5 (const char *pw_in, Salt * salt)
+
+int
+main( int argc, char *argv[] )
 {
-       lutil_MD5_CTX   MD5context;
-       unsigned char   MD5digest[16];
+       int rc;
+       char    *user = NULL;
 
-       lutil_MD5Init (&MD5context);
-       lutil_MD5Update (&MD5context, pw_in, strlen(pw_in));
-       if (salt && salt->salt && salt->len)
-               lutil_MD5Update (&MD5context, salt->salt, salt->len);
-       lutil_MD5Final (MD5digest, &MD5context);
+       LDAP           *ld = NULL;
+       struct berval bv = {0, NULL};
+       BerElement  *ber = NULL;
 
-       return (pw_encode (MD5digest, salt, sizeof (MD5digest)));
-}
+       int id, code = LDAP_OTHER;
+       LDAPMessage *res;
+       char *matcheddn = NULL, *text = NULL, **refs = NULL;
+       char    *retoid = NULL;
+       struct berval *retdata = NULL;
 
-char *
-hash_sha1 (const char *pw_in, Salt * salt)
-{
-       lutil_SHA1_CTX  SHA1context;
-       unsigned char   SHA1digest[20];
+    tool_init();
+       prog = lutil_progname( "ldappasswd", argc, argv );
 
-       lutil_SHA1Init (&SHA1context);
-       lutil_SHA1Update (&SHA1context, pw_in, strlen(pw_in));
-       if (salt && salt->salt && salt->len)
-               lutil_SHA1Update (&SHA1context, salt->salt, salt->len);
-       lutil_SHA1Final (SHA1digest, &SHA1context);
+       /* LDAPv3 only */
+       protocol = LDAP_VERSION3;
 
-       return (pw_encode (SHA1digest, salt, sizeof (SHA1digest)));
-}
+       tool_args( argc, argv );
 
-static Hash hashes[] =
-{
-#ifdef SLAPD_CLEARTEXT
-       {"none",  4, hash_none,  0, HASHTYPE_NONE,  HASHTYPE_NONE,  0},
-#endif
-#ifdef SLAPD_CRYPT
-       {"crypt", 5, hash_crypt, 1, HASHTYPE_CRYPT, HASHTYPE_CRYPT, 2},
-#endif
-       {"md5",   3, hash_md5,   0, HASHTYPE_MD5,   HASHTYPE_SMD5,  0},
-       {"smd5",  4, hash_md5,   1, HASHTYPE_SMD5,  HASHTYPE_SMD5,  4},
-       {"sha",   3, hash_sha1,  0, HASHTYPE_SHA1,  HASHTYPE_SSHA1, 0},
-       {"ssha",  4, hash_sha1,  1, HASHTYPE_SSHA1, HASHTYPE_SSHA1, 4},
-       {NULL,    0, NULL,       0, HASHTYPE_NONE,  HASHTYPE_NONE,  0}
-};
-
-int
-modify_dn (LDAP * ld, char *targetdn, char *pwattr, char *oldpw,
-          char *newpw, HashTypes htype, Salt * salt)
-{
-       int             ret = 0;
-       int             salted = salt->salt ? 1 : 0;
-       int             want_salt = salt->len && !salted;
-       char           *buf = NULL;
-       char           *hashed_pw = NULL;
-       char           *strvals[2];
-       LDAPMod         mod, *mods[2];
-
-       if (!ld || !targetdn || !newpw)
-               return (1);
-
-       /* auto-generate password */
-       if (auto_gen_pw)
-               newpw = gen_pass (auto_gen_pw);
-
-       /* handle salt */
-       if (want_salt)
-       {
-               make_salt (salt, salt->len);
-               htype = hashes[htype].type_salted;
+       if( argc - optind > 1 ) {
+               usage();
+       } else if ( argc - optind == 1 ) {
+               user = strdup( argv[optind] );
+       } else {
+               user = NULL;
        }
-       else if (hashes[htype].default_salt_len)
-       {
-               /* user chose a salted hash and needs a salt */
-               if (!salted)
-               {
-                       want_salt++;
-                       salt->len = hashes[htype].default_salt_len;
-                       make_salt (salt, salt->len);
-               }
-       }
-
-       /* hash password */
-       hashed_pw = hashes[htype].func (newpw, salt->len ? salt : NULL);
 
-       /* return salt back to it's original state */
-       if (want_salt)
-       {
-               free (salt->salt);
-               salt->salt = NULL;
+       if( oldpwfile ) {
+               rc = lutil_get_filed_password( oldpwfile, &oldpw );
+               if( rc ) {
+                       rc = EXIT_FAILURE;
+                       goto done;
+               }
        }
 
-       buf = (char *)malloc (hashes[htype].namesz + 3 + strlen (hashed_pw));
-       if (htype)
-               sprintf (buf, "{%s}%s", hashes[htype].name, hashed_pw);
-       else
-               sprintf (buf, "%s", hashed_pw);
+       if( want_oldpw && oldpw.bv_val == NULL ) {
+               /* prompt for old password */
+               char *ckoldpw;
+               oldpw.bv_val = strdup(getpassphrase(_("Old password: ")));
+               ckoldpw = getpassphrase(_("Re-enter old password: "));
 
-       if (verbose > 0)
-       {
-               printf ("%s", targetdn);
-               if (verbose > 1)
+               if( oldpw.bv_val == NULL || ckoldpw == NULL ||
+                       strcmp( oldpw.bv_val, ckoldpw ))
                {
-                       printf (":%s", buf);
-                       if (verbose > 2)
-                               printf (":%s", newpw);
+                       fprintf( stderr, _("passwords do not match\n") );
+                       rc = EXIT_FAILURE;
+                       goto done;
                }
-               printf ("\n");
-       }
 
-       strvals[0] = buf;
-       strvals[1] = NULL;
-       mod.mod_vals.modv_strvals = strvals;
-       mod.mod_type = pwattr;
-       mod.mod_op = LDAP_MOD_REPLACE;
-       mods[0] = &mod;
-       mods[1] =NULL;
-
-       if (!noupdates && (ret = ldap_modify_s (ld, targetdn, mods)) != LDAP_SUCCESS)
-               ldap_perror (ld, "ldap_modify_s");
+               oldpw.bv_len = strlen( oldpw.bv_val );
+       }
 
-       free (hashed_pw);
-       free (buf);
-       return (ret);
-}
+       if( newpwfile ) {
+               rc = lutil_get_filed_password( newpwfile, &newpw );
+               if( rc ) {
+                       rc = EXIT_FAILURE;
+                       goto done;
+               }
+       }
 
-void
-usage (char *s)
-{
-       fprintf (stderr, "Usage: %s [options] [filter]\n", s);
-       fprintf (stderr, "  -a attrib\tpassword attribute (default: %s)\n", LDAP_PASSWD_ATTRIB);
-       fprintf (stderr, "  -b basedn\tbasedn to perform searches\n");
-/*      fprintf (stderr, "  -C\t\tuse entry's current hash mechanism\n"); */
-       fprintf (stderr, "  -D binddn\tbind dn\n");
-       fprintf (stderr, "  -d level\tdebugging level\n");
-       fprintf (stderr, "  -E\t\tprompt for new password\n");
-       fprintf (stderr, "  -e passwd\tnew password\n");
-       fprintf (stderr, "  -g passlen\tauto-generate passwords with length pwlen\n");
-       fprintf (stderr, "  -H hash\thash type (default: crypt)\n");
-       fprintf (stderr, "  -h host\tldap server (default: localhost)\n");
-#ifdef HAVE_KERBEROS
-       fprintf (stderr, "  -K\t\tuse Kerberos step 1\n");
-       fprintf (stderr, "  -k\t\tuse Kerberos\n");
-#endif
-       fprintf (stderr, "  -l time\ttime limit\n");
-       fprintf (stderr, "  -n\t\tmake no modifications\n");
-       fprintf (stderr, "  -P version\tprotocol version (2 or 3)\n");
-       fprintf (stderr, "  -p port\tldap port\n");
-       fprintf (stderr, "  -s scope\tsearch scope: base, one, sub (default: sub)\n");
-       fprintf (stderr, "  -t targetdn\tdn to change password\n");
-       fprintf (stderr, "  -v\t\tverbose (more v's, more verbose)\n");
-       fprintf (stderr, "  -W\t\tprompt for bind password\n");
-       fprintf (stderr, "  -w passwd\tbind password (for simple authentication)\n");
-       fprintf (stderr, "  -Y saltlen\tsalt length to use\n");
-/*      fprintf (stderr, "  -y salt\tsalt to use\n"); */
-       fprintf (stderr, "  -z size\tsize limit\n");
-       exit (1);
-}
+       if( want_newpw && newpw.bv_val == NULL ) {
+               /* prompt for new password */
+               char *cknewpw;
+               newpw.bv_val = strdup(getpassphrase(_("New password: ")));
+               cknewpw = getpassphrase(_("Re-enter new password: "));
 
-int
-main (int argc, char *argv[])
-{
-       char           *base = NULL;
-       char           *binddn = NULL;
-       char           *bindpw = NULL;
-       char           *filtpattern = NULL;
-       char           *ldaphost = NULL;
-       char           *targetdn = NULL;
-       char           *pwattr = LDAP_PASSWD_ATTRIB;
-       char           *newpw = NULL;
-       int             authmethod = LDAP_AUTH_SIMPLE;
-       int             hashtype = HASHTYPE_CRYPT;
-       int             i, j;
-       int             ldapport = 0;
-       int             debug = 0;
-       int             scope = LDAP_SCOPE_SUBTREE;
-       int             sizelimit = -1;
-       int             timelimit = -1;
-       int             version = -1;
-       int             want_bindpw = 0;
-       int             want_newpw = 0;
-       LDAP           *ld;
-       Salt            salt;
-
-       salt.salt = NULL;
-       salt.len = 0;
-
-       if (argc == 1)
-               usage (argv[0]);
-
-       while ((i = getopt (argc, argv, "a:b:C:D:d:Ee:g:H:h:Kkl:nP:p:s:t:vWw:Y:y:z:")) != EOF)
-       {
-               switch (i)
+               if( newpw.bv_val == NULL || cknewpw == NULL ||
+                       strcmp( newpw.bv_val, cknewpw ))
                {
-               case 'a':       /* password attribute */
-                       pwattr = STRDUP (optarg);
-                       break;
+                       fprintf( stderr, _("passwords do not match\n") );
+                       rc = EXIT_FAILURE;
+                       goto done;
+               }
 
-               case 'b':       /* base search dn */
-                       base = STRDUP (optarg);
-                       break;
+               newpw.bv_len = strlen( newpw.bv_val );
+       }
 
-               case 'C':
-                       want_entryhash++;
-                       break;
+       if ( pw_file ) {
+               rc = lutil_get_filed_password( pw_file, &passwd );
+               if( rc ) {
+                       rc = EXIT_FAILURE;
+                       goto done;
+               }
 
-               case 'D':       /* bind distinguished name */
-                       binddn = STRDUP (optarg);
-                       break;
+       } else if ( want_bindpw ) {
+               passwd.bv_val = getpassphrase( _("Enter LDAP Password: ") );
+               passwd.bv_len = passwd.bv_val ? strlen( passwd.bv_val ) : 0;
+       }
 
-               case 'd':       /* debugging option */
-                       debug |= atoi (optarg);
-                       break;
+       ld = tool_conn_setup( 0, 0 );
 
-               case 'E':       /* prompt for new password */
-                       want_newpw++;
-                       break;
+       tool_bind( ld );
 
-               case 'e':       /* new password */
-                       newpw = STRDUP (optarg);
-                       break;
+       if ( assertion || authzid || manageDSAit || noop ) {
+               tool_server_controls( ld, NULL, 0 );
+       }
 
-               case 'g':
-                       auto_gen_pw = strtol (optarg, NULL, 10);
-                       break;
+       if( user != NULL || oldpw.bv_val != NULL || newpw.bv_val != NULL ) {
+               /* build change password control */
+               ber = ber_alloc_t( LBER_USE_DER );
 
-               case 'H':       /* hashes */
-                       for (j = 0; hashes[j].name; j++)
-                       {
-                               if (!strncasecmp (optarg, hashes[j].name, hashes[j].namesz))
-                               {
-                                       hashtype = hashes[j].type;
-                                       break;
-                               }
-                       }
+               if( ber == NULL ) {
+                       perror( "ber_alloc_t" );
+                       rc = EXIT_FAILURE;
+                       goto done;
+               }
 
-                       if (!hashes[j].name)
-                       {
-                               fprintf (stderr, "hash type: %s is unknown\n", optarg);
-                               usage (argv[0]);
-                       }
-                       break;
+               ber_printf( ber, "{" /*}*/ );
 
-               case 'h':       /* ldap host */
-                       ldaphost = STRDUP (optarg);
-                       break;
+               if( user != NULL ) {
+                       ber_printf( ber, "ts",
+                               LDAP_TAG_EXOP_MODIFY_PASSWD_ID, user );
+                       free(user);
+               }
 
-               case 'K':       /* use kerberos bind, 1st part only */
-#ifdef HAVE_KERBEROS
-                       authmethod = LDAP_AUTH_KRBV41;
-#else
-                       fprintf (stderr, "%s was not compiled with Kerberos support\n", argv[0]);
-#endif
-                       break;
+               if( oldpw.bv_val != NULL ) {
+                       ber_printf( ber, "tO",
+                               LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, &oldpw );
+                       free(oldpw.bv_val);
+               }
 
-               case 'k':       /* use kerberos bind */
-#ifdef HAVE_KERBEROS
-                       authmethod = LDAP_AUTH_KRBV4;
-#else
-                       fprintf (stderr, "%s was not compiled with Kerberos support\n", argv[0]);
-#endif
-                       break;
+               if( newpw.bv_val != NULL ) {
+                       ber_printf( ber, "tO",
+                               LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, &newpw );
+                       free(newpw.bv_val);
+               }
 
-               case 'l':       /* time limit */
-                       timelimit = strtol (optarg, NULL, 10);
-                       break;
+               ber_printf( ber, /*{*/ "N}" );
 
-               case 'n':       /* don't update entry(s) */
-                       noupdates++;
-                       break;
+               rc = ber_flatten2( ber, &bv, 0 );
 
-               case 'P':
-                       switch(optarg[0])
-                       {
-                       case '2':
-                               version = LDAP_VERSION2;
-                               break;
-                       case '3':
-                               version = LDAP_VERSION3;
-                               break;
-                       }
-                       break;
+               if( rc < 0 ) {
+                       perror( "ber_flatten2" );
+                       rc = EXIT_FAILURE;
+                       goto done;
+               }
+       }
 
-               case 'p':       /* ldap port */
-                       ldapport = strtol (optarg, NULL, 10);
-                       break;
+       if ( not ) {
+               rc = LDAP_SUCCESS;
+               goto done;
+       }
 
-               case 's':       /* scope */
-                       if (strncasecmp (optarg, "base", 4) == 0)
-                               scope = LDAP_SCOPE_BASE;
-                       else if (strncasecmp (optarg, "one", 3) == 0)
-                               scope = LDAP_SCOPE_ONELEVEL;
-                       else if (strncasecmp (optarg, "sub", 3) == 0)
-                               scope = LDAP_SCOPE_SUBTREE;
-                       else
-                       {
-                               fprintf (stderr, "scope should be base, one, or sub\n");
-                               usage (argv[0]);
-                       }
-                       break;
+       rc = ldap_extended_operation( ld,
+               LDAP_EXOP_MODIFY_PASSWD, bv.bv_val ? &bv : NULL, 
+               NULL, NULL, &id );
 
-               case 't':       /* target dn */
-                       targetdn = STRDUP (optarg);
-                       break;
+       ber_free( ber, 1 );
 
-               case 'v':       /* verbose */
-                       verbose++;
-                       break;
+       if( rc != LDAP_SUCCESS ) {
+               ldap_perror( ld, "ldap_extended_operation" );
+               rc = EXIT_FAILURE;
+               goto done;
+       }
 
-               case 'W':       /* promt for bind password */
-                       want_bindpw++;
-                       break;
+       for ( ; ; ) {
+               struct timeval  tv;
 
-               case 'w':       /* bind password */
-                       bindpw = STRDUP (optarg);
-                       break;
+               if ( tool_check_abandon( ld, id ) ) {
+                       return LDAP_CANCELLED;
+               }
 
-               case 'Y':       /* salt length */
-                       salt.len = strtol (optarg, NULL, 10);
-                       break;
+               tv.tv_sec = 0;
+               tv.tv_usec = 100000;
 
-               case 'y':       /* user specified salt */
-                       salt.len = strlen (optarg);
-                       salt.salt = (unsigned char *)STRDUP (optarg);
-                       break;
+               rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
+               if ( rc < 0 ) {
+                       ldap_perror( ld, "ldappasswd: ldap_result" );
+                       return rc;
+               }
 
-               case 'z':       /* time limit */
-                       sizelimit = strtol (optarg, NULL, 10);
+               if ( rc != 0 ) {
                        break;
-
-               default:
-                       usage (argv[0]);
                }
        }
 
-       /* grab filter */
-       if (!(argc - optind < 1))
-               filtpattern = STRDUP (argv[optind]);
-
-       /* check for target(s) */
-       if (!filtpattern && !targetdn)
-               targetdn = binddn;
+       rc = ldap_parse_result( ld, res,
+               &code, &matcheddn, &text, &refs, NULL, 0 );
 
-       /* handle bind password */
-       if (want_bindpw)
-               bindpw = strdup (getpass ("Enter LDAP password: "));
+       if( rc != LDAP_SUCCESS ) {
+               ldap_perror( ld, "ldap_parse_result" );
+               rc = EXIT_FAILURE;
+               goto done;
+       }
 
-       /* handle new password */
-       if (!newpw)
-       {
-               char *cknewpw;
-               newpw = strdup (getpass ("New password: "));
-               cknewpw = getpass ("Re-enter new password: ");
+       rc = ldap_parse_extended_result( ld, res, &retoid, &retdata, 1 );
 
-               if (strncmp (newpw, cknewpw, strlen (newpw)))
-               {
-                       fprintf (stderr, "passwords do not match\n");
-                       exit (1);
-               }
+       if( rc != LDAP_SUCCESS ) {
+               ldap_perror( ld, "ldap_parse_result" );
+               rc = EXIT_FAILURE;
+               goto done;
        }
 
-       if ( debug ) {
-               lber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug );
-               ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug );
-       }
+       if( retdata != NULL ) {
+               ber_tag_t tag;
+               char *s;
+               ber = ber_init( retdata );
 
-       /* connect to server */
-       if ((ld = ldap_open (ldaphost, ldapport)) == NULL)
-       {
-               perror (ldaphost);
-               exit (1);
-       }
+               if( ber == NULL ) {
+                       perror( "ber_init" );
+                       rc = EXIT_FAILURE;
+                       goto done;
+               }
 
-       /* set options */
-       if( timelimit != -1 ) {
-               ldap_set_option (ld, LDAP_OPT_TIMELIMIT, (void *)&timelimit);
-       }
-       if( sizelimit != -1 ) {
-               ldap_set_option (ld, LDAP_OPT_SIZELIMIT, (void *)&sizelimit);
-       }
+               /* we should check the tag */
+               tag = ber_scanf( ber, "{a}", &s);
 
-       /* this seems prudent */
-       {
-               int deref = LDAP_DEREF_NEVER;
-               ldap_set_option( ld, LDAP_OPT_DEREF, &deref);
-       }
+               if( tag == LBER_ERROR ) {
+                       perror( "ber_scanf" );
+               } else {
+                       printf(_("New password: %s\n"), s);
+                       free( s );
+               }
 
-       if( version != -1 ) {
-               ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+               ber_free( ber, 1 );
        }
 
-       /* authenticate to server */
-       if (ldap_bind_s (ld, binddn, bindpw, authmethod) != LDAP_SUCCESS)
-       {
-               ldap_perror (ld, "ldap_bind");
-               exit (1);
-       }
+       if( verbose || code != LDAP_SUCCESS || matcheddn || text || refs ) {
+               printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code );
 
-       if (targetdn)
-       {
-               if (want_entryhash)
-               {
-                       /* insert code here =) */
+               if( text && *text ) {
+                       printf( _("Additional info: %s\n"), text );
                }
-               else
-                       modify_dn (ld, targetdn, pwattr, NULL, newpw, hashtype, &salt);
-       }
 
-       if (filtpattern)
-       {
-               char            filter[BUFSIZ];
-               LDAPMessage    *result = NULL, *e = NULL;
-               char           *attrs[3] = {"dn", NULL, NULL};
-               attrs[1] = pwattr;
-
-               /* search */
-               sprintf (filter, "%s", filtpattern);
-               i = ldap_search_s (ld, base, scope, filter, attrs, 0, &result);
-               if (i != LDAP_SUCCESS &&
-                   i != LDAP_TIMELIMIT_EXCEEDED &&
-                   i != LDAP_SIZELIMIT_EXCEEDED)
-               {
-                       ldap_perror (ld, "ldap_search_s");
-                       exit (1);
+               if( matcheddn && *matcheddn ) {
+                       printf( _("Matched DN: %s\n"), matcheddn );
                }
 
-               for (e = ldap_first_entry (ld, result); e; e = ldap_next_entry (ld, e))
-               {
-                       char *dn = ldap_get_dn (ld, e);
-                       if (dn)
-                       {
-                               struct berval **pw_vals = ldap_get_values_len (ld, e, pwattr);
-                               modify_dn (ld, dn, pwattr, pw_vals ? pw_vals[0]->bv_val : NULL, newpw, hashtype, &salt);
-                               if (pw_vals)
-                                       ldap_value_free_len (pw_vals);
-                               free (dn);
+               if( refs ) {
+                       int i;
+                       for( i=0; refs[i]; i++ ) {
+                               printf(_("Referral: %s\n"), refs[i] );
                        }
                }
        }
 
-       /* disconnect from server */
-       ldap_unbind (ld);
-       exit(0);
+       ber_memfree( text );
+       ber_memfree( matcheddn );
+       ber_memvfree( (void **) refs );
+       ber_memfree( retoid );
+       ber_bvfree( retdata );
 
-       /* unreached */
-       return (0);
+       rc = ( code == LDAP_SUCCESS ) ? EXIT_SUCCESS : EXIT_FAILURE;
+
+done:
+       /* disconnect from server */
+       tool_unbind( ld ); 
+       tool_destroy();
+       return rc;
 }