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