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