]> git.sur5r.net Git - openldap/blob - clients/tools/ldappasswd.c
d5d18e36bd675ecfec28a7acc559590a608008d1
[openldap] / clients / tools / ldappasswd.c
1 /*
2  *      Copyright 1998, David E. Storey, All rights reserved.
3  *      This software is not subject to any license of The Murphy Group, Inc.
4  *      or George Mason University.
5  *
6  *      Redistribution and use in source and binary forms are permitted only
7  *      as authorized by the OpenLDAP Public License.  A copy of this
8  *      license is available at http://www.OpenLDAP.org/license.html or
9  *      in file LICENSE in the top-level directory of the distribution.
10  *
11  *      ldappasswd.c - program to modify passwords in an LDAP tree
12  *
13  *      Author: David E. Storey <dave@tamos.net>
14  *
15  *      ToDo: option for referral handling
16  *              cracklib support?
17  *              kerberos support? (is this really necessary?)
18  *              update "shadow" fields?
19  *              create/view/change password policies?
20  *
21  *      Note: I am totally FOR comments and suggestions!
22  */
23
24 #include "portable.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/time.h>
29
30 #include <ac/string.h>
31 #include <ac/unistd.h>
32
33 #include <lber.h>
34 #include <ldap.h>
35 #include <lutil.h>
36 #include <lutil_md5.h>
37 #include <lutil_sha1.h>
38
39 #include "ldapconfig.h"
40
41 #define LDAP_PASSWD_ATTRIB "userPassword"
42
43 typedef enum {
44         HASHTYPE_NONE,
45         HASHTYPE_CRYPT,
46         HASHTYPE_MD5,
47         HASHTYPE_SHA1
48 } HashTypes;
49
50 struct hash_t {
51         char *name;
52         int namesz;
53         int (*func)(const char *, char *);
54         HashTypes type;
55 };
56
57 const char crypt64[] =
58         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./";
59 char     *base = NULL;
60 char     *binddn = NULL;
61 char     *bindpw = NULL;
62 char     *ldaphost = "localhost";
63 char     *pwattr = LDAP_PASSWD_ATTRIB;
64 char     *targetdn = NULL;
65 char     *filtpattern = NULL;
66 int       ldapport = LDAP_PORT;
67 int       noupdates = 0;
68 int       verbose = 0;
69 int       hashtype = HASHTYPE_CRYPT;
70 int       scope = LDAP_SCOPE_SUBTREE;
71
72 /*** functions ***/
73
74 /*
75  * if you'd like to write a better salt generator, please, be my guest.
76  * I just needed *something*. It's actually halfway effective for small,
77  * two character salts and it can come up with sequentially different
78  * salts.
79  */
80
81 void
82 crypt_make_salt(char *salt)
83 {
84         struct timeval tv;
85         int i;
86         char t_salt[5];
87
88         /* grab current time */
89         gettimeofday(&tv, (struct timezone *) 0);
90         i += tv.tv_usec + (int)&salt;
91         strncpy(t_salt, (char *)&i, sizeof(i));
92
93         for (i = 0; i < sizeof(i); i++)
94                 salt[i] = crypt64[t_salt[i] % (sizeof(crypt64) - 1)];
95         salt[i] = '\0';
96 }
97
98 int
99 hash_none(const char *pw_in, char *pw_out)
100 {
101         strcpy(pw_out, pw_in);
102         return(1);
103 }
104
105 int
106 hash_crypt(const char *pw_in, char *pw_out)
107 {
108         char salt[5];
109         crypt_make_salt(salt);
110         strcpy(pw_out, crypt(pw_in, salt));
111         return(1);
112 }
113
114 int
115 hash_md5(const char *pw_in, char *pw_out)
116 {
117         lutil_MD5_CTX MD5context;
118         unsigned char MD5digest[16];
119         char base64digest[25];  /* ceiling(sizeof(input)/3) * 4 + 1 */
120
121         lutil_MD5Init(&MD5context);
122         lutil_MD5Update(&MD5context, (unsigned char *)pw_in, strlen(pw_in));
123         lutil_MD5Final(MD5digest, &MD5context);
124         if (lutil_b64_ntop(MD5digest, sizeof(MD5digest), base64digest, sizeof(base64digest)) < 0)
125                 return (0);
126
127         strcpy(pw_out, base64digest);
128         return(1);
129 }
130
131 int
132 hash_sha1(const char *pw_in, char *pw_out)
133 {
134         lutil_SHA1_CTX SHA1context;
135         unsigned char SHA1digest[20];
136         char base64digest[29];  /* ceiling(sizeof(input)/3) * 4 + 1 */
137
138         lutil_SHA1Init(&SHA1context);
139         lutil_SHA1Update(&SHA1context, (unsigned char *)pw_in, strlen(pw_in));
140         lutil_SHA1Final(SHA1digest, &SHA1context);
141         if (lutil_b64_ntop(SHA1digest, sizeof(SHA1digest), base64digest, sizeof(base64digest)) < 0)
142                 return(0);
143
144         strcpy(pw_out, base64digest);
145         return(1);
146 }
147
148 static struct hash_t hashes[] = {
149         {"none",  4, hash_none,  HASHTYPE_NONE},
150         {"crypt", 5, hash_crypt, HASHTYPE_CRYPT},
151         {"md5",   3, hash_md5,   HASHTYPE_MD5},
152         {"sha",   3, hash_sha1,  HASHTYPE_SHA1},
153         {NULL,    0, NULL,       HASHTYPE_NONE}
154 };
155
156 int
157 modify_dn(LDAP *ld, char *targetdn, char *newpw)
158 {
159         int ret = 0;
160         char hashed_pw[128] = {'\0'};
161         char buf[128] = {'\0'};
162         char *strvals[2] = {buf, NULL};
163         LDAPMod mod, *mods[2] = {&mod, NULL};
164
165         if (!ld || !targetdn || !newpw)
166                 return(1);
167
168         /* hash password */
169         hashes[hashtype].func(newpw, hashed_pw);
170         if (hashtype)
171                 sprintf(buf, "{%s}%s", hashes[hashtype].name, hashed_pw);
172         else
173                 sprintf(buf, "%s", hashed_pw);
174
175         if (verbose > 0)
176         {
177                 printf("%s", targetdn);
178                 if (verbose > 1)
179                 {
180                         printf(":%s", buf);
181                         if (verbose > 2)
182                                 printf(":%s", newpw);
183                 }
184                 printf("\n");
185         }
186
187         mod.mod_vals.modv_strvals = strvals;
188         mod.mod_type = pwattr;
189         mod.mod_op = LDAP_MOD_REPLACE;
190
191         if (!noupdates && (ret = ldap_modify_s(ld, targetdn, mods)) != LDAP_SUCCESS)
192                 ldap_perror(ld, "ldap_modify_s");
193         return(ret);
194 }
195
196 void
197 usage(char *s)
198 {
199         fprintf(stderr, "usage: %s [options] [filter]\n", s);
200         fprintf(stderr, "\t-a attrib   password attribute (default: userPassword)\n");
201         fprintf(stderr, "\t-b basedn   basedn to perform searches\n");
202         fprintf(stderr, "\t-c hash     hash type: none, crypt, md5, sha (default: crypt)\n");
203         fprintf(stderr, "\t-D binddn   bind dn\n");
204         fprintf(stderr, "\t-d level    debugging level\n");
205         fprintf(stderr, "\t-h host     ldap server (default: localhost)\n");
206         fprintf(stderr, "\t-l time     time limit\n");
207         fprintf(stderr, "\t-n          make no modifications\n");
208         fprintf(stderr, "\t-p port     ldap port\n");
209         fprintf(stderr, "\t-s scope    search scope: base, one, sub (default: sub)\n");
210         fprintf(stderr, "\t-t targetdn dn to change password\n");
211         fprintf(stderr, "\t-W newpass  new password\n");
212         fprintf(stderr, "\t-w [passwd] bind password (for simple authentication)\n");
213         fprintf(stderr, "\t-v          verbose\n");
214         fprintf(stderr, "\t-z size     size limit\n");
215         exit(1);
216 }
217
218 int
219 main(int argc, char *argv[])
220 {
221         char *newpw = NULL;
222         int i, j;
223         int sizelimit = LDAP_NO_LIMIT;
224         int timelimit = LDAP_NO_LIMIT;
225         int want_bindpw = 0;
226         LDAP *ld;
227
228         while ((i = getopt(argc, argv, "D:W:a:b:c:d:h:l:np:s:t:vw::z:")) != EOF)
229         {
230                 switch(i)
231                 {
232                 case 'D':          /* bind distinguished name */
233                         binddn = strdup(optarg);
234                         break;
235
236                 case 'W':          /* new password */
237                         newpw = strdup(optarg);
238                         break;
239
240                 case 'a':          /* password attribute */
241                         pwattr = strdup(optarg);
242                         break;
243
244                 case 'b':          /* base search dn */
245                         base = strdup(optarg);
246                         break;
247
248                 case 'c':          /* hashes */
249                         for (j = 0; hashes[j].name; j++)
250                         {
251                                 if (!strncasecmp(optarg, hashes[j].name, hashes[j].namesz))
252                                 {
253                                         hashtype = hashes[j].type;
254                                         break;
255                                 }
256                         }
257
258                         if (!hashes[j].name)
259                         {
260                                 fprintf(stderr, "hash type: %s is unknown\n", optarg);
261                                 usage(argv[0]);
262                         }
263                         break;
264
265                 case 'd':          /* debugging option */
266 #ifdef LDAP_DEBUG
267                         ldap_debug = lber_debug = atoi(optarg);   /* */
268 #else
269                         fprintf(stderr, "compile with -DLDAP_DEBUG for debugging\n");
270 #endif
271                         break;
272
273                 case 'h':          /* ldap host */
274                         ldaphost = strdup(optarg);
275                         break;
276
277                 case 'l':          /* time limit */
278                         timelimit = strtol(optarg, NULL, 10);
279                         break;
280
281                 case 'n':          /* don't update entry(s) */
282                         noupdates++;
283                         break;
284
285                 case 'p':          /* ldap port */
286                         ldapport = strtol(optarg, NULL, 10);
287                         break;
288
289                 case 's':          /* scope */
290                         if (strncasecmp(optarg, "base", 4) == 0)
291                                 scope = LDAP_SCOPE_BASE;
292                         else if (strncasecmp(optarg, "one", 3) == 0)
293                                 scope = LDAP_SCOPE_ONELEVEL;
294                         else if (strncasecmp(optarg, "sub", 3) == 0)
295                                 scope = LDAP_SCOPE_SUBTREE;
296                         else {
297                                 fprintf(stderr, "scope should be base, one, or sub\n" );
298                                 usage(argv[0]);
299                         }
300                         break;
301
302                 case 't':          /* target dn */
303                         targetdn = strdup(optarg);
304                         break;
305
306                 case 'v':          /* verbose */
307                         verbose++;
308                         break;
309
310                 case 'w':          /* bind password */
311                         if (optarg)
312                                 bindpw = strdup(optarg);
313                         else
314                                 want_bindpw++;
315                     break;
316
317                 case 'z':          /* time limit */
318                         sizelimit = strtol(optarg, NULL, 10);
319                         break;
320
321                 default:
322                         usage(argv[0]);
323                 }
324         }
325
326         /* grab filter */
327         if (!(argc - optind < 1))
328                 filtpattern = strdup(argv[optind]);
329
330         /* check for target(s) */
331         if (!filtpattern && !targetdn)
332                 targetdn = binddn;
333
334         /* handle bind password */
335         if (want_bindpw)
336                 bindpw = strdup(getpass("Enter LDAP password: "));
337
338         /* handle new password */
339         if (!newpw)
340         {
341                 char *cknewpw;
342                 newpw = strdup(getpass("New password: "));
343                 cknewpw = getpass("Re-enter new password: ");
344
345                 if (strncmp(newpw, cknewpw, strlen(newpw)))
346                 {
347                         fprintf(stderr, "passwords do not match\n");
348                         return(1);
349                 }
350         }
351
352         /* connect to server */
353         if ((ld = ldap_open(ldaphost, ldapport)) == NULL)
354         {
355                 perror(ldaphost);
356                 return(1);
357         }
358
359         /* set options */
360         ldap_set_option(ld, LDAP_OPT_TIMELIMIT, (void *)&timelimit);
361         ldap_set_option(ld, LDAP_OPT_SIZELIMIT, (void *)&sizelimit);
362
363         /* authenticate to server */
364         if (ldap_bind_s(ld, binddn, bindpw, LDAP_AUTH_SIMPLE) != LDAP_SUCCESS)
365         {
366                 ldap_perror(ld, "ldap_bind");
367                 return(1);
368         }
369
370         if (filtpattern)
371         {
372                 char filter[BUFSIZ];
373                 LDAPMessage *result = NULL, *e = NULL;
374                 char *attrs[] = {"dn", NULL};
375
376                 /* search */
377                 sprintf(filter, "%s", filtpattern);
378                 i = ldap_search_s(ld, base, scope, filter, attrs, 1, &result);
379                 if (i != LDAP_SUCCESS && i != LDAP_TIMELIMIT_EXCEEDED && i != LDAP_SIZELIMIT_EXCEEDED)
380                 {
381                         ldap_perror(ld, "ldap_search_s");
382                         return(1);
383                 }
384
385                 for (e = ldap_first_entry(ld, result); e; e = ldap_next_entry(ld, e))
386                 {
387                         char *dn = ldap_get_dn(ld, e);
388                         if (dn)
389                         {
390                                 modify_dn(ld, dn, newpw);
391                                 free(dn);
392                         }
393                 }
394         }
395
396         if (targetdn)
397                 modify_dn(ld, targetdn, newpw);
398
399         /* disconnect from server */
400         ldap_unbind(ld);
401         return(0);
402 }