Imported Upstream version 1.1.6
[platform/upstream/pam.git] / modules / pam_cracklib / pam_cracklib.c
1 /*
2  * pam_cracklib module
3  */
4
5 /*
6  * 0.9. switch to using a distance algorithm in similar()
7  * 0.86.  added support for setting minimum numbers of digits, uppers,
8  *        lowers, and others
9  * 0.85.  added six new options to use this with long passwords.
10  * 0.8. tidied output and improved D(()) usage for debugging.
11  * 0.7. added support for more obscure checks for new passwd.
12  * 0.6. root can reset user passwd to any values (it's only warned)
13  * 0.5. supports retries - 'retry=N' argument
14  * 0.4. added argument 'type=XXX' for 'New XXX password' prompt
15  * 0.3. Added argument 'debug'
16  * 0.2. new password is feeded to cracklib for verify after typed once
17  * 0.1. First release
18  */
19
20 /*
21  * Written by Cristian Gafton <gafton@redhat.com> 1996/09/10
22  * Long password support by Philip W. Dalrymple <pwd@mdtsoft.com> 1997/07/18
23  * See the end of the file for Copyright Information
24  *
25  * Modification for long password systems (>8 chars).  The original
26  * module had problems when used in a md5 password system in that it
27  * allowed too short passwords but required that at least half of the
28  * bytes in the new password did not appear in the old one.  this
29  * action is still the default and the changes should not break any
30  * current user. This modification adds 6 new options, one to set the
31  * number of bytes in the new password that are not in the old one,
32  * the other five to control the length checking, these are all
33  * documented (or will be before anyone else sees this code) in the PAM
34  * S.A.G. in the section on the cracklib module.
35  */
36
37 #include "config.h"
38
39 #include <stdio.h>
40 #ifdef HAVE_LIBXCRYPT
41 # include <xcrypt.h>
42 #elif defined(HAVE_CRYPT_H)
43 # include <crypt.h>
44 #endif
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <syslog.h>
49 #include <stdarg.h>
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <ctype.h>
53 #include <limits.h>
54 #include <pwd.h>
55 #include <security/pam_modutil.h>
56
57 #ifdef HAVE_CRACK_H
58 #include <crack.h>
59 #else
60 extern char *FascistCheck(char *pw, const char *dictpath);
61 #endif
62
63 #ifndef CRACKLIB_DICTS
64 #define CRACKLIB_DICTS NULL
65 #endif
66
67 /* For Translators: "%s%s" could be replaced with "<service> " or "". */
68 #define PROMPT1 _("New %s%spassword: ")
69 /* For Translators: "%s%s" could be replaced with "<service> " or "". */
70 #define PROMPT2 _("Retype new %s%spassword: ")
71 #define MISTYPED_PASS _("Sorry, passwords do not match.")
72
73 #ifdef MIN
74 #undef MIN
75 #endif
76 #define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b))
77
78 /*
79  * here, we make a definition for the externally accessible function
80  * in this file (this definition is required for static a module
81  * but strongly encouraged generally) it is used to instruct the
82  * modules include file to define the function prototypes.
83  */
84
85 #define PAM_SM_PASSWORD
86
87 #include <security/pam_modules.h>
88 #include <security/_pam_macros.h>
89 #include <security/pam_ext.h>
90
91 /* argument parsing */
92 #define PAM_DEBUG_ARG       0x0001
93
94 struct cracklib_options {
95         int retry_times;
96         int diff_ok;
97         int min_length;
98         int dig_credit;
99         int up_credit;
100         int low_credit;
101         int oth_credit;
102         int min_class;
103         int max_repeat;
104         int max_sequence;
105         int max_class_repeat;
106         int reject_user;
107         int gecos_check;
108         int enforce_for_root;
109         const char *cracklib_dictpath;
110 };
111
112 #define CO_RETRY_TIMES  1
113 #define CO_DIFF_OK      5
114 #define CO_MIN_LENGTH   9
115 # define CO_MIN_LENGTH_BASE 5
116 #define CO_DIG_CREDIT   1
117 #define CO_UP_CREDIT    1
118 #define CO_LOW_CREDIT   1
119 #define CO_OTH_CREDIT   1
120 #define CO_MIN_WORD_LENGTH 4
121
122 static int
123 _pam_parse (pam_handle_t *pamh, struct cracklib_options *opt,
124             int argc, const char **argv)
125 {
126      int ctrl=0;
127
128      /* step through arguments */
129      for (ctrl=0; argc-- > 0; ++argv) {
130          char *ep = NULL;
131
132          /* generic options */
133
134          if (!strcmp(*argv,"debug"))
135              ctrl |= PAM_DEBUG_ARG;
136          else if (!strncmp(*argv,"type=",5))
137              pam_set_item (pamh, PAM_AUTHTOK_TYPE, *argv+5);
138          else if (!strncmp(*argv,"retry=",6)) {
139              opt->retry_times = strtol(*argv+6,&ep,10);
140              if (!ep || (opt->retry_times < 1))
141                  opt->retry_times = CO_RETRY_TIMES;
142          } else if (!strncmp(*argv,"difok=",6)) {
143              opt->diff_ok = strtol(*argv+6,&ep,10);
144              if (!ep || (opt->diff_ok < 0))
145                  opt->diff_ok = CO_DIFF_OK;
146          } else if (!strncmp(*argv,"difignore=",10)) {
147                 /* just ignore */
148          } else if (!strncmp(*argv,"minlen=",7)) {
149              opt->min_length = strtol(*argv+7,&ep,10);
150              if (!ep || (opt->min_length < CO_MIN_LENGTH_BASE))
151                  opt->min_length = CO_MIN_LENGTH_BASE;
152          } else if (!strncmp(*argv,"dcredit=",8)) {
153              opt->dig_credit = strtol(*argv+8,&ep,10);
154              if (!ep)
155                  opt->dig_credit = 0;
156          } else if (!strncmp(*argv,"ucredit=",8)) {
157              opt->up_credit = strtol(*argv+8,&ep,10);
158              if (!ep)
159                  opt->up_credit = 0;
160          } else if (!strncmp(*argv,"lcredit=",8)) {
161              opt->low_credit = strtol(*argv+8,&ep,10);
162              if (!ep)
163                  opt->low_credit = 0;
164          } else if (!strncmp(*argv,"ocredit=",8)) {
165              opt->oth_credit = strtol(*argv+8,&ep,10);
166              if (!ep)
167                  opt->oth_credit = 0;
168          } else if (!strncmp(*argv,"minclass=",9)) {
169              opt->min_class = strtol(*argv+9,&ep,10);
170              if (!ep)
171                  opt->min_class = 0;
172              if (opt->min_class > 4)
173                  opt->min_class = 4;
174          } else if (!strncmp(*argv,"maxrepeat=",10)) {
175              opt->max_repeat = strtol(*argv+10,&ep,10);
176              if (!ep)
177                  opt->max_repeat = 0;
178          } else if (!strncmp(*argv,"maxsequence=",12)) {
179              opt->max_sequence = strtol(*argv+12,&ep,10);
180              if (!ep)
181                  opt->max_sequence = 0;
182          } else if (!strncmp(*argv,"maxclassrepeat=",15)) {
183              opt->max_class_repeat = strtol(*argv+15,&ep,10);
184              if (!ep)
185                  opt->max_class_repeat = 0;
186          } else if (!strncmp(*argv,"reject_username",15)) {
187                  opt->reject_user = 1;
188          } else if (!strncmp(*argv,"gecoscheck",10)) {
189                  opt->gecos_check = 1;
190          } else if (!strncmp(*argv,"enforce_for_root",16)) {
191                   opt->enforce_for_root = 1;
192          } else if (!strncmp(*argv,"authtok_type",12)) {
193            /* for pam_get_authtok, ignore */;
194          } else if (!strncmp(*argv,"use_authtok",11)) {
195            /* for pam_get_authtok, ignore */;
196          } else if (!strncmp(*argv,"use_first_pass",14)) {
197            /* for pam_get_authtok, ignore */;
198          } else if (!strncmp(*argv,"try_first_pass",14)) {
199            /* for pam_get_authtok, ignore */;
200          } else if (!strncmp(*argv,"dictpath=",9)) {
201              opt->cracklib_dictpath = *argv+9;
202              if (!*(opt->cracklib_dictpath)) {
203                  opt->cracklib_dictpath = CRACKLIB_DICTS;
204              }
205          } else {
206              pam_syslog(pamh,LOG_ERR,"pam_parse: unknown option; %s",*argv);
207          }
208      }
209
210      return ctrl;
211 }
212
213 /* Helper functions */
214
215 /*
216  * can't be a palindrome - like `R A D A R' or `M A D A M'
217  */
218 static int palindrome(const char *new)
219 {
220     int i, j;
221
222         i = strlen (new);
223
224         for (j = 0;j < i;j++)
225                 if (new[i - j - 1] != new[j])
226                         return 0;
227
228         return 1;
229 }
230
231 /*
232  * Calculate how different two strings are in terms of the number of
233  * character removals, additions, and changes needed to go from one to
234  * the other
235  */
236
237 static int distdifferent(const char *old, const char *new,
238                          size_t i, size_t j)
239 {
240     char c, d;
241
242     if ((i == 0) || (strlen(old) < i)) {
243         c = 0;
244     } else {
245         c = old[i - 1];
246     }
247     if ((j == 0) || (strlen(new) < j)) {
248         d = 0;
249     } else {
250         d = new[j - 1];
251     }
252     return (c != d);
253 }
254
255 static int distcalculate(int **distances, const char *old, const char *new,
256                          size_t i, size_t j)
257 {
258     int tmp = 0;
259
260     if (distances[i][j] != -1) {
261         return distances[i][j];
262     }
263
264     tmp =          distcalculate(distances, old, new, i - 1, j - 1);
265     tmp = MIN(tmp, distcalculate(distances, old, new,     i, j - 1));
266     tmp = MIN(tmp, distcalculate(distances, old, new, i - 1,     j));
267     tmp += distdifferent(old, new, i, j);
268
269     distances[i][j] = tmp;
270
271     return tmp;
272 }
273
274 static int distance(const char *old, const char *new)
275 {
276     int **distances = NULL;
277     size_t m, n, i, j, r;
278
279     m = strlen(old);
280     n = strlen(new);
281     distances = malloc(sizeof(int*) * (m + 1));
282
283     for (i = 0; i <= m; i++) {
284         distances[i] = malloc(sizeof(int) * (n + 1));
285         for(j = 0; j <= n; j++) {
286             distances[i][j] = -1;
287         }
288     }
289     for (i = 0; i <= m; i++) {
290         distances[i][0] = i;
291     }
292     for (j = 0; j <= n; j++) {
293         distances[0][j] = j;
294     }
295     distances[0][0] = 0;
296
297     r = distcalculate(distances, old, new, m, n);
298
299     for (i = 0; i <= m; i++) {
300         memset(distances[i], 0, sizeof(int) * (n + 1));
301         free(distances[i]);
302     }
303     free(distances);
304
305     return r;
306 }
307
308 static int similar(struct cracklib_options *opt,
309                    const char *old, const char *new)
310 {
311     if (distance(old, new) >= opt->diff_ok) {
312         return 0;
313     }
314
315     if (strlen(new) >= (strlen(old) * 2)) {
316         return 0;
317     }
318
319     /* passwords are too similar */
320     return 1;
321 }
322
323 /*
324  * enough classes of charecters
325  */
326
327 static int minclass (struct cracklib_options *opt,
328                      const char *new)
329 {
330     int digits = 0;
331     int uppers = 0;
332     int lowers = 0;
333     int others = 0;
334     int total_class;
335     int i;
336     int retval;
337
338     D(( "called" ));
339     for (i = 0; new[i]; i++)
340        {
341          if (isdigit (new[i]))
342              digits = 1;
343          else if (isupper (new[i]))
344              uppers = 1;
345          else if (islower (new[i]))
346              lowers = 1;
347          else
348              others = 1;
349        }
350
351     total_class = digits + uppers + lowers + others;
352
353     D (("total class: %d\tmin_class: %d", total_class, opt->min_class));
354
355     if (total_class >= opt->min_class)
356         retval = 0;
357     else
358       retval = 1;
359
360     return retval;
361 }
362
363
364 /*
365  * a nice mix of characters.
366  */
367 static int simple(struct cracklib_options *opt, const char *new)
368 {
369     int digits = 0;
370     int uppers = 0;
371     int lowers = 0;
372     int others = 0;
373     int size;
374     int i;
375     enum { NONE, DIGIT, UCASE, LCASE, OTHER } prevclass = NONE;
376     int sameclass = 0;
377
378     for (i = 0;new[i];i++) {
379         if (isdigit (new[i])) {
380             digits++;
381             if (prevclass != DIGIT) {
382                 prevclass = DIGIT;
383                 sameclass = 1;
384             } else
385                 sameclass++;
386         }
387         else if (isupper (new[i])) {
388             uppers++;
389             if (prevclass != UCASE) {
390                 prevclass = UCASE;
391                 sameclass = 1;
392             } else
393                 sameclass++;
394         }
395         else if (islower (new[i])) {
396             lowers++;
397             if (prevclass != LCASE) {
398                 prevclass = LCASE;
399                 sameclass = 1;
400             } else
401                 sameclass++;
402         }
403         else {
404             others++;
405             if (prevclass != OTHER) {
406                 prevclass = OTHER;
407                 sameclass = 1;
408             } else
409                 sameclass++;
410         }
411         if (opt->max_class_repeat > 1 && sameclass > opt->max_class_repeat) {
412                 return 1;
413         }
414     }
415
416     /*
417      * The scam was this - a password of only one character type
418      * must be 8 letters long.  Two types, 7, and so on.
419      * This is now changed, the base size and the credits or defaults
420      * see the docs on the module for info on these parameters, the
421      * defaults cause the effect to be the same as before the change
422      */
423
424     if ((opt->dig_credit >= 0) && (digits > opt->dig_credit))
425         digits = opt->dig_credit;
426
427     if ((opt->up_credit >= 0) && (uppers > opt->up_credit))
428         uppers = opt->up_credit;
429
430     if ((opt->low_credit >= 0) && (lowers > opt->low_credit))
431         lowers = opt->low_credit;
432
433     if ((opt->oth_credit >= 0) && (others > opt->oth_credit))
434         others = opt->oth_credit;
435
436     size = opt->min_length;
437
438     if (opt->dig_credit >= 0)
439         size -= digits;
440     else if (digits < opt->dig_credit * -1)
441         return 1;
442
443     if (opt->up_credit >= 0)
444         size -= uppers;
445     else if (uppers < opt->up_credit * -1)
446         return 1;
447
448     if (opt->low_credit >= 0)
449         size -= lowers;
450     else if (lowers < opt->low_credit * -1)
451         return 1;
452
453     if (opt->oth_credit >= 0)
454         size -= others;
455     else if (others < opt->oth_credit * -1)
456         return 1;
457
458     if (size <= i)
459         return 0;
460
461     return 1;
462 }
463
464 static int consecutive(struct cracklib_options *opt, const char *new)
465 {
466     char c;
467     int i;
468     int same;
469
470     if (opt->max_repeat == 0)
471         return 0;
472
473     for (i = 0; new[i]; i++) {
474         if (i > 0 && new[i] == c) {
475             ++same;
476             if (same > opt->max_repeat)
477                 return 1;
478         } else {
479             c = new[i];
480             same = 1;
481         }
482     }
483     return 0;
484 }
485
486 static int sequence(struct cracklib_options *opt, const char *new)
487 {
488     char c;
489     int i;
490     int sequp = 1;
491     int seqdown = 1;
492
493     if (opt->max_sequence == 0)
494         return 0;
495
496     if (new[0] == '\0')
497         return 0;
498
499     for (i = 1; new[i]; i++) {
500         c = new[i-1];
501         if (new[i] == c+1) {
502             ++sequp;
503             if (sequp > opt->max_sequence)
504                 return 1;
505             seqdown = 1;
506         } else if (new[i] == c-1) {
507             ++seqdown;
508             if (seqdown > opt->max_sequence)
509                 return 1;
510             sequp = 1;
511         } else {
512             sequp = 1;
513             seqdown = 1;
514         }
515     }
516     return 0;
517 }
518
519 static int wordcheck(const char *new, char *word)
520 {
521     char *f, *b;
522
523     if (strstr(new, word) != NULL)
524         return 1;
525
526     /* now reverse the word, we can do that in place
527        as it is strdup-ed */
528     f = word;
529     b = word+strlen(word)-1;
530     while (f < b) {
531         char c;
532
533         c = *f;
534         *f = *b;
535         *b = c;
536         --b;
537         ++f;
538     }
539
540     if (strstr(new, word) != NULL)
541         return 1;
542     return 0;
543 }
544
545 static int usercheck(struct cracklib_options *opt, const char *new,
546                      char *user)
547 {
548     if (!opt->reject_user)
549         return 0;
550
551     return wordcheck(new, user);
552 }
553
554 static char * str_lower(char *string)
555 {
556         char *cp;
557
558         if (!string)
559                 return NULL;
560
561         for (cp = string; *cp; cp++)
562                 *cp = tolower(*cp);
563         return string;
564 }
565
566 static int gecoscheck(pam_handle_t *pamh, struct cracklib_options *opt, const char *new,
567                      const char *user)
568 {
569     struct passwd *pwd;
570     char *list;
571     char *p;
572     char *next;
573
574     if (!opt->gecos_check)
575         return 0;
576
577     if ((pwd = pam_modutil_getpwnam(pamh, user)) == NULL) {
578         return 0;
579     }
580
581     list = strdup(pwd->pw_gecos);
582
583     if (list == NULL || *list == '\0') {
584         free(list);
585         return 0;
586     }
587
588     for (p = list;;p = next + 1) {
589          next = strchr(p, ' ');
590          if (next)
591              *next = '\0';
592
593          if (strlen(p) >= CO_MIN_WORD_LENGTH) {
594              str_lower(p);
595              if (wordcheck(new, p)) {
596                  free(list);
597                  return 1;
598              }
599          }
600
601          if (!next)
602              break;
603     }
604
605     free(list);
606     return 0;
607 }
608
609 static const char *password_check(pam_handle_t *pamh, struct cracklib_options *opt,
610                                   const char *old, const char *new,
611                                   const char *user)
612 {
613         const char *msg = NULL;
614         char *oldmono = NULL, *newmono, *wrapped = NULL;
615         char *usermono = NULL;
616
617         if (old && strcmp(new, old) == 0) {
618             msg = _("is the same as the old one");
619             return msg;
620         }
621
622         newmono = str_lower(x_strdup(new));
623         if (!newmono)
624                 msg = _("memory allocation error");
625
626         usermono = str_lower(x_strdup(user));
627         if (!usermono)
628                 msg = _("memory allocation error");
629
630         if (!msg && old) {
631                 oldmono = str_lower(x_strdup(old));
632                 if (oldmono)
633                         wrapped = malloc(strlen(oldmono) * 2 + 1);
634                 if (wrapped) {
635                         strcpy (wrapped, oldmono);
636                         strcat (wrapped, oldmono);
637                 } else {
638                         msg = _("memory allocation error");
639                 }
640         }
641
642         if (!msg && palindrome(newmono))
643                 msg = _("is a palindrome");
644
645         if (!msg && oldmono && strcmp(oldmono, newmono) == 0)
646                 msg = _("case changes only");
647
648         if (!msg && oldmono && similar(opt, oldmono, newmono))
649                 msg = _("is too similar to the old one");
650
651         if (!msg && simple(opt, new))
652                 msg = _("is too simple");
653
654         if (!msg && wrapped && strstr(wrapped, newmono))
655                 msg = _("is rotated");
656
657         if (!msg && minclass (opt, new))
658                 msg = _("not enough character classes");
659
660         if (!msg && consecutive(opt, new))
661                 msg = _("contains too many same characters consecutively");
662
663         if (!msg && sequence(opt, new))
664                 msg = _("contains too long of a monotonic character sequence");
665
666         if (!msg && (usercheck(opt, newmono, usermono) || gecoscheck(pamh, opt, newmono, user)))
667                 msg = _("contains the user name in some form");
668
669         free(usermono);
670         if (newmono) {
671                 memset(newmono, 0, strlen(newmono));
672                 free(newmono);
673         }
674         if (oldmono) {
675           memset(oldmono, 0, strlen(oldmono));
676           free(oldmono);
677         }
678         if (wrapped) {
679           memset(wrapped, 0, strlen(wrapped));
680           free(wrapped);
681         }
682
683         return msg;
684 }
685
686
687 static int _pam_unix_approve_pass(pam_handle_t *pamh,
688                                   unsigned int ctrl,
689                                   struct cracklib_options *opt,
690                                   const char *pass_old,
691                                   const char *pass_new)
692 {
693     const char *msg = NULL;
694     const char *user;
695     int retval;
696
697     if (pass_new == NULL || (pass_old && !strcmp(pass_old,pass_new))) {
698         if (ctrl & PAM_DEBUG_ARG)
699             pam_syslog(pamh, LOG_DEBUG, "bad authentication token");
700         pam_error(pamh, "%s", pass_new == NULL ?
701                    _("No password supplied"):_("Password unchanged"));
702         return PAM_AUTHTOK_ERR;
703     }
704
705     retval = pam_get_user(pamh, &user, NULL);
706     if (retval != PAM_SUCCESS || user == NULL) {
707         if (ctrl & PAM_DEBUG_ARG)
708                 pam_syslog(pamh,LOG_ERR,"Can not get username");
709         return PAM_AUTHTOK_ERR;
710     }
711     /*
712      * if one wanted to hardwire authentication token strength
713      * checking this would be the place
714      */
715     msg = password_check(pamh, opt, pass_old, pass_new, user);
716
717     if (msg) {
718         if (ctrl & PAM_DEBUG_ARG)
719             pam_syslog(pamh, LOG_NOTICE,
720                        "new passwd fails strength check: %s", msg);
721         pam_error(pamh, _("BAD PASSWORD: %s"), msg);
722         return PAM_AUTHTOK_ERR;
723     };
724     return PAM_SUCCESS;
725
726 }
727
728 /* The Main Thing (by Cristian Gafton, CEO at this module :-)
729  * (stolen from http://home.netscape.com)
730  */
731 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
732                                 int argc, const char **argv)
733 {
734     unsigned int ctrl;
735     struct cracklib_options options;
736
737     D(("called."));
738
739     memset(&options, 0, sizeof(options));
740     options.retry_times = CO_RETRY_TIMES;
741     options.diff_ok = CO_DIFF_OK;
742     options.min_length = CO_MIN_LENGTH;
743     options.dig_credit = CO_DIG_CREDIT;
744     options.up_credit = CO_UP_CREDIT;
745     options.low_credit = CO_LOW_CREDIT;
746     options.oth_credit = CO_OTH_CREDIT;
747     options.cracklib_dictpath = CRACKLIB_DICTS;
748
749     ctrl = _pam_parse(pamh, &options, argc, argv);
750
751     if (flags & PAM_PRELIM_CHECK) {
752         /* Check for passwd dictionary */
753         /* We cannot do that, since the original path is compiled
754            into the cracklib library and we don't know it.  */
755         return PAM_SUCCESS;
756     } else if (flags & PAM_UPDATE_AUTHTOK) {
757         int retval;
758         const void *oldtoken;
759         int tries;
760
761         D(("do update"));
762
763
764         retval = pam_get_item (pamh, PAM_OLDAUTHTOK, &oldtoken);
765         if (retval != PAM_SUCCESS) {
766             if (ctrl & PAM_DEBUG_ARG)
767                 pam_syslog(pamh,LOG_ERR,"Can not get old passwd");
768             oldtoken = NULL;
769         }
770
771         tries = 0;
772         while (tries < options.retry_times) {
773           const char *crack_msg;
774           const char *newtoken = NULL;
775
776
777           tries++;
778
779           /* Planned modus operandi:
780            * Get a passwd.
781            * Verify it against cracklib.
782            * If okay get it a second time.
783            * Check to be the same with the first one.
784            * set PAM_AUTHTOK and return
785            */
786
787           retval = pam_get_authtok_noverify (pamh, &newtoken, NULL);
788           if (retval != PAM_SUCCESS) {
789             pam_syslog(pamh, LOG_ERR, "pam_get_authtok_noverify returned error: %s",
790                        pam_strerror (pamh, retval));
791             continue;
792           } else if (newtoken == NULL) {      /* user aborted password change, quit */
793             return PAM_AUTHTOK_ERR;
794           }
795
796           D(("testing password"));
797           /* now test this passwd against cracklib */
798
799           D(("against cracklib"));
800           if ((crack_msg = FascistCheck (newtoken, options.cracklib_dictpath))) {
801             if (ctrl & PAM_DEBUG_ARG)
802               pam_syslog(pamh,LOG_DEBUG,"bad password: %s",crack_msg);
803             pam_error (pamh, _("BAD PASSWORD: %s"), crack_msg);
804             if (getuid() || options.enforce_for_root || (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
805               {
806                 pam_set_item (pamh, PAM_AUTHTOK, NULL);
807                 retval = PAM_AUTHTOK_ERR;
808                 continue;
809               }
810           }
811
812           /* check it for strength too... */
813           D(("for strength"));
814           retval = _pam_unix_approve_pass (pamh, ctrl, &options,
815                                            oldtoken, newtoken);
816           if (retval != PAM_SUCCESS) {
817             if (getuid() || options.enforce_for_root || (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
818               {
819                 pam_set_item(pamh, PAM_AUTHTOK, NULL);
820                 retval = PAM_AUTHTOK_ERR;
821                 continue;
822               }
823           }
824
825           retval = pam_get_authtok_verify (pamh, &newtoken, NULL);
826           if (retval != PAM_SUCCESS) {
827             pam_syslog(pamh, LOG_ERR, "pam_get_authtok_verify returned error: %s",
828                        pam_strerror (pamh, retval));
829             pam_set_item(pamh, PAM_AUTHTOK, NULL);
830             continue;
831           } else if (newtoken == NULL) {      /* user aborted password change, quit */
832             return PAM_AUTHTOK_ERR;
833           }
834
835           return PAM_SUCCESS;
836         }
837
838         D(("returning because maxtries reached"));
839
840         pam_set_item (pamh, PAM_AUTHTOK, NULL);
841
842         /* if we have only one try, we can use the real reason,
843            else say that there were too many tries. */
844         if (options.retry_times > 1)
845           return PAM_MAXTRIES;
846         else
847           return retval;
848
849     } else {
850         if (ctrl & PAM_DEBUG_ARG)
851             pam_syslog(pamh, LOG_NOTICE, "UNKNOWN flags setting %02X",flags);
852         return PAM_SERVICE_ERR;
853     }
854
855     /* Not reached */
856     return PAM_SERVICE_ERR;
857 }
858
859
860
861 #ifdef PAM_STATIC
862 /* static module data */
863 struct pam_module _pam_cracklib_modstruct = {
864      "pam_cracklib",
865      NULL,
866      NULL,
867      NULL,
868      NULL,
869      NULL,
870      pam_sm_chauthtok
871 };
872 #endif
873
874 /*
875  * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1996.
876  *                                              All rights reserved
877  *
878  * Redistribution and use in source and binary forms, with or without
879  * modification, are permitted provided that the following conditions
880  * are met:
881  * 1. Redistributions of source code must retain the above copyright
882  *    notice, and the entire permission notice in its entirety,
883  *    including the disclaimer of warranties.
884  * 2. Redistributions in binary form must reproduce the above copyright
885  *    notice, this list of conditions and the following disclaimer in the
886  *    documentation and/or other materials provided with the distribution.
887  * 3. The name of the author may not be used to endorse or promote
888  *    products derived from this software without specific prior
889  *    written permission.
890  *
891  * ALTERNATIVELY, this product may be distributed under the terms of
892  * the GNU Public License, in which case the provisions of the GPL are
893  * required INSTEAD OF the above restrictions.  (This clause is
894  * necessary due to a potential bad interaction between the GPL and
895  * the restrictions contained in a BSD-style copyright.)
896  *
897  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
898  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
899  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
900  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
901  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
902  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
903  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
904  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
905  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
906  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
907  * OF THE POSSIBILITY OF SUCH DAMAGE.
908  *
909  * The following copyright was appended for the long password support
910  * added with the libpam 0.58 release:
911  *
912  * Modificaton Copyright (c) Philip W. Dalrymple III <pwd@mdtsoft.com>
913  *       1997. All rights reserved
914  *
915  * THE MODIFICATION THAT PROVIDES SUPPORT FOR LONG PASSWORD TYPE CHECKING TO
916  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
917  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
918  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
919  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
920  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
921  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
922  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
923  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
924  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
925  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
926  * OF THE POSSIBILITY OF SUCH DAMAGE.
927  */