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