]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/pam_unix_passwd.c
Relevant BUGIDs:
[linux-pam] / modules / pam_unix / pam_unix_passwd.c
1 /*
2  * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
3  * Copyright (C) 1996.
4  * Copyright (c) Jan Rêkorajski, 1999.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, and the entire permission notice in its entirety,
11  *    including the disclaimer of warranties.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote
16  *    products derived from this software without specific prior
17  *    written permission.
18  *
19  * ALTERNATIVELY, this product may be distributed under the terms of
20  * the GNU Public License, in which case the provisions of the GPL are
21  * required INSTEAD OF the above restrictions.  (This clause is
22  * necessary due to a potential bad interaction between the GPL and
23  * the restrictions contained in a BSD-style copyright.)
24  *
25  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
26  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
29  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35  * OF THE POSSIBILITY OF SUCH DAMAGE.
36  */
37
38 #include "config.h"
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <stdarg.h>
43 #include <string.h>
44 #include <malloc.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include <sys/types.h>
48 #include <pwd.h>
49 #include <syslog.h>
50 #include <shadow.h>
51 #include <time.h>               /* for time() */
52 #include <fcntl.h>
53 #include <ctype.h>
54 #include <sys/time.h>
55 #include <sys/stat.h>
56 #include <rpc/rpc.h>
57 #include <rpcsvc/yp_prot.h>
58 #include <rpcsvc/ypclnt.h>
59
60 #include <signal.h>
61 #include <errno.h>
62 #include <sys/wait.h>
63 #ifdef WITH_SELINUX
64 static int selinux_enabled=-1;
65 #include <selinux/selinux.h>
66 static security_context_t prev_context=NULL;
67 #define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
68 #endif
69
70 #ifdef USE_CRACKLIB
71 #include <crack.h>
72 #endif
73
74 #include <security/_pam_macros.h>
75
76 /* indicate the following groups are defined */
77
78 #define PAM_SM_PASSWORD
79
80 #include <security/pam_modules.h>
81 #include <security/pam_ext.h>
82 #include <security/pam_modutil.h>
83
84 #include "yppasswd.h"
85 #include "md5.h"
86 #include "support.h"
87
88 #if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
89 extern int getrpcport(const char *host, unsigned long prognum,
90                       unsigned long versnum, unsigned int proto);
91 #endif                          /* GNU libc 2.1 */
92
93 /*
94  * PAM framework looks for these entry-points to pass control to the
95  * password changing module.
96  */
97
98 #if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
99 # include "./lckpwdf.-c"
100 #endif
101
102 extern char *bigcrypt(const char *key, const char *salt);
103
104 /*
105    How it works:
106    Gets in username (has to be done) from the calling program
107    Does authentication of user (only if we are not running as root)
108    Gets new password/checks for sanity
109    Sets it.
110  */
111
112 /* passwd/salt conversion macros */
113
114 #define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
115 #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
116
117 /* data tokens */
118
119 #define _UNIX_OLD_AUTHTOK       "-UN*X-OLD-PASS"
120 #define _UNIX_NEW_AUTHTOK       "-UN*X-NEW-PASS"
121
122 #define MAX_PASSWD_TRIES        3
123 #define PW_TMPFILE              "/etc/npasswd"
124 #define SH_TMPFILE              "/etc/nshadow"
125 #ifndef CRACKLIB_DICTS
126 #define CRACKLIB_DICTS          NULL
127 #endif
128 #define OPW_TMPFILE             "/etc/security/nopasswd"
129 #define OLD_PASSWORDS_FILE      "/etc/security/opasswd"
130
131 /*
132  * i64c - convert an integer to a radix 64 character
133  */
134 static int i64c(int i)
135 {
136         if (i < 0)
137                 return ('.');
138         else if (i > 63)
139                 return ('z');
140         if (i == 0)
141                 return ('.');
142         if (i == 1)
143                 return ('/');
144         if (i >= 2 && i <= 11)
145                 return ('0' - 2 + i);
146         if (i >= 12 && i <= 37)
147                 return ('A' - 12 + i);
148         if (i >= 38 && i <= 63)
149                 return ('a' - 38 + i);
150         return ('\0');
151 }
152
153 static char *crypt_md5_wrapper(const char *pass_new)
154 {
155         /*
156          * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
157          * removed use of static variables (AGM)
158          */
159
160         struct timeval tv;
161         MD5_CTX ctx;
162         unsigned char result[16];
163         char *cp = (char *) result;
164         unsigned char tmp[16];
165         int i;
166         char *x = NULL;
167
168         GoodMD5Init(&ctx);
169         gettimeofday(&tv, (struct timezone *) 0);
170         GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
171         i = getpid();
172         GoodMD5Update(&ctx, (void *) &i, sizeof i);
173         i = clock();
174         GoodMD5Update(&ctx, (void *) &i, sizeof i);
175         GoodMD5Update(&ctx, result, sizeof result);
176         GoodMD5Final(tmp, &ctx);
177         strcpy(cp, "$1$");      /* magic for the MD5 */
178         cp += strlen(cp);
179         for (i = 0; i < 8; i++)
180                 *cp++ = i64c(tmp[i] & 077);
181         *cp = '\0';
182
183         /* no longer need cleartext */
184         x = Goodcrypt_md5(pass_new, (const char *) result);
185
186         return x;
187 }
188
189 static char *getNISserver(pam_handle_t *pamh)
190 {
191         char *master;
192         char *domainname;
193         int port, err;
194
195         if ((err = yp_get_default_domain(&domainname)) != 0) {
196                 pam_syslog(pamh, LOG_WARNING, "can't get local yp domain: %s",
197                          yperr_string(err));
198                 return NULL;
199         }
200         if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
201                 pam_syslog(pamh, LOG_WARNING, "can't find the master ypserver: %s",
202                          yperr_string(err));
203                 return NULL;
204         }
205         port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
206         if (port == 0) {
207                 pam_syslog(pamh, LOG_WARNING,
208                          "yppasswdd not running on NIS master host");
209                 return NULL;
210         }
211         if (port >= IPPORT_RESERVED) {
212                 pam_syslog(pamh, LOG_WARNING,
213                          "yppasswd daemon running on illegal port");
214                 return NULL;
215         }
216         return master;
217 }
218
219 #ifdef WITH_SELINUX
220
221 static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user, const char *fromwhat, const char *towhat)
222 {
223     int retval, child, fds[2];
224     void (*sighandler)(int) = NULL;
225
226     D(("called."));
227     /* create a pipe for the password */
228     if (pipe(fds) != 0) {
229         D(("could not make pipe"));
230         return PAM_AUTH_ERR;
231     }
232
233     if (off(UNIX_NOREAP, ctrl)) {
234         /*
235          * This code arranges that the demise of the child does not cause
236          * the application to receive a signal it is not expecting - which
237          * may kill the application or worse.
238          *
239          * The "noreap" module argument is provided so that the admin can
240          * override this behavior.
241          */
242         sighandler = signal(SIGCHLD, SIG_DFL);
243     }
244
245     /* fork */
246     child = fork();
247     if (child == 0) {
248         size_t i=0;
249         struct rlimit rlim;
250         static char *envp[] = { NULL };
251         char *args[] = { NULL, NULL, NULL, NULL };
252
253         /* XXX - should really tidy up PAM here too */
254
255         close(0); close(1);
256         /* reopen stdin as pipe */
257         close(fds[1]);
258         dup2(fds[0], STDIN_FILENO);
259
260         if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
261           for (i=2; i < rlim.rlim_max; i++) {
262             if ((unsigned int)fds[0] != i)
263                    close(i);
264           }
265         }
266
267         if (SELINUX_ENABLED && geteuid() == 0) {
268           /* must set the real uid to 0 so the helper will not error
269              out if pam is called from setuid binary (su, sudo...) */
270           setuid(0);
271         }
272
273         /* exec binary helper */
274         args[0] = x_strdup(CHKPWD_HELPER);
275         args[1] = x_strdup(user);
276         args[2] = x_strdup("shadow");
277
278         execve(CHKPWD_HELPER, args, envp);
279
280         /* should not get here: exit with error */
281         D(("helper binary is not available"));
282         exit(PAM_AUTHINFO_UNAVAIL);
283     } else if (child > 0) {
284         /* wait for child */
285         /* if the stored password is NULL */
286         int rc=0;
287         if (fromwhat)
288           pam_modutil_write(fds[1], fromwhat, strlen(fromwhat)+1);
289         else
290           pam_modutil_write(fds[1], "", 1);
291         if (towhat) {
292           pam_modutil_write(fds[1], towhat, strlen(towhat)+1);
293         }
294         else
295           pam_modutil_write(fds[1], "", 1);
296
297         close(fds[0]);       /* close here to avoid possible SIGPIPE above */
298         close(fds[1]);
299         rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
300         if (rc<0) {
301           pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
302           retval = PAM_AUTH_ERR;
303         } else {
304           retval = WEXITSTATUS(retval);
305         }
306     } else {
307         D(("fork failed"));
308         close(fds[0]);
309         close(fds[1]);
310         retval = PAM_AUTH_ERR;
311     }
312
313     if (sighandler != NULL) {
314         (void) signal(SIGCHLD, sighandler);   /* restore old signal handler */
315     }
316
317     return retval;
318 }
319 #endif
320
321 static int check_old_password(const char *forwho, const char *newpass)
322 {
323         static char buf[16384];
324         char *s_luser, *s_uid, *s_npas, *s_pas;
325         int retval = PAM_SUCCESS;
326         FILE *opwfile;
327
328         opwfile = fopen(OLD_PASSWORDS_FILE, "r");
329         if (opwfile == NULL)
330                 return PAM_ABORT;
331
332         while (fgets(buf, 16380, opwfile)) {
333                 if (!strncmp(buf, forwho, strlen(forwho))) {
334                         buf[strlen(buf) - 1] = '\0';
335                         s_luser = strtok(buf, ":,");
336                         s_uid = strtok(NULL, ":,");
337                         s_npas = strtok(NULL, ":,");
338                         s_pas = strtok(NULL, ":,");
339                         while (s_pas != NULL) {
340                                 char *md5pass = Goodcrypt_md5(newpass, s_pas);
341                                 if (!strcmp(md5pass, s_pas)) {
342                                         _pam_delete(md5pass);
343                                         retval = PAM_AUTHTOK_ERR;
344                                         break;
345                                 }
346                                 s_pas = strtok(NULL, ":,");
347                                 _pam_delete(md5pass);
348                         }
349                         break;
350                 }
351         }
352         fclose(opwfile);
353
354         return retval;
355 }
356
357 static int save_old_password(pam_handle_t *pamh,
358                              const char *forwho, const char *oldpass,
359                              int howmany)
360 {
361     static char buf[16384];
362     static char nbuf[16384];
363     char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
364     int npas;
365     FILE *pwfile, *opwfile;
366     int err = 0;
367     int oldmask;
368     int found = 0;
369     struct passwd *pwd = NULL;
370     struct stat st;
371
372     if (howmany < 0) {
373         return PAM_SUCCESS;
374     }
375
376     if (oldpass == NULL) {
377         return PAM_SUCCESS;
378     }
379
380     oldmask = umask(077);
381
382 #ifdef WITH_SELINUX
383     if (SELINUX_ENABLED) {
384       security_context_t passwd_context=NULL;
385       if (getfilecon("/etc/passwd",&passwd_context)<0) {
386         return PAM_AUTHTOK_ERR;
387       };
388       if (getfscreatecon(&prev_context)<0) {
389         freecon(passwd_context);
390         return PAM_AUTHTOK_ERR;
391       }
392       if (setfscreatecon(passwd_context)) {
393         freecon(passwd_context);
394         freecon(prev_context);
395         return PAM_AUTHTOK_ERR;
396       }
397       freecon(passwd_context);
398     }
399 #endif
400     pwfile = fopen(OPW_TMPFILE, "w");
401     umask(oldmask);
402     if (pwfile == NULL) {
403       err = 1;
404       goto done;
405     }
406
407     opwfile = fopen(OLD_PASSWORDS_FILE, "r");
408     if (opwfile == NULL) {
409         fclose(pwfile);
410       err = 1;
411       goto done;
412     }
413
414     if (fstat(fileno(opwfile), &st) == -1) {
415         fclose(opwfile);
416         fclose(pwfile);
417         err = 1;
418         goto done;
419     }
420
421     if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
422         fclose(opwfile);
423         fclose(pwfile);
424         err = 1;
425         goto done;
426     }
427     if (fchmod(fileno(pwfile), st.st_mode) == -1) {
428         fclose(opwfile);
429         fclose(pwfile);
430         err = 1;
431         goto done;
432     }
433
434     while (fgets(buf, 16380, opwfile)) {
435         if (!strncmp(buf, forwho, strlen(forwho))) {
436             buf[strlen(buf) - 1] = '\0';
437             s_luser = strtok(buf, ":");
438             s_uid = strtok(NULL, ":");
439             s_npas = strtok(NULL, ":");
440             s_pas = strtok(NULL, ":");
441             npas = strtol(s_npas, NULL, 10) + 1;
442             while (npas > howmany) {
443                 s_pas = strpbrk(s_pas, ",");
444                 if (s_pas != NULL)
445                     s_pas++;
446                 npas--;
447             }
448             pass = crypt_md5_wrapper(oldpass);
449             if (s_pas == NULL)
450                 snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
451                          s_luser, s_uid, npas, pass);
452             else
453                 snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
454                          s_luser, s_uid, npas, s_pas, pass);
455             _pam_delete(pass);
456             if (fputs(nbuf, pwfile) < 0) {
457                 err = 1;
458                 break;
459             }
460             found = 1;
461         } else if (fputs(buf, pwfile) < 0) {
462             err = 1;
463             break;
464         }
465     }
466     fclose(opwfile);
467
468     if (!found) {
469         pwd = pam_modutil_getpwnam(pamh, forwho);
470         if (pwd == NULL) {
471             err = 1;
472         } else {
473             pass = crypt_md5_wrapper(oldpass);
474             snprintf(nbuf, sizeof(nbuf), "%s:%d:1:%s\n",
475                      forwho, pwd->pw_uid, pass);
476             _pam_delete(pass);
477             if (fputs(nbuf, pwfile) < 0) {
478                 err = 1;
479             }
480         }
481     }
482
483     if (fclose(pwfile)) {
484         D(("error writing entries to old passwords file: %m"));
485         err = 1;
486     }
487
488 done:
489     if (!err) {
490         if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
491             err = 1;
492     }
493 #ifdef WITH_SELINUX
494     if (SELINUX_ENABLED) {
495       if (setfscreatecon(prev_context)) {
496         err = 1;
497       }
498       if (prev_context)
499         freecon(prev_context);
500       prev_context=NULL;
501     }
502 #endif
503     if (!err) {
504         return PAM_SUCCESS;
505     } else {
506         unlink(OPW_TMPFILE);
507         return PAM_AUTHTOK_ERR;
508     }
509 }
510
511 static int _update_passwd(pam_handle_t *pamh,
512                           const char *forwho, const char *towhat)
513 {
514     struct passwd *tmpent = NULL;
515     struct stat st;
516     FILE *pwfile, *opwfile;
517     int err = 1;
518     int oldmask;
519
520     oldmask = umask(077);
521 #ifdef WITH_SELINUX
522     if (SELINUX_ENABLED) {
523       security_context_t passwd_context=NULL;
524       if (getfilecon("/etc/passwd",&passwd_context)<0) {
525         return PAM_AUTHTOK_ERR;
526       };
527       if (getfscreatecon(&prev_context)<0) {
528         freecon(passwd_context);
529         return PAM_AUTHTOK_ERR;
530       }
531       if (setfscreatecon(passwd_context)) {
532         freecon(passwd_context);
533         freecon(prev_context);
534         return PAM_AUTHTOK_ERR;
535       }
536       freecon(passwd_context);
537     }
538 #endif
539     pwfile = fopen(PW_TMPFILE, "w");
540     umask(oldmask);
541     if (pwfile == NULL) {
542       err = 1;
543       goto done;
544     }
545
546     opwfile = fopen("/etc/passwd", "r");
547     if (opwfile == NULL) {
548         fclose(pwfile);
549         err = 1;
550         goto done;
551     }
552
553     if (fstat(fileno(opwfile), &st) == -1) {
554         fclose(opwfile);
555         fclose(pwfile);
556         err = 1;
557         goto done;
558     }
559
560     if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
561         fclose(opwfile);
562         fclose(pwfile);
563         err = 1;
564         goto done;
565     }
566     if (fchmod(fileno(pwfile), st.st_mode) == -1) {
567         fclose(opwfile);
568         fclose(pwfile);
569         err = 1;
570         goto done;
571     }
572
573     tmpent = fgetpwent(opwfile);
574     while (tmpent) {
575         if (!strcmp(tmpent->pw_name, forwho)) {
576             /* To shut gcc up */
577             union {
578                 const char *const_charp;
579                 char *charp;
580             } assigned_passwd;
581             assigned_passwd.const_charp = towhat;
582
583             tmpent->pw_passwd = assigned_passwd.charp;
584             err = 0;
585         }
586         if (putpwent(tmpent, pwfile)) {
587             D(("error writing entry to password file: %m"));
588             err = 1;
589             break;
590         }
591         tmpent = fgetpwent(opwfile);
592     }
593     fclose(opwfile);
594
595     if (fclose(pwfile)) {
596         D(("error writing entries to password file: %m"));
597         err = 1;
598     }
599
600 done:
601     if (!err) {
602         if (!rename(PW_TMPFILE, "/etc/passwd"))
603             pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
604         else
605             err = 1;
606     }
607 #ifdef WITH_SELINUX
608     if (SELINUX_ENABLED) {
609       if (setfscreatecon(prev_context)) {
610         err = 1;
611       }
612       if (prev_context)
613         freecon(prev_context);
614       prev_context=NULL;
615     }
616 #endif
617     if (!err) {
618         return PAM_SUCCESS;
619     } else {
620         unlink(PW_TMPFILE);
621         return PAM_AUTHTOK_ERR;
622     }
623 }
624
625 static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
626 {
627     struct spwd *spwdent = NULL, *stmpent = NULL;
628     struct stat st;
629     FILE *pwfile, *opwfile;
630     int err = 1;
631     int oldmask;
632
633     spwdent = getspnam(forwho);
634     if (spwdent == NULL) {
635         return PAM_USER_UNKNOWN;
636     }
637     oldmask = umask(077);
638
639 #ifdef WITH_SELINUX
640     if (SELINUX_ENABLED) {
641       security_context_t shadow_context=NULL;
642       if (getfilecon("/etc/shadow",&shadow_context)<0) {
643         return PAM_AUTHTOK_ERR;
644       };
645       if (getfscreatecon(&prev_context)<0) {
646         freecon(shadow_context);
647         return PAM_AUTHTOK_ERR;
648       }
649       if (setfscreatecon(shadow_context)) {
650         freecon(shadow_context);
651         freecon(prev_context);
652         return PAM_AUTHTOK_ERR;
653       }
654       freecon(shadow_context);
655     }
656 #endif
657     pwfile = fopen(SH_TMPFILE, "w");
658     umask(oldmask);
659     if (pwfile == NULL) {
660         err = 1;
661         goto done;
662     }
663
664     opwfile = fopen("/etc/shadow", "r");
665     if (opwfile == NULL) {
666         fclose(pwfile);
667         err = 1;
668         goto done;
669     }
670
671     if (fstat(fileno(opwfile), &st) == -1) {
672         fclose(opwfile);
673         fclose(pwfile);
674         err = 1;
675         goto done;
676     }
677
678     if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
679         fclose(opwfile);
680         fclose(pwfile);
681         err = 1;
682         goto done;
683     }
684     if (fchmod(fileno(pwfile), st.st_mode) == -1) {
685         fclose(opwfile);
686         fclose(pwfile);
687         err = 1;
688         goto done;
689     }
690
691     stmpent = fgetspent(opwfile);
692     while (stmpent) {
693
694         if (!strcmp(stmpent->sp_namp, forwho)) {
695             stmpent->sp_pwdp = towhat;
696             stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
697             err = 0;
698             D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
699         }
700
701         if (putspent(stmpent, pwfile)) {
702             D(("error writing entry to shadow file: %m"));
703             err = 1;
704             break;
705         }
706
707         stmpent = fgetspent(opwfile);
708     }
709     fclose(opwfile);
710
711     if (fclose(pwfile)) {
712         D(("error writing entries to shadow file: %m"));
713         err = 1;
714     }
715
716  done:
717     if (!err) {
718         if (!rename(SH_TMPFILE, "/etc/shadow"))
719             pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
720         else
721             err = 1;
722     }
723
724 #ifdef WITH_SELINUX
725     if (SELINUX_ENABLED) {
726       if (setfscreatecon(prev_context)) {
727         err = 1;
728       }
729       if (prev_context)
730         freecon(prev_context);
731       prev_context=NULL;
732     }
733 #endif
734
735     if (!err) {
736         return PAM_SUCCESS;
737     } else {
738         unlink(SH_TMPFILE);
739         return PAM_AUTHTOK_ERR;
740     }
741 }
742
743 static int _do_setpass(pam_handle_t* pamh, const char *forwho,
744                        const char *fromwhat,
745                        char *towhat, unsigned int ctrl, int remember)
746 {
747         struct passwd *pwd = NULL;
748         int retval = 0;
749         int unlocked = 0;
750         char *master = NULL;
751
752         D(("called"));
753
754         pwd = getpwnam(forwho);
755
756         if (pwd == NULL) {
757                 retval = PAM_AUTHTOK_ERR;
758                 goto done;
759         }
760
761         if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) {
762             if ((master=getNISserver(pamh)) != NULL) {
763                 struct timeval timeout;
764                 struct yppasswd yppwd;
765                 CLIENT *clnt;
766                 int status;
767                 int err = 0;
768
769                 /* Unlock passwd file to avoid deadlock */
770 #ifdef USE_LCKPWDF
771                 ulckpwdf();
772 #endif
773                 unlocked = 1;
774
775                 /* Initialize password information */
776                 yppwd.newpw.pw_passwd = pwd->pw_passwd;
777                 yppwd.newpw.pw_name = pwd->pw_name;
778                 yppwd.newpw.pw_uid = pwd->pw_uid;
779                 yppwd.newpw.pw_gid = pwd->pw_gid;
780                 yppwd.newpw.pw_gecos = pwd->pw_gecos;
781                 yppwd.newpw.pw_dir = pwd->pw_dir;
782                 yppwd.newpw.pw_shell = pwd->pw_shell;
783                 yppwd.oldpass = fromwhat ? strdup (fromwhat) : strdup ("");
784                 yppwd.newpw.pw_passwd = towhat;
785
786                 D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
787
788                 /* The yppasswd.x file said `unix authentication required',
789                  * so I added it. This is the only reason it is in here.
790                  * My yppasswdd doesn't use it, but maybe some others out there
791                  * do.                                        --okir
792                  */
793                 clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
794                 clnt->cl_auth = authunix_create_default();
795                 memset((char *) &status, '\0', sizeof(status));
796                 timeout.tv_sec = 25;
797                 timeout.tv_usec = 0;
798                 err = clnt_call(clnt, YPPASSWDPROC_UPDATE,
799                                 (xdrproc_t) xdr_yppasswd, (char *) &yppwd,
800                                 (xdrproc_t) xdr_int, (char *) &status,
801                                 timeout);
802
803                 free (yppwd.oldpass);
804
805                 if (err) {
806                         _make_remark(pamh, ctrl, PAM_TEXT_INFO,
807                                 clnt_sperrno(err));
808                 } else if (status) {
809                         D(("Error while changing NIS password.\n"));
810                 }
811                 D(("The password has%s been changed on %s.",
812                    (err || status) ? " not" : "", master));
813                 pam_syslog(pamh, LOG_NOTICE, "password%s changed for %s on %s",
814                          (err || status) ? " not" : "", pwd->pw_name, master);
815
816                 auth_destroy(clnt->cl_auth);
817                 clnt_destroy(clnt);
818                 if (err || status) {
819                         _make_remark(pamh, ctrl, PAM_TEXT_INFO,
820                                 _("NIS password could not be changed."));
821                         retval = PAM_TRY_AGAIN;
822                 }
823 #ifdef DEBUG
824                 sleep(5);
825 #endif
826             } else {
827                     retval = PAM_TRY_AGAIN;
828             }
829         }
830
831         if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
832 #ifdef USE_LCKPWDF
833                 if(unlocked) {
834                         int i = 0;
835                         /* These values for the number of attempts and the sleep time
836                            are, of course, completely arbitrary.
837                            My reading of the PAM docs is that, once pam_chauthtok() has been
838                            called with PAM_UPDATE_AUTHTOK, we are obliged to take any
839                            reasonable steps to make sure the token is updated; so retrying
840                            for 1/10 sec. isn't overdoing it. */
841                         while((retval = lckpwdf()) != 0 && i < 100) {
842                                 usleep(1000);
843                                 i++;
844                         }
845                         if(retval != 0) {
846                                 return PAM_AUTHTOK_LOCK_BUSY;
847                         }
848                 }
849 #endif
850                 /* first, save old password */
851                 if (save_old_password(pamh, forwho, fromwhat, remember)) {
852                         retval = PAM_AUTHTOK_ERR;
853                         goto done;
854                 }
855                 if (on(UNIX_SHADOW, ctrl) || _unix_shadowed(pwd)) {
856                         retval = _update_shadow(pamh, forwho, towhat);
857 #ifdef WITH_SELINUX
858                         if (retval != PAM_SUCCESS && SELINUX_ENABLED)
859                           retval = _unix_run_shadow_binary(pamh, ctrl, forwho, fromwhat, towhat);
860 #endif
861                         if (retval == PAM_SUCCESS)
862                                 if (!_unix_shadowed(pwd))
863                                         retval = _update_passwd(pamh, forwho, "x");
864                 } else {
865                         retval = _update_passwd(pamh, forwho, towhat);
866                 }
867         }
868
869
870 done:
871 #ifdef USE_LCKPWDF
872         ulckpwdf();
873 #endif
874
875         return retval;
876 }
877
878 static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned int ctrl)
879 {
880         struct passwd *pwd = NULL;      /* Password and shadow password */
881         struct spwd *spwdent = NULL;    /* file entries for the user */
882         time_t curdays;
883         int retval = PAM_SUCCESS;
884
885         /* UNIX passwords area */
886         pwd = getpwnam(user);   /* Get password file entry... */
887         if (pwd == NULL)
888                 return PAM_AUTHINFO_UNAVAIL;    /* We don't need to do the rest... */
889
890         if (_unix_shadowed(pwd)) {
891                 /* ...and shadow password file entry for this user, if shadowing
892                    is enabled */
893                 setspent();
894                 spwdent = getspnam(user);
895                 endspent();
896
897 #ifdef WITH_SELINUX
898                 if (spwdent == NULL && SELINUX_ENABLED )
899                     spwdent = _unix_run_verify_binary(pamh, ctrl, user);
900 #endif
901                 if (spwdent == NULL)
902                         return PAM_AUTHINFO_UNAVAIL;
903         } else {
904                 if (strcmp(pwd->pw_passwd,"*NP*") == 0) { /* NIS+ */
905                         uid_t save_uid;
906
907                         save_uid = geteuid();
908                         seteuid (pwd->pw_uid);
909                         spwdent = getspnam( user );
910                         seteuid (save_uid);
911
912                         if (spwdent == NULL)
913                                 return PAM_AUTHINFO_UNAVAIL;
914                 } else
915                         spwdent = NULL;
916         }
917
918         if (spwdent != NULL) {
919                 /* We have the user's information, now let's check if their account
920                    has expired (60 * 60 * 24 = number of seconds in a day) */
921
922                 if (off(UNIX__IAMROOT, ctrl)) {
923                         /* Get the current number of days since 1970 */
924                         curdays = time(NULL) / (60 * 60 * 24);
925                         if (curdays < spwdent->sp_lstchg) {
926                                 pam_syslog(pamh, LOG_DEBUG,
927                                         "account %s has password changed in future",
928                                         user);
929                                 curdays = spwdent->sp_lstchg;
930                         }
931                         if ((curdays - spwdent->sp_lstchg < spwdent->sp_min)
932                                  && (spwdent->sp_min != -1))
933                                 /*
934                                  * The last password change was too recent.
935                                  */
936                                 retval = PAM_AUTHTOK_ERR;
937                         else if ((curdays - spwdent->sp_lstchg > spwdent->sp_max)
938                                  && (curdays - spwdent->sp_lstchg > spwdent->sp_inact)
939                                  && (curdays - spwdent->sp_lstchg >
940                                      spwdent->sp_max + spwdent->sp_inact)
941                                  && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
942                                  && (spwdent->sp_lstchg != 0))
943                                 /*
944                                  * Their password change has been put off too long,
945                                  */
946                                 retval = PAM_ACCT_EXPIRED;
947                         else if ((curdays > spwdent->sp_expire) && (spwdent->sp_expire != -1)
948                                  && (spwdent->sp_lstchg != 0))
949                                 /*
950                                  * OR their account has just plain expired
951                                  */
952                                 retval = PAM_ACCT_EXPIRED;
953                 }
954         }
955         return retval;
956 }
957
958 static int _pam_unix_approve_pass(pam_handle_t * pamh
959                                   ,unsigned int ctrl
960                                   ,const char *pass_old
961                                   ,const char *pass_new)
962 {
963         const void *user;
964         const char *remark = NULL;
965         int retval = PAM_SUCCESS;
966
967         D(("&new=%p, &old=%p", pass_old, pass_new));
968         D(("new=[%s]", pass_new));
969         D(("old=[%s]", pass_old));
970
971         if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
972                 if (on(UNIX_DEBUG, ctrl)) {
973                         pam_syslog(pamh, LOG_DEBUG, "bad authentication token");
974                 }
975                 _make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
976                         _("No password supplied") : _("Password unchanged"));
977                 return PAM_AUTHTOK_ERR;
978         }
979         /*
980          * if one wanted to hardwire authentication token strength
981          * checking this would be the place - AGM
982          */
983
984         retval = pam_get_item(pamh, PAM_USER, &user);
985         if (retval != PAM_SUCCESS) {
986                 if (on(UNIX_DEBUG, ctrl)) {
987                         pam_syslog(pamh, LOG_ERR, "Can not get username");
988                         return PAM_AUTHTOK_ERR;
989                 }
990         }
991         if (off(UNIX__IAMROOT, ctrl)) {
992 #ifdef USE_CRACKLIB
993                 remark = FascistCheck (pass_new, CRACKLIB_DICTS);
994                 D(("called cracklib [%s]", remark));
995 #else
996                 if (strlen(pass_new) < 6)
997                   remark = _("You must choose a longer password");
998                 D(("length check [%s]", remark));
999 #endif
1000                 if (on(UNIX_REMEMBER_PASSWD, ctrl)) {
1001                         if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR)
1002                           remark = _("Password has been already used. Choose another.");
1003                         if (retval == PAM_ABORT) {
1004                                 pam_syslog(pamh, LOG_ERR, "can't open %s file to check old passwords",
1005                                         OLD_PASSWORDS_FILE);
1006                                 return retval;
1007                         }
1008                 }
1009         }
1010         if (remark) {
1011                 _make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
1012                 retval = PAM_AUTHTOK_ERR;
1013         }
1014         return retval;
1015 }
1016
1017
1018 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
1019                                 int argc, const char **argv)
1020 {
1021         unsigned int ctrl, lctrl;
1022         int retval, i;
1023         int remember = -1;
1024
1025         /* <DO NOT free() THESE> */
1026         const char *user;
1027         const void *pass_old, *pass_new;
1028         /* </DO NOT free() THESE> */
1029
1030         D(("called."));
1031
1032         ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);
1033
1034         /*
1035          * First get the name of a user
1036          */
1037         retval = pam_get_user(pamh, &user, NULL);
1038         if (retval == PAM_SUCCESS) {
1039                 /*
1040                  * Various libraries at various times have had bugs related to
1041                  * '+' or '-' as the first character of a user name. Don't take
1042                  * any chances here. Require that the username starts with an
1043                  * alphanumeric character.
1044                  */
1045                 if (user == NULL || !isalnum(*user)) {
1046                         pam_syslog(pamh, LOG_ERR, "bad username [%s]", user);
1047                         return PAM_USER_UNKNOWN;
1048                 }
1049                 if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
1050                         pam_syslog(pamh, LOG_DEBUG, "username [%s] obtained",
1051                                  user);
1052         } else {
1053                 if (on(UNIX_DEBUG, ctrl))
1054                         pam_syslog(pamh, LOG_DEBUG,
1055                                  "password - could not identify user");
1056                 return retval;
1057         }
1058
1059         D(("Got username of %s", user));
1060
1061         /*
1062          * Before we do anything else, check to make sure that the user's
1063          * info is in one of the databases we can modify from this module,
1064          * which currently is 'files' and 'nis'.  We have to do this because
1065          * getpwnam() doesn't tell you *where* the information it gives you
1066          * came from, nor should it.  That's our job.
1067          */
1068         if (_unix_comesfromsource(pamh, user, 1, on(UNIX_NIS, ctrl)) == 0) {
1069                 pam_syslog(pamh, LOG_DEBUG,
1070                          "user \"%s\" does not exist in /etc/passwd%s",
1071                          user, on(UNIX_NIS, ctrl) ? " or NIS" : "");
1072                 return PAM_USER_UNKNOWN;
1073         } else {
1074                 struct passwd *pwd;
1075                 _unix_getpwnam(pamh, user, 1, 1, &pwd);
1076                 if (pwd == NULL) {
1077                         pam_syslog(pamh, LOG_DEBUG,
1078                                 "user \"%s\" has corrupted passwd entry",
1079                                 user);
1080                         return PAM_USER_UNKNOWN;
1081                 }
1082                 if (!_unix_shadowed(pwd) &&
1083                     (strchr(pwd->pw_passwd, '*') != NULL)) {
1084                         pam_syslog(pamh, LOG_DEBUG,
1085                                 "user \"%s\" does not have modifiable password",
1086                                 user);
1087                         return PAM_USER_UNKNOWN;
1088                 }
1089         }
1090
1091         /*
1092          * This is not an AUTH module!
1093          */
1094         if (on(UNIX__NONULL, ctrl))
1095                 set(UNIX__NULLOK, ctrl);
1096
1097         if (on(UNIX__PRELIM, ctrl)) {
1098                 /*
1099                  * obtain and verify the current password (OLDAUTHTOK) for
1100                  * the user.
1101                  */
1102                 char *Announce;
1103
1104                 D(("prelim check"));
1105
1106                 if (_unix_blankpasswd(pamh, ctrl, user)) {
1107                         return PAM_SUCCESS;
1108                 } else if (off(UNIX__IAMROOT, ctrl)) {
1109
1110                         /* instruct user what is happening */
1111 #define greeting "Changing password for "
1112                         Announce = (char *) malloc(sizeof(greeting) + strlen(user));
1113                         if (Announce == NULL) {
1114                                 pam_syslog(pamh, LOG_CRIT,
1115                                          "password - out of memory");
1116                                 return PAM_BUF_ERR;
1117                         }
1118                         (void) strcpy(Announce, greeting);
1119                         (void) strcpy(Announce + sizeof(greeting) - 1, user);
1120 #undef greeting
1121
1122                         lctrl = ctrl;
1123                         set(UNIX__OLD_PASSWD, lctrl);
1124                         retval = _unix_read_password(pamh, lctrl
1125                                                      ,Announce
1126                                              ,_("(current) UNIX password: ")
1127                                                      ,NULL
1128                                                      ,_UNIX_OLD_AUTHTOK
1129                                              ,&pass_old);
1130                         free(Announce);
1131
1132                         if (retval != PAM_SUCCESS) {
1133                                 pam_syslog(pamh, LOG_NOTICE,
1134                                     "password - (old) token not obtained");
1135                                 return retval;
1136                         }
1137                         /* verify that this is the password for this user */
1138
1139                         retval = _unix_verify_password(pamh, user, pass_old, ctrl);
1140                 } else {
1141                         D(("process run by root so do nothing this time around"));
1142                         pass_old = NULL;
1143                         retval = PAM_SUCCESS;   /* root doesn't have too */
1144                 }
1145
1146                 if (retval != PAM_SUCCESS) {
1147                         D(("Authentication failed"));
1148                         pass_old = NULL;
1149                         return retval;
1150                 }
1151                 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
1152                 pass_old = NULL;
1153                 if (retval != PAM_SUCCESS) {
1154                         pam_syslog(pamh, LOG_CRIT,
1155                                  "failed to set PAM_OLDAUTHTOK");
1156                 }
1157                 retval = _unix_verify_shadow(pamh,user, ctrl);
1158                 if (retval == PAM_AUTHTOK_ERR) {
1159                         if (off(UNIX__IAMROOT, ctrl))
1160                                 _make_remark(pamh, ctrl, PAM_ERROR_MSG,
1161                                              _("You must wait longer to change your password"));
1162                         else
1163                                 retval = PAM_SUCCESS;
1164                 }
1165         } else if (on(UNIX__UPDATE, ctrl)) {
1166                 /*
1167                  * tpass is used below to store the _pam_md() return; it
1168                  * should be _pam_delete()'d.
1169                  */
1170
1171                 char *tpass = NULL;
1172                 int retry = 0;
1173
1174                 /*
1175                  * obtain the proposed password
1176                  */
1177
1178                 D(("do update"));
1179
1180                 /*
1181                  * get the old token back. NULL was ok only if root [at this
1182                  * point we assume that this has already been enforced on a
1183                  * previous call to this function].
1184                  */
1185
1186                 if (off(UNIX_NOT_SET_PASS, ctrl)) {
1187                         retval = pam_get_item(pamh, PAM_OLDAUTHTOK
1188                                               ,&pass_old);
1189                 } else {
1190                         retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
1191                                               ,&pass_old);
1192                         if (retval == PAM_NO_MODULE_DATA) {
1193                                 retval = PAM_SUCCESS;
1194                                 pass_old = NULL;
1195                         }
1196                 }
1197                 D(("pass_old [%s]", pass_old));
1198
1199                 if (retval != PAM_SUCCESS) {
1200                         pam_syslog(pamh, LOG_NOTICE, "user not authenticated");
1201                         return retval;
1202                 }
1203
1204                 D(("get new password now"));
1205
1206                 lctrl = ctrl;
1207
1208                 if (on(UNIX_USE_AUTHTOK, lctrl)) {
1209                         set(UNIX_USE_FIRST_PASS, lctrl);
1210                 }
1211                 retry = 0;
1212                 retval = PAM_AUTHTOK_ERR;
1213                 while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
1214                         /*
1215                          * use_authtok is to force the use of a previously entered
1216                          * password -- needed for pluggable password strength checking
1217                          */
1218
1219                         retval = _unix_read_password(pamh, lctrl
1220                                                      ,NULL
1221                                              ,_("Enter new UNIX password: ")
1222                                             ,_("Retype new UNIX password: ")
1223                                                      ,_UNIX_NEW_AUTHTOK
1224                                              ,&pass_new);
1225
1226                         if (retval != PAM_SUCCESS) {
1227                                 if (on(UNIX_DEBUG, ctrl)) {
1228                                         pam_syslog(pamh, LOG_ALERT,
1229                                                  "password - new password not obtained");
1230                                 }
1231                                 pass_old = NULL;        /* tidy up */
1232                                 return retval;
1233                         }
1234                         D(("returned to _unix_chauthtok"));
1235
1236                         /*
1237                          * At this point we know who the user is and what they
1238                          * propose as their new password. Verify that the new
1239                          * password is acceptable.
1240                          */
1241
1242                         if (*(const char *)pass_new == '\0') {  /* "\0" password = NULL */
1243                                 pass_new = NULL;
1244                         }
1245                         retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
1246                 }
1247
1248                 if (retval != PAM_SUCCESS) {
1249                         pam_syslog(pamh, LOG_NOTICE,
1250                                  "new password not acceptable");
1251                         pass_new = pass_old = NULL;     /* tidy up */
1252                         return retval;
1253                 }
1254 #ifdef USE_LCKPWDF
1255                 /* These values for the number of attempts and the sleep time
1256                    are, of course, completely arbitrary.
1257                    My reading of the PAM docs is that, once pam_chauthtok() has been
1258                    called with PAM_UPDATE_AUTHTOK, we are obliged to take any
1259                    reasonable steps to make sure the token is updated; so retrying
1260                    for 1/10 sec. isn't overdoing it. */
1261                 i=0;
1262                 while((retval = lckpwdf()) != 0 && i < 100) {
1263                         usleep(1000);
1264                         i++;
1265                 }
1266                 if(retval != 0) {
1267                         return PAM_AUTHTOK_LOCK_BUSY;
1268                 }
1269 #endif
1270
1271                 if (pass_old) {
1272                         retval = _unix_verify_password(pamh, user, pass_old, ctrl);
1273                         if (retval != PAM_SUCCESS) {
1274                                 pam_syslog(pamh, LOG_NOTICE, "user password changed by another process");
1275 #ifdef USE_LCKPWDF
1276                                 ulckpwdf();
1277 #endif
1278                                 return retval;
1279                         }
1280                 }
1281
1282                 retval = _unix_verify_shadow(pamh, user, ctrl);
1283                 if (retval != PAM_SUCCESS) {
1284                         pam_syslog(pamh, LOG_NOTICE, "user not authenticated 2");
1285 #ifdef USE_LCKPWDF
1286                         ulckpwdf();
1287 #endif
1288                         return retval;
1289                 }
1290
1291                 retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
1292                 if (retval != PAM_SUCCESS) {
1293                         pam_syslog(pamh, LOG_NOTICE,
1294                                  "new password not acceptable 2");
1295                         pass_new = pass_old = NULL;     /* tidy up */
1296 #ifdef USE_LCKPWDF
1297                         ulckpwdf();
1298 #endif
1299                         return retval;
1300                 }
1301
1302                 /*
1303                  * By reaching here we have approved the passwords and must now
1304                  * rebuild the password database file.
1305                  */
1306
1307                 /*
1308                  * First we encrypt the new password.
1309                  */
1310
1311                 if (on(UNIX_MD5_PASS, ctrl)) {
1312                         tpass = crypt_md5_wrapper(pass_new);
1313                 } else {
1314                         /*
1315                          * Salt manipulation is stolen from Rick Faith's passwd
1316                          * program.  Sorry Rick :) -- alex
1317                          */
1318
1319                         time_t tm;
1320                         char salt[3];
1321
1322                         time(&tm);
1323                         salt[0] = bin_to_ascii(tm & 0x3f);
1324                         salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
1325                         salt[2] = '\0';
1326
1327                         if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
1328                                 /*
1329                                  * to avoid using the _extensions_ of the bigcrypt()
1330                                  * function we truncate the newly entered password
1331                                  * [Problems that followed from this are fixed as per
1332                                  *  Bug 521314.]
1333                                  */
1334                                 char *temp = malloc(9);
1335
1336                                 if (temp == NULL) {
1337                                         pam_syslog(pamh, LOG_CRIT,
1338                                                  "out of memory for password");
1339                                         pass_new = pass_old = NULL;     /* tidy up */
1340 #ifdef USE_LCKPWDF
1341                                         ulckpwdf();
1342 #endif
1343                                         return PAM_BUF_ERR;
1344                                 }
1345                                 /* copy first 8 bytes of password */
1346                                 strncpy(temp, pass_new, 8);
1347                                 temp[8] = '\0';
1348
1349                                 /* no longer need cleartext */
1350                                 tpass = bigcrypt(temp, salt);
1351
1352                                 _pam_delete(temp);      /* tidy up */
1353                         } else {
1354                                 tpass = bigcrypt(pass_new, salt);
1355                         }
1356                 }
1357
1358                 D(("password processed"));
1359
1360                 /* update the password database(s) -- race conditions..? */
1361
1362                 retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
1363                                      remember);
1364                 /* _do_setpass has called ulckpwdf for us */
1365
1366                 _pam_delete(tpass);
1367                 pass_old = pass_new = NULL;
1368         } else {                /* something has broken with the module */
1369                 pam_syslog(pamh, LOG_ALERT,
1370                          "password received unknown request");
1371                 retval = PAM_ABORT;
1372         }
1373
1374         D(("retval was %d", retval));
1375
1376         return retval;
1377 }
1378
1379
1380 /* static module data */
1381 #ifdef PAM_STATIC
1382 struct pam_module _pam_unix_passwd_modstruct = {
1383     "pam_unix_passwd",
1384     NULL,
1385     NULL,
1386     NULL,
1387     NULL,
1388     NULL,
1389     pam_sm_chauthtok,
1390 };
1391 #endif