From: Kurt Zeilenga Date: Sun, 6 Dec 1998 03:44:28 +0000 (+0000) Subject: Add ldappasswd program contributed by David E. Storey X-Git-Tag: OPENLDAP_SLAPD_BACK_LDAP~960 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=f0a32aed71e58c21c4283bac6d8ed4218d1965cb;p=openldap Add ldappasswd program contributed by David E. Storey --- diff --git a/clients/tools/Makefile.in b/clients/tools/Makefile.in index dc0a10e679..806ea35f3b 100644 --- a/clients/tools/Makefile.in +++ b/clients/tools/Makefile.in @@ -1,8 +1,8 @@ ## ## Makefile for LDAP tools ## -SRCS = ldapsearch.c ldapmodify.c ldapdelete.c ldapmodrdn.c -OBJS = ldapsearch.o ldapmodify.o ldapdelete.o ldapmodrdn.o +SRCS = ldapsearch.c ldapmodify.c ldapdelete.c ldapmodrdn.c ldappasswd.c +OBJS = ldapsearch.o ldapmodify.o ldapdelete.o ldapmodrdn.o ldappasswd.o XLIBS = $(KRB_LIBS) @@ -11,7 +11,7 @@ LDAP_LIBDIR= ../../libraries XSRCS = ldsversion.c ldmversion.c lddversion.c ldrversion.c -PROGRAMS = ldapsearch ldapmodify ldapdelete ldapmodrdn ldapadd +PROGRAMS = ldapsearch ldapmodify ldapdelete ldapmodrdn ldapadd ldappasswd ldapsearch: ldsversion.o $(LTLINK) -o $@ ldapsearch.o ldsversion.o $(LIBS) @@ -25,6 +25,9 @@ ldapdelete: lddversion.o ldapmodrdn: ldrversion.o $(LTLINK) -o $@ ldapmodrdn.o ldrversion.o $(LIBS) +ldappasswd: ldappasswd.o + $(LTLINK) -o $@ ldappasswd.o $(LIBS) @LUTIL_LIBS@ $(LDAP_LIBPATH) -llutil + ldapadd: ldapmodify $(RM) $@ $(LN) ldapmodify ldapadd @@ -67,5 +70,6 @@ install-local: FORCE $(LTINSTALL) $(INSTALLFLAGS) -m 755 ldapmodify $(bindir) $(LTINSTALL) $(INSTALLFLAGS) -m 755 ldapdelete $(bindir) $(LTINSTALL) $(INSTALLFLAGS) -m 755 ldapmodrdn $(bindir) + $(LTINSTALL) $(INSTALLFLAGS) -m 755 ldappasswd $(bindir) $(RM) $(bindir)/ldapadd $(LN) $(bindir)/ldapmodify $(bindir)/ldapadd diff --git a/clients/tools/ldappasswd.c b/clients/tools/ldappasswd.c new file mode 100644 index 0000000000..2214938b6f --- /dev/null +++ b/clients/tools/ldappasswd.c @@ -0,0 +1,400 @@ +/* + * 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. + * + * 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. + * + * ldappasswd.c - program to modify passwords in an LDAP tree + * + * Created: 1998-11-26 + * Author: David E. Storey + * Last Modified: 1998-12-05 + * + * ToDo: passwd style change of password (termcap?) + * option for referral handling + * cracklib support? + * kerberos support? (is this really necessary?) + * update "shadow" fields? + * create/view/change password policies? + * + * Note: I am totally FOR comments and suggestions! + */ + +#include "portable.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "ldapconfig.h" + +#define LDAP_PASSWD_ATTRIB "userPassword" + +typedef enum { + HASHTYPE_NONE, + HASHTYPE_CRYPT, + HASHTYPE_MD5, + HASHTYPE_SHA1 +} HashTypes; + +struct hash_t { + char *name; + int namesz; + int (*func)(const char *, char *); + HashTypes type; +}; + +const char crypt64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./"; +char *base = NULL; +char *binddn = NULL; +char *bindpw = NULL; +char *ldaphost = "localhost"; +char *pwattr = LDAP_PASSWD_ATTRIB; +char *targetdn = NULL; +char *filtpattern = NULL; +int ldapport = LDAP_PORT; +int noupdates = 0; +int verbose = 0; +int hashtype = HASHTYPE_CRYPT; +int scope = LDAP_SCOPE_SUBTREE; + +/*** functions ***/ + +/* + * if you'd like to write a better salt generator, please, be my guest. + * I just needed *something*. It's actually halfway effective for small, + * two character salts and it can come up with sequentially different + * salts. + */ + +void +crypt_make_salt(char *salt) +{ + struct timeval tv; + int i; + char t_salt[5]; + + /* grab current time */ + gettimeofday(&tv, (struct timezone *) 0); + i += tv.tv_usec + (int)&salt; + strncpy(t_salt, (char *)&i, sizeof(i)); + + for (i = 0; i < sizeof(i); i++) + salt[i] = crypt64[t_salt[i] % (sizeof(crypt64) - 1)]; + salt[i] = '\0'; +} + +int +hash_none(const char *pw_in, char *pw_out) +{ + strcpy(pw_out, pw_in); + return(1); +} + +int +hash_crypt(const char *pw_in, char *pw_out) +{ + char salt[5]; + crypt_make_salt(salt); + strcpy(pw_out, crypt(pw_in, salt)); + return(1); +} + +int +hash_md5(const char *pw_in, char *pw_out) +{ + lutil_MD5_CTX MD5context; + unsigned char MD5digest[16]; + char base64digest[25]; /* ceiling(sizeof(input)/3) * 4 + 1 */ + + lutil_MD5Init(&MD5context); + lutil_MD5Update(&MD5context, (unsigned char *)pw_in, strlen(pw_in)); + lutil_MD5Final(MD5digest, &MD5context); + if (lutil_b64_ntop(MD5digest, sizeof(MD5digest), base64digest, sizeof(base64digest)) < 0) + return (0); + + strcpy(pw_out, base64digest); + return(1); +} + +int +hash_sha1(const char *pw_in, char *pw_out) +{ + lutil_SHA1_CTX SHA1context; + unsigned char SHA1digest[20]; + char base64digest[29]; /* ceiling(sizeof(input)/3) * 4 + 1 */ + + lutil_SHA1Init(&SHA1context); + lutil_SHA1Update(&SHA1context, (unsigned char *)pw_in, strlen(pw_in)); + lutil_SHA1Final(SHA1digest, &SHA1context); + if (lutil_b64_ntop(SHA1digest, sizeof(SHA1digest), base64digest, sizeof(base64digest)) < 0) + return(0); + + strcpy(pw_out, base64digest); + return(1); +} + +static struct hash_t hashes[] = { + {"none", 4, hash_none, HASHTYPE_NONE}, + {"crypt", 5, hash_crypt, HASHTYPE_CRYPT}, + {"md5", 3, hash_md5, HASHTYPE_MD5}, + {"sha", 3, hash_sha1, HASHTYPE_SHA1}, + {NULL, 0, NULL, HASHTYPE_NONE} +}; + +int +modify_dn(LDAP *ld, char *targetdn, char *newpw) +{ + int ret = 0; + char hashed_pw[128] = {'\0'}; + char buf[128] = {'\0'}; + char *strvals[2] = {buf, NULL}; + LDAPMod mod, *mods[2] = {&mod, NULL}; + + if (!ld || !targetdn || !newpw) + return(1); + + /* hash password */ + hashes[hashtype].func(newpw, hashed_pw); + if (hashtype) + sprintf(buf, "{%s}%s", hashes[hashtype].name, hashed_pw); + else + sprintf(buf, "%s", hashed_pw); + + if (verbose > 0) + { + printf("%s", targetdn); + if (verbose > 1) + { + printf(":%s", buf); + if (verbose > 2) + printf(":%s", newpw); + } + printf("\n"); + } + + mod.mod_vals.modv_strvals = strvals; + mod.mod_type = pwattr; + mod.mod_op = LDAP_MOD_REPLACE; + + if (!noupdates && (ret = ldap_modify_s(ld, targetdn, mods)) != LDAP_SUCCESS) + ldap_perror(ld, "ldap_modify_s"); + return(ret); +} + +void +usage(char *s) +{ + fprintf(stderr, "usage: %s [options] [filter]\n", s); + fprintf(stderr, " -a attrib password attribute (default: userPassword)\n"); + fprintf(stderr, " -b basedn basedn to perform searches\n"); + fprintf(stderr, " -c hash hash type: none, crypt, md5, sha (default: crypt)\n"); + fprintf(stderr, " -D binddn bind dn\n"); + fprintf(stderr, " -d level debugging level\n"); + fprintf(stderr, " -h host ldap server (default: localhost)\n"); + fprintf(stderr, " -l time time limit\n"); + fprintf(stderr, " -n make no modifications\n"); + fprintf(stderr, " -p port ldap port\n"); + fprintf(stderr, " -s scope search scope: base, one, sub (default: sub)\n"); + fprintf(stderr, " -t targetdn dn to change password\n"); + fprintf(stderr, " -W newpass new password\n"); + fprintf(stderr, " -w passwd bind password (for simple authentication)\n"); + fprintf(stderr, " -v verbose\n"); + fprintf(stderr, " -z size size limit\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + char *newpw = NULL; + int i, j; + int sizelimit = LDAP_NO_LIMIT; + int timelimit = LDAP_NO_LIMIT; + LDAP *ld; + + while ((i = getopt(argc, argv, "D:W:a:b:c:d:h:l:np:s:t:vw:z:")) != EOF) + { + switch(i) + { + case 'D': /* bind distinguished name */ + binddn = strdup(optarg); + break; + + case 'W': /* new password */ + if (optarg) + newpw = strdup(optarg); + break; + + case 'a': /* password attribute */ + if (optarg) + pwattr = strdup(optarg); + break; + + case 'b': /* base search dn */ + if (optarg) + base = strdup(optarg); + break; + + case 'c': /* hashes */ + for (j = 0; hashes[j].name; j++) + { + if (!strncasecmp(optarg, hashes[j].name, hashes[j].namesz)) + { + hashtype = hashes[j].type; + break; + } + } + + if (!hashes[j].name) + { + fprintf(stderr, "hash type: %s is unknown\n", optarg); + usage(argv[0]); + } + break; + + case 'd': /* debugging option */ +#ifdef LDAP_DEBUG + ldap_debug = lber_debug = atoi(optarg); /* */ +#else + fprintf(stderr, "compile with -DLDAP_DEBUG for debugging\n"); +#endif + break; + + case 'h': /* ldap host */ + if (optarg) + ldaphost = strdup(optarg); + break; + + case 'l': /* time limit */ + if (optarg) + timelimit = strtol(optarg, NULL, 10); + break; + + case 'n': /* don't update entry(s) */ + noupdates++; + break; + + case 'p': /* ldap port */ + if (optarg) + ldapport = strtol(optarg, NULL, 10); + break; + + 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; + + case 't': /* password type */ + if (optarg) + targetdn = strdup(optarg); + else + targetdn = binddn; + break; + + case 'v': /* verbose */ + verbose++; + break; + + case 'w': /* bind password */ + bindpw = strdup(optarg); + break; + + case 'z': /* time limit */ + if (optarg) + sizelimit = strtol(optarg, NULL, 10); + break; + + default: + usage(argv[0]); + } + } + + if (!(argc - optind < 1)) + filtpattern = strdup(argv[optind]); + + if (!filtpattern && !targetdn) + { + fprintf(stderr, "No filter or targetdn(-t)\n"); + usage(argv[0]); + } + + if (!newpw) + { + fprintf(stderr, "Need a password (-W)\n"); + usage(argv[0]); + } + + /* connect to server */ + if ((ld = ldap_open(ldaphost, ldapport)) == NULL) + { + perror(ldaphost); + return(1); + } + + /* set options */ + ldap_set_option(ld, LDAP_OPT_TIMELIMIT, (void *)&timelimit); + ldap_set_option(ld, LDAP_OPT_SIZELIMIT, (void *)&sizelimit); + + /* authenticate to server */ + if (ldap_bind_s(ld, binddn, bindpw, LDAP_AUTH_SIMPLE) != LDAP_SUCCESS) + { + ldap_perror(ld, "ldap_bind"); + return(1); + } + + if (filtpattern) + { + char filter[BUFSIZ]; + LDAPMessage *result = NULL, *e = NULL; + char *attrs[] = {"dn", NULL}; + + /* search */ + sprintf(filter, "%s", filtpattern); + i = ldap_search_s(ld, base, scope, filter, attrs, 1, &result); + if (i != LDAP_SUCCESS && i != LDAP_TIMELIMIT_EXCEEDED && i != LDAP_SIZELIMIT_EXCEEDED) + { + ldap_perror(ld, "ldap_search_s"); + return(1); + } + + for (e = ldap_first_entry(ld, result); e; e = ldap_next_entry(ld, e)) + { + char *dn = ldap_get_dn(ld, e); + if (dn) + { + modify_dn(ld, dn, newpw); + free(dn); + } + } + } + + if (targetdn) + modify_dn(ld, targetdn, newpw); + + /* disconnect from server */ + ldap_unbind(ld); + return(0); +} diff --git a/doc/man/man1/ldappasswd.1 b/doc/man/man1/ldappasswd.1 new file mode 100644 index 0000000000..bb86553eb9 --- /dev/null +++ b/doc/man/man1/ldappasswd.1 @@ -0,0 +1,118 @@ +.TH LDAPPASSWD 1 "5 December 1998" "LDAPPasswd" +.SH NAME +ldappasswd \- change the password of an LDAP entry +.SH SYNOPSIS +.B ldappasswd +[\c +.BI \-a \ passwdattribute\fR] +[\c +.BI \-b \ searchbase\fR] +[\c +.BI \-c \ none\fR\||\|\fIcrypt\fR\||\|\fImd5\fR\||\|\fIsha\fR] +[\c +.BI \-D \ binddn\fR] +[\c +.BI \-d \ debuglevel\fR] +[\c +.BI \-h \ ldaphost\fR] +[\c +.BI \-l \ searchtime\fR] +[\c +.B \-n\fR] +[\c +.BI \-p \ ldapport\fR] +[\c +.BI \-s \ base\fR\||\|\fIone\fR\||\|\fIsub\fR] +[\c +.BR \-t \ [\fItargetdn\fR]\ ] +[\c +.B \-v\fR] +[\c +.BI \-W \ newpasswd\fR] +[\c +.BI \-w \ passwd\fR] +[\c +.BI \-z \ searchsize\fR] +[\fIfilter\fR] +.SH DESCRIPTION +.B ldappasswd +is a tool to modify the password of one or more LDAP entries. +Multiple entries can be specified using a search filter. +It is neither designed nor intended to be a replacement for +.BR passwd (1) +and should not be installed as such. +.LP +.B ldappasswd +works by specifying a single target dn or by using a search filter. +Matching entries will be modified with the new password. +The new password will be hashed using +.I crypt +or any other supported hashing algorithm. +For hashing algorithms other than +.I crypt +or +.IR none , +the stored password will be base64 encoded. +Salts are only generated for crypt and are based on the least +significant bits of the current time and other psuedo randomness. +.SH OPTIONS +.TP +.BI \-a \ passwdattribute +Specify the LDAP attribute to change. The default is "userPassword". +.TP +.BI \-b \ searchbase +Use \fIsearchbase\fP as the starting point for the search instead of +the default. +.TP +.B \-c \fInone\fR\||\|\fIcrypt\fR\||\|\fImd5\fR\||\|\fIsha\fR +Specify the hashing algorithm used to store the password. The default is +.IR crypt . +.TP +.BI \-D \ binddn +Use \fIbinddn\fP to bind to the X.500 directory. \fIbinddn\fP should be +a string-represented DN as defined in RFC 1779. +.TP +.BI \-d \ debuglevel +Set the LDAP debugging level to \fIdebuglevel\fP. +.B ldappasswd +must be compiled with LDAP_DEBUG defined for this option to have any effect. +.TP +.BI \-h \ ldaphost +Specify an alternate host on which the ldap server is running. +.TP +.BI \-l \ searchtime +Specify a maximum query time in seconds. +.TP +.B \-n +Make no modifications. (Can be useful when used in conjunction with +.BR \-v \ or +.BR \-d ) +.TP +.BI \-p \ ldapport +Specify an alternate port on which the ldap server is running. +.TP +.BI \-s \ base\fR\||\|\fIone\fR\||\|\fIsub\fR +Specify the scope of the search. The default is +.IR base . +.TP +.B \-t \fR[\fItargetdn\fR] +Specify the target dn to modify. If an argument is not given, the target dn will be the binddn. +.TP +.B \-v +The more v's the more verbose. +.TP +.BI \-W \ newpasswd +Specify the new password. +.TP +.BI \-w \ passwd +Use \fIpasswd\fP as the password for simple authentication. +.TP +.BI \-z \ searchsize +Specify a maximum query size. +.SH AUTHOR +David E. Storey +.SH "SEE ALSO" +.BR ldapadd (1), +.BR ldapdelete (1), +.BR ldapmodrdn (1), +.BR ldapsearch (1)