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