]> git.sur5r.net Git - openldap/blob - clients/tools/ldappasswd.c
Add OpenLDAP RCSid to *.[ch] in clients, libraries, and servers.
[openldap] / clients / tools / ldappasswd.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*
7  *      Copyright 1998, David E. Storey, All rights reserved.
8  *      This software is not subject to any license of The Murphy Group, Inc.
9  *      or George Mason University.
10  *
11  *      Redistribution and use in source and binary forms are permitted only
12  *      as authorized by the OpenLDAP Public License.  A copy of this
13  *      license is available at http://www.OpenLDAP.org/license.html or
14  *      in file LICENSE in the top-level directory of the distribution.
15  *
16  *      ldappasswd.c - program to modify passwords in an LDAP tree
17  *
18  *      Author: David E. Storey <dave@tamos.net>
19  */
20
21 #include "portable.h"
22
23 #include <stdio.h>
24
25 #include <ac/stdlib.h>
26
27 #include <ac/ctype.h>
28 #include <ac/signal.h>
29 #include <ac/socket.h>
30 #include <ac/string.h>
31 #include <ac/time.h>
32 #include <ac/unistd.h>
33
34 #include <lber.h>
35 #include <ldap.h>
36 #include <lutil.h>
37 #include <lutil_md5.h>
38 #include <lutil_sha1.h>
39
40 #include "ldap_defaults.h"
41
42 /* local macros */
43 #define CEILING(x)      ((double)(x) > (int)(x) ? (int)(x) + 1 : (int)(x))
44
45 #define LDAP_PASSWD_ATTRIB "userPassword"
46 #define LDAP_PASSWD_CONF   LDAP_SYSCONFDIR LDAP_DIRSEP "passwd.conf"
47
48 #define HS_NONE  0
49 #define HS_PLAIN 1
50 #define HS_CONV  2
51
52 typedef enum
53 {
54         HASHTYPE_NONE,
55         HASHTYPE_CRYPT,
56         HASHTYPE_MD5,
57         HASHTYPE_SMD5,
58         HASHTYPE_SHA1,
59         HASHTYPE_SSHA1
60 }
61 HashTypes;
62
63 typedef struct salt_t
64 {
65         unsigned char  *salt;
66         unsigned int    len;
67 }
68 Salt;
69
70 typedef struct hash_t
71 {
72         const char     *name;
73         unsigned int    namesz;
74         char           *(*func) (const char *, Salt *);
75         unsigned char   takes_salt;
76         HashTypes       type;
77         HashTypes       type_salted;
78         unsigned int    default_salt_len;
79 }
80 Hash;
81
82 static int      noupdates = 0;
83 static int      verbose = 0;
84 static int      want_entryhash = 0;
85 static int      auto_gen_pw = 0;
86
87 /*** functions ***/
88
89 /*
90  * pw_encode() essentially base64 encodes a password and its salt
91  */
92
93 static char *
94 pw_encode (unsigned char *passwd, Salt * salt, unsigned int len)
95 {
96         int             salted = salt && salt->salt && salt->len;
97         int             b64_len = 0;
98         char           *base64digest = NULL;
99         unsigned char  *npasswd = passwd;
100
101         if (salted)
102         {
103                 npasswd = (unsigned char *)malloc (len + salt->len);
104                 memcpy (npasswd, passwd, len);
105                 memcpy (&npasswd[len], salt->salt, salt->len);
106                 len += salt->len;
107         }
108
109         b64_len = CEILING (len / 3) * 4 + 1;
110         base64digest = (char *)malloc (b64_len);
111         if (lutil_b64_ntop (npasswd, len, base64digest, b64_len) < 0)
112         {
113                 free (base64digest);
114                 base64digest = NULL;
115         }
116
117         if (salted)
118                 free (npasswd);
119
120         return (base64digest);
121 }
122
123 /*
124  * if you'd like to write a better salt generator, please, be my guest.
125  */
126
127 static void
128 make_salt (Salt * salt, unsigned int len)
129 {
130
131         if (!salt)
132                 return;
133
134         salt->len = len;
135         salt->salt = (unsigned char *)malloc (len);
136
137         for (len = 0; len < salt->len; len++)
138                 salt->salt[len] = rand () & 0xff;
139 }
140
141 /*
142  * password generator
143  */
144
145 static char *
146 gen_pass (unsigned int len)
147 {
148         static const unsigned char autogen[] =
149                 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890.,";
150         unsigned int i;
151         Salt            salt;
152
153         salt.salt = NULL;
154         salt.len = 0;
155
156         make_salt (&salt, len);
157         for (i = 0; i < len; i++)
158                 salt.salt[i] = autogen[salt.salt[i] % (sizeof (autogen) - 1)];
159
160         return ((char *)salt.salt);
161 }
162
163 #ifdef SLAPD_CLEARTEXT
164 static char *
165 hash_none (const char *pw_in, Salt * salt)
166 {
167         return (strdup (pw_in));
168 }
169 #endif
170
171 #ifdef SLAPD_CRYPT
172 static char *
173 hash_crypt (const char *pw_in, Salt * salt)
174 {
175         static const unsigned char crypt64[] =
176                 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890./";
177         char   *crypted_pw = NULL;
178         Salt    lsalt;
179
180         if (salt && salt->salt && strlen ((char *)salt->salt) >= 2)
181         {
182                 /* sanity check */
183                 if (!(isalnum(salt->salt[0]) || salt->salt[0] == '.' || salt->salt[0] == '/'))
184                         salt->salt[0] = crypt64[salt->salt[0] % (sizeof (crypt64) - 1)];
185                 if (!(isalnum(salt->salt[1]) || salt->salt[1] == '.' || salt->salt[1] == '/'))
186                         salt->salt[1] = crypt64[salt->salt[1] % (sizeof (crypt64) - 1)];
187
188                 crypted_pw = crypt (pw_in, (char *)salt->salt);
189         }
190         else
191         {
192                 make_salt (&lsalt, 2);
193                 lsalt.salt[0] = crypt64[lsalt.salt[0] % (sizeof (crypt64) - 1)];
194                 lsalt.salt[1] = crypt64[lsalt.salt[1] % (sizeof (crypt64) - 1)];
195                 crypted_pw = crypt (pw_in, (char *)lsalt.salt);
196                 free (lsalt.salt);
197         }
198         return (strdup (crypted_pw));
199 }
200 #endif
201
202 static char *
203 hash_md5 (const char *pw_in, Salt * salt)
204 {
205         lutil_MD5_CTX   MD5context;
206         unsigned char   MD5digest[16];
207
208         lutil_MD5Init (&MD5context);
209         lutil_MD5Update (&MD5context,
210                          (const unsigned char *)pw_in, strlen(pw_in));
211         if (salt && salt->salt && salt->len)
212                 lutil_MD5Update (&MD5context, salt->salt, salt->len);
213         lutil_MD5Final (MD5digest, &MD5context);
214
215         return (pw_encode (MD5digest, salt, sizeof (MD5digest)));
216 }
217
218 static char *
219 hash_sha1 (const char *pw_in, Salt * salt)
220 {
221         lutil_SHA1_CTX  SHA1context;
222         unsigned char   SHA1digest[20];
223
224         lutil_SHA1Init (&SHA1context);
225         lutil_SHA1Update (&SHA1context,
226                           (const unsigned char *)pw_in, strlen(pw_in));
227         if (salt && salt->salt && salt->len)
228                 lutil_SHA1Update (&SHA1context, salt->salt, salt->len);
229         lutil_SHA1Final (SHA1digest, &SHA1context);
230
231         return (pw_encode (SHA1digest, salt, sizeof (SHA1digest)));
232 }
233
234 static const Hash hashes[] =
235 {
236 #ifdef SLAPD_CLEARTEXT
237         {"none",  4, hash_none,  0, HASHTYPE_NONE,  HASHTYPE_NONE,  0},
238 #endif
239 #ifdef SLAPD_CRYPT
240         {"crypt", 5, hash_crypt, 1, HASHTYPE_CRYPT, HASHTYPE_CRYPT, 2},
241 #endif
242         {"md5",   3, hash_md5,   0, HASHTYPE_MD5,   HASHTYPE_SMD5,  0},
243         {"smd5",  4, hash_md5,   1, HASHTYPE_SMD5,  HASHTYPE_SMD5,  4},
244         {"sha",   3, hash_sha1,  0, HASHTYPE_SHA1,  HASHTYPE_SSHA1, 0},
245         {"ssha",  4, hash_sha1,  1, HASHTYPE_SSHA1, HASHTYPE_SSHA1, 4},
246         {NULL,    0, NULL,       0, HASHTYPE_NONE,  HASHTYPE_NONE,  0}
247 };
248
249 static int
250 modify_dn (LDAP * ld, char *targetdn, char *pwattr, char *oldpw,
251            char *newpw, HashTypes htype, Salt * salt)
252 {
253         int             ret = 0;
254         int             salted = salt->salt ? 1 : 0;
255         int             want_salt = salt->len && !salted;
256         char           *buf = NULL;
257         char           *hashed_pw = NULL;
258         char           *strvals[2];
259         LDAPMod         mod, *mods[2];
260
261         if (!ld || !targetdn || !newpw)
262                 return (1);
263
264         /* auto-generate password */
265         if (auto_gen_pw)
266                 newpw = gen_pass (auto_gen_pw);
267
268         /* handle salt */
269         if (want_salt)
270         {
271                 make_salt (salt, salt->len);
272                 htype = hashes[htype].type_salted;
273         }
274         else if (hashes[htype].default_salt_len)
275         {
276                 /* user chose a salted hash and needs a salt */
277                 if (!salted)
278                 {
279                         want_salt++;
280                         salt->len = hashes[htype].default_salt_len;
281                         make_salt (salt, salt->len);
282                 }
283         }
284
285         /* hash password */
286         hashed_pw = hashes[htype].func (newpw, salt->len ? salt : NULL);
287
288         /* return salt back to its original state */
289         if (want_salt)
290         {
291                 free (salt->salt);
292                 salt->salt = NULL;
293         }
294
295         buf = (char *)malloc (hashes[htype].namesz + 3 + strlen (hashed_pw));
296         if (htype)
297                 sprintf (buf, "{%s}%s", hashes[htype].name, hashed_pw);
298         else
299                 sprintf (buf, "%s", hashed_pw);
300
301         if (verbose > 0)
302         {
303                 printf ("%s", targetdn);
304                 if (verbose > 1)
305                 {
306                         printf (":%s", buf);
307                         if (verbose > 2)
308                                 printf (":%s", newpw);
309                 }
310                 printf ("\n");
311         }
312
313         strvals[0] = buf;
314         strvals[1] = NULL;
315         mod.mod_values = strvals;
316         mod.mod_type = pwattr;
317         mod.mod_op = LDAP_MOD_REPLACE;
318         mods[0] = &mod;
319         mods[1] =NULL;
320
321         if (!noupdates && (ret = ldap_modify_s (ld, targetdn, mods)) != LDAP_SUCCESS)
322                 ldap_perror (ld, "ldap_modify");
323
324         free (hashed_pw);
325         free (buf);
326         return (ret);
327 }
328
329 static void
330 usage(const char *s)
331 {
332         fprintf (stderr, "Usage: %s [options] [filter]\n", s);
333         fprintf (stderr, "  -a attrib\tpassword attribute (default: " LDAP_PASSWD_ATTRIB ")\n");
334         fprintf (stderr, "  -b basedn\tbasedn to perform searches\n");
335 /*      fprintf (stderr, "  -C\t\tuse entry's current hash mechanism\n"); */
336         fprintf (stderr, "  -D binddn\tbind dn\n");
337         fprintf (stderr, "  -d level\tdebugging level\n");
338         fprintf (stderr, "  -E\t\tprompt for new password\n");
339         fprintf (stderr, "  -e passwd\tnew password\n");
340         fprintf (stderr, "  -g passlen\tauto-generate passwords with length pwlen\n");
341         fprintf (stderr, "  -H hash\thash type (default: crypt)\n");
342         fprintf (stderr, "  -h host\tldap server (default: localhost)\n");
343 #ifdef HAVE_KERBEROS
344         fprintf (stderr, "  -K\t\tuse Kerberos step 1\n");
345         fprintf (stderr, "  -k\t\tuse Kerberos\n");
346 #endif
347         fprintf (stderr, "  -l time\ttime limit\n");
348         fprintf (stderr, "  -n\t\tmake no modifications\n");
349         fprintf (stderr, "  -P version\tprotocol version (2 or 3)\n");
350         fprintf (stderr, "  -p port\tldap port\n");
351         fprintf (stderr, "  -s scope\tsearch scope: base, one, sub (default: sub)\n");
352         fprintf (stderr, "  -t targetdn\tdn to change password\n");
353         fprintf (stderr, "  -v\t\tverbose (more v's, more verbose)\n");
354         fprintf (stderr, "  -W\t\tprompt for bind password\n");
355         fprintf (stderr, "  -w passwd\tbind password (for simple authentication)\n");
356         fprintf (stderr, "  -Y saltlen\tsalt length to use\n");
357 /*      fprintf (stderr, "  -y salt\tsalt to use\n"); */
358         fprintf (stderr, "  -z size\tsize limit\n");
359         exit( EXIT_FAILURE );
360 }
361
362 int
363 main (int argc, char *argv[])
364 {
365         char           *base = NULL;
366         char           *binddn = NULL;
367         char           *bindpw = NULL;
368         char           *filtpattern = NULL;
369         char           *ldaphost = NULL;
370         char           *targetdn = NULL;
371         char           *pwattr = LDAP_PASSWD_ATTRIB;
372         char           *newpw = NULL;
373         int             authmethod = LDAP_AUTH_SIMPLE;
374         int             hashtype = HASHTYPE_CRYPT;
375         int             i, j;
376         int             ldapport = 0;
377         int             debug = 0;
378         int             scope = LDAP_SCOPE_SUBTREE;
379         int             sizelimit = -1;
380         int             timelimit = -1;
381         int             version = -1;
382         int             want_bindpw = 0;
383         int             want_newpw = 0;
384         LDAP           *ld;
385         Salt            salt;
386
387         salt.salt = NULL;
388         salt.len = 0;
389
390         if (argc == 1)
391                 usage (argv[0]);
392
393         while ((i = getopt (argc, argv, "a:b:C:D:d:Ee:g:H:h:Kkl:nP:p:s:t:vWw:Y:y:z:")) != EOF)
394         {
395                 switch (i)
396                 {
397                 case 'a':       /* password attribute */
398                         pwattr = strdup (optarg);
399                         break;
400
401                 case 'b':       /* base search dn */
402                         base = strdup (optarg);
403                         break;
404
405                 case 'C':
406                         want_entryhash++;
407                         break;
408
409                 case 'D':       /* bind distinguished name */
410                         binddn = strdup (optarg);
411                         break;
412
413                 case 'd':       /* debugging option */
414                         debug |= atoi (optarg);
415                         break;
416
417                 case 'E':       /* prompt for new password */
418                         want_newpw++;
419                         break;
420
421                 case 'e':       /* new password */
422                         newpw = strdup (optarg);
423                         break;
424
425                 case 'g':
426                         auto_gen_pw = strtol (optarg, NULL, 10);
427                         break;
428
429                 case 'H':       /* hashes */
430                         for (j = 0; hashes[j].name; j++)
431                         {
432                                 if (!strncasecmp (optarg, hashes[j].name, hashes[j].namesz))
433                                 {
434                                         hashtype = hashes[j].type;
435                                         break;
436                                 }
437                         }
438
439                         if (!hashes[j].name)
440                         {
441                                 fprintf (stderr, "hash type: %s is unknown\n", optarg);
442                                 usage (argv[0]);
443                         }
444                         break;
445
446                 case 'h':       /* ldap host */
447                         ldaphost = strdup (optarg);
448                         break;
449
450                 case 'K':       /* use kerberos bind, 1st part only */
451 #ifdef HAVE_KERBEROS
452                         authmethod = LDAP_AUTH_KRBV41;
453 #else
454                         fprintf (stderr, "%s was not compiled with Kerberos support\n", argv[0]);
455                         usage (argv[0]);
456 #endif
457                         break;
458
459                 case 'k':       /* use kerberos bind */
460 #ifdef HAVE_KERBEROS
461                         authmethod = LDAP_AUTH_KRBV4;
462 #else
463                         fprintf (stderr, "%s was not compiled with Kerberos support\n", argv[0]);
464                         usage (argv[0]);
465 #endif
466                         break;
467
468                 case 'l':       /* time limit */
469                         timelimit = strtol (optarg, NULL, 10);
470                         break;
471
472                 case 'n':       /* don't update entry(s) */
473                         noupdates++;
474                         break;
475
476                 case 'P':
477                         switch( atoi( optarg ) ) {
478                         case 2:
479                                 version = LDAP_VERSION2;
480                                 break;
481                         case 3:
482                                 version = LDAP_VERSION3;
483                                 break;
484                         default:
485                                 fprintf( stderr, "protocol version should be 2 or 3\n" );
486                                 usage( argv[0] );
487                         }
488                         break;
489
490                 case 'p':       /* ldap port */
491                         ldapport = strtol (optarg, NULL, 10);
492                         break;
493
494                 case 's':       /* scope */
495                         if (strcasecmp (optarg, "base") == 0)
496                                 scope = LDAP_SCOPE_BASE;
497                         else if (strcasecmp (optarg, "one") == 0)
498                                 scope = LDAP_SCOPE_ONELEVEL;
499                         else if (strcasecmp (optarg, "sub") == 0)
500                                 scope = LDAP_SCOPE_SUBTREE;
501                         else
502                         {
503                                 fprintf (stderr, "scope should be base, one, or sub\n");
504                                 usage (argv[0]);
505                         }
506                         break;
507
508                 case 't':       /* target dn */
509                         targetdn = strdup (optarg);
510                         break;
511
512                 case 'v':       /* verbose */
513                         verbose++;
514                         break;
515
516                 case 'W':       /* promt for bind password */
517                         want_bindpw++;
518                         break;
519
520                 case 'w':       /* bind password */
521                         bindpw = strdup (optarg);
522                         {
523                                 char* p;
524
525                                 for( p = optarg; *p == '\0'; p++ ) {
526                                         *p = '*';
527                                 }
528                         }
529                         break;
530
531                 case 'Y':       /* salt length */
532                         salt.len = strtol (optarg, NULL, 10);
533                         break;
534
535                 case 'y':       /* user specified salt */
536                         salt.len = strlen (optarg);
537                         salt.salt = (unsigned char *)strdup (optarg);
538                         break;
539
540                 case 'z':       /* time limit */
541                         sizelimit = strtol (optarg, NULL, 10);
542                         break;
543
544                 default:
545                         usage (argv[0]);
546                 }
547         }
548
549         /* grab filter */
550         if (!(argc - optind < 1))
551                 filtpattern = strdup (argv[optind]);
552
553         /* check for target(s) */
554         if (!filtpattern && !targetdn)
555                 targetdn = binddn;
556
557         /* handle bind password */
558         if (want_bindpw)
559                 bindpw = strdup (getpass ("Enter LDAP password: "));
560
561         /* handle new password */
562         if (!newpw)
563         {
564                 char *cknewpw;
565                 newpw = strdup (getpass ("New password: "));
566                 cknewpw = getpass ("Re-enter new password: ");
567
568                 if (strncmp (newpw, cknewpw, strlen (newpw)))
569                 {
570                         fprintf (stderr, "passwords do not match\n");
571                         return ( EXIT_FAILURE );
572                 }
573         }
574
575         if ( debug ) {
576                 if( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug ) != LBER_OPT_SUCCESS ) {
577                         fprintf( stderr, "Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug );
578                 }
579                 if( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug ) != LDAP_OPT_SUCCESS ) {
580                         fprintf( stderr, "Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug );
581                 }
582         }
583
584 #ifdef SIGPIPE
585         (void) SIGNAL( SIGPIPE, SIG_IGN );
586 #endif
587         /* seed random number generator */
588
589 #ifdef HAVE_GETTIMEOFDAY
590         /* this is of questionable value
591          * gettimeofday may not provide much usec
592          */
593         {
594                 struct timeval tv;
595                 gettimeofday (&tv, NULL);
596                 srand(tv.tv_sec * (tv.tv_usec + 1));
597         }
598 #else
599         /* The traditional seed */
600         srand((unsigned)time( NULL ));
601 #endif
602
603         /* connect to server */
604         if ((ld = ldap_init (ldaphost, ldapport)) == NULL)
605         {
606                 perror ("ldap_init");
607                 return ( EXIT_FAILURE );
608         }
609
610         /* set options */
611         if (timelimit != -1 &&
612                 ldap_set_option( ld, LDAP_OPT_TIMELIMIT, (void *) &timelimit ) != LDAP_OPT_SUCCESS )
613         {
614                 fprintf( stderr, "Could not set LDAP_OPT_TIMELIMIT %d\n", timelimit );
615         }
616         if (sizelimit != -1 &&
617                 ldap_set_option( ld, LDAP_OPT_SIZELIMIT, (void *) &sizelimit ) != LDAP_OPT_SUCCESS )
618         {
619                 fprintf( stderr, "Could not set LDAP_OPT_SIZELIMIT %d\n", sizelimit );
620         }
621
622         /* this seems prudent */
623         {
624                 int deref = LDAP_DEREF_NEVER;
625                 ldap_set_option( ld, LDAP_OPT_DEREF, &deref);
626         }
627         /* don't chase referrals */
628         ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF );
629
630         if (version != -1 &&
631                 ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ) != LDAP_OPT_SUCCESS )
632         {
633                 fprintf( stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n", version );
634         }
635
636         /* authenticate to server */
637         if (ldap_bind_s (ld, binddn, bindpw, authmethod) != LDAP_SUCCESS)
638         {
639                 ldap_perror (ld, "ldap_bind");
640                 return ( EXIT_FAILURE );
641         }
642
643         if (targetdn)
644         {
645                 if (want_entryhash)
646                 {
647                         /* insert code here =) */
648                 }
649                 else
650                         modify_dn (ld, targetdn, pwattr, NULL, newpw, hashtype, &salt);
651         }
652
653         if (filtpattern)
654         {
655                 char            filter[BUFSIZ];
656                 LDAPMessage     *result = NULL, *e;
657                 char            *attrs[2];
658                 attrs[0] = pwattr;
659                 attrs[1] = NULL;
660
661                 /* search */
662                 sprintf (filter, "%s", filtpattern);
663                 i = ldap_search_s (ld, base, scope, filter, attrs, 0, &result);
664                 if (i != LDAP_SUCCESS &&
665                     i != LDAP_TIMELIMIT_EXCEEDED &&
666                     i != LDAP_SIZELIMIT_EXCEEDED)
667                 {
668                         ldap_perror (ld, "ldap_search");
669                         return ( EXIT_FAILURE );
670                 }
671
672                 for (e = ldap_first_entry (ld, result); e; e = ldap_next_entry (ld, e))
673                 {
674                         char *dn = ldap_get_dn (ld, e);
675                         if (dn)
676                         {
677                                 struct berval **pw_vals = ldap_get_values_len (ld, e, pwattr);
678                                 modify_dn (ld, dn, pwattr, pw_vals ? pw_vals[0]->bv_val : NULL, newpw, hashtype, &salt);
679                                 if (pw_vals)
680                                         ldap_value_free_len (pw_vals);
681                                 free (dn);
682                         }
683                 }
684         }
685
686         /* disconnect from server */
687         ldap_unbind (ld);
688
689         return ( EXIT_SUCCESS );
690 }