]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/pam_unix_passwd.c
9fdebefba64f55b6a7c2ef1b309bd87bd6bed644
[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  * Copyright (c) Red Hat, Inc., 2007, 2008.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, and the entire permission notice in its entirety,
12  *    including the disclaimer of warranties.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote
17  *    products derived from this software without specific prior
18  *    written permission.
19  *
20  * ALTERNATIVELY, this product may be distributed under the terms of
21  * the GNU Public License, in which case the provisions of the GPL are
22  * required INSTEAD OF the above restrictions.  (This clause is
23  * necessary due to a potential bad interaction between the GPL and
24  * the restrictions contained in a BSD-style copyright.)
25  *
26  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
27  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
30  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36  * OF THE POSSIBILITY OF SUCH DAMAGE.
37  */
38
39 #include "config.h"
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <stdarg.h>
44 #include <string.h>
45 #include <malloc.h>
46 #include <unistd.h>
47 #include <errno.h>
48 #include <sys/types.h>
49 #include <pwd.h>
50 #include <syslog.h>
51 #include <shadow.h>
52 #include <time.h>               /* for time() */
53 #include <fcntl.h>
54 #include <ctype.h>
55 #include <sys/time.h>
56 #include <sys/stat.h>
57
58 #include <signal.h>
59 #include <errno.h>
60 #include <sys/wait.h>
61 #include <sys/resource.h>
62
63 #include <security/_pam_macros.h>
64
65 /* indicate the following groups are defined */
66
67 #define PAM_SM_PASSWORD
68
69 #include <security/pam_modules.h>
70 #include <security/pam_ext.h>
71 #include <security/pam_modutil.h>
72
73 #include "md5.h"
74 #include "support.h"
75 #include "passverify.h"
76 #include "bigcrypt.h"
77
78 #if (HAVE_YP_GET_DEFAULT_DOMAIN || HAVE_GETDOMAINNAME) && HAVE_YP_MASTER
79 # define HAVE_NIS
80 #endif
81
82 #ifdef HAVE_NIS
83 # include <rpc/rpc.h>
84
85 # if HAVE_RPCSVC_YP_PROT_H
86 #  include <rpcsvc/yp_prot.h>
87 # endif
88
89 # if HAVE_RPCSVC_YPCLNT_H
90 #  include <rpcsvc/ypclnt.h>
91 # endif
92
93 # include "yppasswd.h"
94
95 # if !HAVE_DECL_GETRPCPORT &&!HAVE_RPCB_GETADDR
96 extern int getrpcport(const char *host, unsigned long prognum,
97                       unsigned long versnum, unsigned int proto);
98 # endif                         /* GNU libc 2.1 */
99 #endif
100
101 /*
102    How it works:
103    Gets in username (has to be done) from the calling program
104    Does authentication of user (only if we are not running as root)
105    Gets new password/checks for sanity
106    Sets it.
107  */
108
109 /* data tokens */
110
111 #define _UNIX_OLD_AUTHTOK       "-UN*X-OLD-PASS"
112 #define _UNIX_NEW_AUTHTOK       "-UN*X-NEW-PASS"
113
114 #define MAX_PASSWD_TRIES        3
115
116 #ifdef HAVE_NIS
117 #ifdef HAVE_RPCB_GETADDR
118 static unsigned short
119 __taddr2port (const struct netconfig *nconf, const struct netbuf *nbuf)
120 {
121   unsigned short port = 0;
122   struct __rpc_sockinfo si;
123   struct sockaddr_in *sin;
124   struct sockaddr_in6 *sin6;
125   if (!__rpc_nconf2sockinfo(nconf, &si))
126     return 0;
127
128   switch (si.si_af)
129     {
130     case AF_INET:
131       sin = nbuf->buf;
132       port = sin->sin_port;
133       break;
134     case AF_INET6:
135       sin6 = nbuf->buf;
136       port = sin6->sin6_port;
137       break;
138     default:
139       break;
140     }
141
142   return htons (port);
143 }
144 #endif
145
146 static char *getNISserver(pam_handle_t *pamh, unsigned int ctrl)
147 {
148         char *master;
149         char *domainname;
150         int port, err;
151 #if defined(HAVE_RPCB_GETADDR)
152         struct netconfig *nconf;
153         struct netbuf svcaddr;
154         char addrbuf[INET6_ADDRSTRLEN];
155         void *handle;
156         int found;
157 #endif
158
159
160 #ifdef HAVE_YP_GET_DEFAULT_DOMAIN
161         if ((err = yp_get_default_domain(&domainname)) != 0) {
162                 pam_syslog(pamh, LOG_WARNING, "can't get local yp domain: %s",
163                          yperr_string(err));
164                 return NULL;
165         }
166 #elif defined(HAVE_GETDOMAINNAME)
167         char domainname_res[256];
168
169         if (getdomainname (domainname_res, sizeof (domainname_res)) == 0)
170           {
171             if (strcmp (domainname_res, "(none)") == 0)
172               {
173                 /* If domainname is not set, some systems will return "(none)" */
174                 domainname_res[0] = '\0';
175               }
176             domainname = domainname_res;
177           }
178         else domainname = NULL;
179 #endif
180
181         if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
182                 pam_syslog(pamh, LOG_WARNING, "can't find the master ypserver: %s",
183                          yperr_string(err));
184                 return NULL;
185         }
186 #ifdef HAVE_RPCB_GETADDR
187         svcaddr.len = 0;
188         svcaddr.maxlen = sizeof (addrbuf);
189         svcaddr.buf = addrbuf;
190         port = 0;
191         found = 0;
192
193         handle = setnetconfig();
194         while ((nconf = getnetconfig(handle)) != NULL) {
195           if (!strcmp(nconf->nc_proto, "udp")) {
196             if (rpcb_getaddr(YPPASSWDPROG, YPPASSWDPROC_UPDATE,
197                              nconf, &svcaddr, master)) {
198               port = __taddr2port (nconf, &svcaddr);
199               endnetconfig (handle);
200               found=1;
201               break;
202             }
203
204             if (rpc_createerr.cf_stat != RPC_UNKNOWNHOST) {
205               clnt_pcreateerror (master);
206               pam_syslog (pamh, LOG_ERR,
207                           "rpcb_getaddr (%s) failed!", master);
208               return NULL;
209             }
210           }
211         }
212
213         if (!found) {
214           pam_syslog (pamh, LOG_ERR,
215                       "Cannot find suitable transport for protocol 'udp'");
216           return NULL;
217         }
218 #else
219         port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
220 #endif
221         if (port == 0) {
222                 pam_syslog(pamh, LOG_WARNING,
223                          "yppasswdd not running on NIS master host");
224                 return NULL;
225         }
226         if (port >= IPPORT_RESERVED) {
227                 pam_syslog(pamh, LOG_WARNING,
228                          "yppasswd daemon running on illegal port");
229                 return NULL;
230         }
231         if (on(UNIX_DEBUG, ctrl)) {
232           pam_syslog(pamh, LOG_DEBUG, "Use NIS server on %s with port %d",
233                      master, port);
234         }
235         return master;
236 }
237 #endif
238
239 #ifdef WITH_SELINUX
240
241 static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user,
242     const char *fromwhat, const char *towhat, int remember)
243 {
244     int retval, child, fds[2];
245     struct sigaction newsa, oldsa;
246
247     D(("called."));
248     /* create a pipe for the password */
249     if (pipe(fds) != 0) {
250         D(("could not make pipe"));
251         return PAM_AUTH_ERR;
252     }
253
254     if (off(UNIX_NOREAP, ctrl)) {
255         /*
256          * This code arranges that the demise of the child does not cause
257          * the application to receive a signal it is not expecting - which
258          * may kill the application or worse.
259          *
260          * The "noreap" module argument is provided so that the admin can
261          * override this behavior.
262          */
263         memset(&newsa, '\0', sizeof(newsa));
264         newsa.sa_handler = SIG_DFL;
265         sigaction(SIGCHLD, &newsa, &oldsa);
266     }
267
268     /* fork */
269     child = fork();
270     if (child == 0) {
271         static char *envp[] = { NULL };
272         const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
273         char buffer[16];
274
275         /* XXX - should really tidy up PAM here too */
276
277         /* reopen stdin as pipe */
278         if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) {
279                 pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin");
280                 _exit(PAM_AUTHINFO_UNAVAIL);
281         }
282
283         if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD,
284                                             PAM_MODUTIL_PIPE_FD,
285                                             PAM_MODUTIL_PIPE_FD) < 0) {
286                 _exit(PAM_AUTHINFO_UNAVAIL);
287         }
288
289         /* exec binary helper */
290         args[0] = UPDATE_HELPER;
291         args[1] = user;
292         args[2] = "update";
293         if (on(UNIX_SHADOW, ctrl))
294                 args[3] = "1";
295         else
296                 args[3] = "0";
297
298         snprintf(buffer, sizeof(buffer), "%d", remember);
299         args[4] = buffer;
300
301         execve(UPDATE_HELPER, (char *const *) args, envp);
302
303         /* should not get here: exit with error */
304         D(("helper binary is not available"));
305         _exit(PAM_AUTHINFO_UNAVAIL);
306     } else if (child > 0) {
307         /* wait for child */
308         /* if the stored password is NULL */
309         int rc=0;
310         if (fromwhat) {
311             int len = strlen(fromwhat);
312
313             if (len > PAM_MAX_RESP_SIZE)
314               len = PAM_MAX_RESP_SIZE;
315             pam_modutil_write(fds[1], fromwhat, len);
316         }
317         pam_modutil_write(fds[1], "", 1);
318         if (towhat) {
319             int len = strlen(towhat);
320
321             if (len > PAM_MAX_RESP_SIZE)
322               len = PAM_MAX_RESP_SIZE;
323             pam_modutil_write(fds[1], towhat, len);
324         }
325         pam_modutil_write(fds[1], "", 1);
326
327         close(fds[0]);       /* close here to avoid possible SIGPIPE above */
328         close(fds[1]);
329         /* wait for helper to complete: */
330         while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
331         if (rc<0) {
332           pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
333           retval = PAM_AUTHTOK_ERR;
334         } else if (!WIFEXITED(retval)) {
335           pam_syslog(pamh, LOG_ERR, "unix_update abnormal exit: %d", retval);
336           retval = PAM_AUTHTOK_ERR;
337         } else {
338           retval = WEXITSTATUS(retval);
339         }
340     } else {
341         D(("fork failed"));
342         close(fds[0]);
343         close(fds[1]);
344         retval = PAM_AUTH_ERR;
345     }
346
347     if (off(UNIX_NOREAP, ctrl)) {
348         sigaction(SIGCHLD, &oldsa, NULL);   /* restore old signal handler */
349     }
350
351     return retval;
352 }
353 #endif
354
355 static int check_old_password(const char *forwho, const char *newpass)
356 {
357         static char buf[16384];
358         char *s_luser, *s_uid, *s_npas, *s_pas;
359         int retval = PAM_SUCCESS;
360         FILE *opwfile;
361         size_t len = strlen(forwho);
362
363         opwfile = fopen(OLD_PASSWORDS_FILE, "r");
364         if (opwfile == NULL)
365                 return PAM_ABORT;
366
367         while (fgets(buf, 16380, opwfile)) {
368                 if (!strncmp(buf, forwho, len) && (buf[len] == ':' ||
369                         buf[len] == ',')) {
370                         char *sptr;
371                         buf[strlen(buf) - 1] = '\0';
372                         s_luser = strtok_r(buf, ":,", &sptr);
373                         s_uid = strtok_r(NULL, ":,", &sptr);
374                         s_npas = strtok_r(NULL, ":,", &sptr);
375                         s_pas = strtok_r(NULL, ":,", &sptr);
376                         while (s_pas != NULL) {
377                                 char *md5pass = Goodcrypt_md5(newpass, s_pas);
378                                 if (md5pass == NULL || !strcmp(md5pass, s_pas)) {
379                                         _pam_delete(md5pass);
380                                         retval = PAM_AUTHTOK_ERR;
381                                         break;
382                                 }
383                                 s_pas = strtok_r(NULL, ":,", &sptr);
384                                 _pam_delete(md5pass);
385                         }
386                         break;
387                 }
388         }
389         fclose(opwfile);
390
391         return retval;
392 }
393
394 static int _do_setpass(pam_handle_t* pamh, const char *forwho,
395                        const char *fromwhat,
396                        char *towhat, unsigned int ctrl, int remember)
397 {
398         struct passwd *pwd = NULL;
399         int retval = 0;
400         int unlocked = 0;
401         char *master = NULL;
402
403         D(("called"));
404
405         pwd = getpwnam(forwho);
406
407         if (pwd == NULL) {
408                 retval = PAM_AUTHTOK_ERR;
409                 goto done;
410         }
411
412         if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) {
413 #ifdef HAVE_NIS
414           if ((master=getNISserver(pamh, ctrl)) != NULL) {
415                 struct timeval timeout;
416                 struct yppasswd yppwd;
417                 CLIENT *clnt;
418                 int status;
419                 enum clnt_stat err;
420
421                 /* Unlock passwd file to avoid deadlock */
422                 unlock_pwdf();
423                 unlocked = 1;
424
425                 /* Initialize password information */
426                 yppwd.newpw.pw_passwd = pwd->pw_passwd;
427                 yppwd.newpw.pw_name = pwd->pw_name;
428                 yppwd.newpw.pw_uid = pwd->pw_uid;
429                 yppwd.newpw.pw_gid = pwd->pw_gid;
430                 yppwd.newpw.pw_gecos = pwd->pw_gecos;
431                 yppwd.newpw.pw_dir = pwd->pw_dir;
432                 yppwd.newpw.pw_shell = pwd->pw_shell;
433                 yppwd.oldpass = fromwhat ? strdup (fromwhat) : strdup ("");
434                 yppwd.newpw.pw_passwd = towhat;
435
436                 D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
437
438                 /* The yppasswd.x file said `unix authentication required',
439                  * so I added it. This is the only reason it is in here.
440                  * My yppasswdd doesn't use it, but maybe some others out there
441                  * do.                                        --okir
442                  */
443                 clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
444                 clnt->cl_auth = authunix_create_default();
445                 memset((char *) &status, '\0', sizeof(status));
446                 timeout.tv_sec = 25;
447                 timeout.tv_usec = 0;
448                 err = clnt_call(clnt, YPPASSWDPROC_UPDATE,
449                                 (xdrproc_t) xdr_yppasswd, (char *) &yppwd,
450                                 (xdrproc_t) xdr_int, (char *) &status,
451                                 timeout);
452
453                 free (yppwd.oldpass);
454
455                 if (err) {
456                         _make_remark(pamh, ctrl, PAM_TEXT_INFO,
457                                 clnt_sperrno(err));
458                 } else if (status) {
459                         D(("Error while changing NIS password.\n"));
460                 }
461                 D(("The password has%s been changed on %s.",
462                    (err || status) ? " not" : "", master));
463                 pam_syslog(pamh, LOG_NOTICE, "password%s changed for %s on %s",
464                          (err || status) ? " not" : "", pwd->pw_name, master);
465
466                 auth_destroy(clnt->cl_auth);
467                 clnt_destroy(clnt);
468                 if (err || status) {
469                         _make_remark(pamh, ctrl, PAM_TEXT_INFO,
470                                 _("NIS password could not be changed."));
471                         retval = PAM_TRY_AGAIN;
472                 }
473 #ifdef PAM_DEBUG
474                 sleep(5);
475 #endif
476             } else {
477                     retval = PAM_TRY_AGAIN;
478             }
479 #else
480           if (on(UNIX_DEBUG, ctrl)) {
481             pam_syslog(pamh, LOG_DEBUG, "No NIS support available");
482           }
483
484           retval = PAM_TRY_AGAIN;
485 #endif
486         }
487
488         if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
489                 if(unlocked) {
490                         if (lock_pwdf() != PAM_SUCCESS) {
491                                 return PAM_AUTHTOK_LOCK_BUSY;
492                         }
493                 }
494 #ifdef WITH_SELINUX
495                 if (unix_selinux_confined())
496                           return _unix_run_update_binary(pamh, ctrl, forwho, fromwhat, towhat, remember);
497 #endif
498                 /* first, save old password */
499                 if (save_old_password(pamh, forwho, fromwhat, remember)) {
500                         retval = PAM_AUTHTOK_ERR;
501                         goto done;
502                 }
503                 if (on(UNIX_SHADOW, ctrl) || is_pwd_shadowed(pwd)) {
504                         retval = unix_update_shadow(pamh, forwho, towhat);
505                         if (retval == PAM_SUCCESS)
506                                 if (!is_pwd_shadowed(pwd))
507                                         retval = unix_update_passwd(pamh, forwho, "x");
508                 } else {
509                         retval = unix_update_passwd(pamh, forwho, towhat);
510                 }
511         }
512
513
514 done:
515         unlock_pwdf();
516
517         return retval;
518 }
519
520 static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned int ctrl)
521 {
522         struct passwd *pwent = NULL;    /* Password and shadow password */
523         struct spwd *spent = NULL;      /* file entries for the user */
524         int daysleft;
525         int retval;
526
527         retval = get_account_info(pamh, user, &pwent, &spent);
528         if (retval == PAM_USER_UNKNOWN) {
529                 return retval;
530         }
531
532         if (retval == PAM_SUCCESS && spent == NULL)
533                 return PAM_SUCCESS;
534
535         if (retval == PAM_UNIX_RUN_HELPER) {
536                 retval = _unix_run_verify_binary(pamh, ctrl, user, &daysleft);
537                 if (retval == PAM_AUTH_ERR || retval == PAM_USER_UNKNOWN)
538                         return retval;
539         }
540         else if (retval == PAM_SUCCESS)
541                 retval = check_shadow_expiry(pamh, spent, &daysleft);
542
543         if (on(UNIX__IAMROOT, ctrl) || retval == PAM_NEW_AUTHTOK_REQD)
544                 return PAM_SUCCESS;
545
546         return retval;
547 }
548
549 static int _pam_unix_approve_pass(pam_handle_t * pamh
550                                   ,unsigned int ctrl
551                                   ,const char *pass_old
552                                   ,const char *pass_new,
553                                   int pass_min_len)
554 {
555         const void *user;
556         const char *remark = NULL;
557         int retval = PAM_SUCCESS;
558
559         D(("&new=%p, &old=%p", pass_old, pass_new));
560         D(("new=[%s]", pass_new));
561         D(("old=[%s]", pass_old));
562
563         if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
564                 if (on(UNIX_DEBUG, ctrl)) {
565                         pam_syslog(pamh, LOG_DEBUG, "bad authentication token");
566                 }
567                 _make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
568                         _("No password supplied") : _("Password unchanged"));
569                 return PAM_AUTHTOK_ERR;
570         }
571         /*
572          * if one wanted to hardwire authentication token strength
573          * checking this would be the place - AGM
574          */
575
576         retval = pam_get_item(pamh, PAM_USER, &user);
577         if (retval != PAM_SUCCESS) {
578                 if (on(UNIX_DEBUG, ctrl)) {
579                         pam_syslog(pamh, LOG_ERR, "Can not get username");
580                         return PAM_AUTHTOK_ERR;
581                 }
582         }
583         if (off(UNIX__IAMROOT, ctrl)) {
584                 if (strlen(pass_new) < pass_min_len)
585                   remark = _("You must choose a longer password");
586                 D(("length check [%s]", remark));
587                 if (on(UNIX_REMEMBER_PASSWD, ctrl)) {
588                         if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR)
589                           remark = _("Password has been already used. Choose another.");
590                         if (retval == PAM_ABORT) {
591                                 pam_syslog(pamh, LOG_ERR, "can't open %s file to check old passwords",
592                                         OLD_PASSWORDS_FILE);
593                                 return retval;
594                         }
595                 }
596         }
597         if (remark) {
598                 _make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
599                 retval = PAM_AUTHTOK_ERR;
600         }
601         return retval;
602 }
603
604 int
605 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
606 {
607         unsigned int ctrl, lctrl;
608         int retval;
609         int remember = -1;
610         int rounds = -1;
611         int pass_min_len = 0;
612
613         /* <DO NOT free() THESE> */
614         const char *user;
615         const void *item;
616         const char *pass_old, *pass_new;
617         /* </DO NOT free() THESE> */
618
619         D(("called."));
620
621         ctrl = _set_ctrl(pamh, flags, &remember, &rounds, &pass_min_len,
622                          argc, argv);
623
624         /*
625          * First get the name of a user
626          */
627         retval = pam_get_user(pamh, &user, NULL);
628         if (retval == PAM_SUCCESS) {
629                 /*
630                  * Various libraries at various times have had bugs related to
631                  * '+' or '-' as the first character of a user name. Don't
632                  * allow them.
633                  */
634                 if (user == NULL || user[0] == '-' || user[0] == '+') {
635                         pam_syslog(pamh, LOG_ERR, "bad username [%s]", user);
636                         return PAM_USER_UNKNOWN;
637                 }
638                 if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
639                         pam_syslog(pamh, LOG_DEBUG, "username [%s] obtained",
640                                  user);
641         } else {
642                 if (on(UNIX_DEBUG, ctrl))
643                         pam_syslog(pamh, LOG_DEBUG,
644                                  "password - could not identify user");
645                 return retval;
646         }
647
648         D(("Got username of %s", user));
649
650         /*
651          * Before we do anything else, check to make sure that the user's
652          * info is in one of the databases we can modify from this module,
653          * which currently is 'files' and 'nis'.  We have to do this because
654          * getpwnam() doesn't tell you *where* the information it gives you
655          * came from, nor should it.  That's our job.
656          */
657         if (_unix_comesfromsource(pamh, user, 1, on(UNIX_NIS, ctrl)) == 0) {
658                 pam_syslog(pamh, LOG_DEBUG,
659                          "user \"%s\" does not exist in /etc/passwd%s",
660                          user, on(UNIX_NIS, ctrl) ? " or NIS" : "");
661                 return PAM_USER_UNKNOWN;
662         } else {
663                 struct passwd *pwd;
664                 _unix_getpwnam(pamh, user, 1, 1, &pwd);
665                 if (pwd == NULL) {
666                         pam_syslog(pamh, LOG_DEBUG,
667                                 "user \"%s\" has corrupted passwd entry",
668                                 user);
669                         return PAM_USER_UNKNOWN;
670                 }
671         }
672
673         /*
674          * This is not an AUTH module!
675          */
676         if (on(UNIX__NONULL, ctrl))
677                 set(UNIX__NULLOK, ctrl);
678
679         if (on(UNIX__PRELIM, ctrl)) {
680                 /*
681                  * obtain and verify the current password (OLDAUTHTOK) for
682                  * the user.
683                  */
684                 D(("prelim check"));
685
686                 if (_unix_blankpasswd(pamh, ctrl, user)) {
687                         return PAM_SUCCESS;
688                 } else if (off(UNIX__IAMROOT, ctrl) ||
689                            (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, user, 0, 1))) {
690                         /* instruct user what is happening */
691                         if (off(UNIX__QUIET, ctrl)) {
692                                 retval = pam_info(pamh, _("Changing password for %s."), user);
693                                 if (retval != PAM_SUCCESS)
694                                         return retval;
695                         }
696                         retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &pass_old, NULL);
697
698                         if (retval != PAM_SUCCESS) {
699                                 pam_syslog(pamh, LOG_NOTICE,
700                                     "password - (old) token not obtained");
701                                 return retval;
702                         }
703                         /* verify that this is the password for this user */
704
705                         retval = _unix_verify_password(pamh, user, pass_old, ctrl);
706                 } else {
707                         D(("process run by root so do nothing this time around"));
708                         pass_old = NULL;
709                         retval = PAM_SUCCESS;   /* root doesn't have too */
710                 }
711
712                 if (retval != PAM_SUCCESS) {
713                         D(("Authentication failed"));
714                         pass_old = NULL;
715                         return retval;
716                 }
717                 pass_old = NULL;
718                 retval = _unix_verify_shadow(pamh,user, ctrl);
719                 if (retval == PAM_AUTHTOK_ERR) {
720                         if (off(UNIX__IAMROOT, ctrl))
721                                 _make_remark(pamh, ctrl, PAM_ERROR_MSG,
722                                              _("You must wait longer to change your password"));
723                         else
724                                 retval = PAM_SUCCESS;
725                 }
726         } else if (on(UNIX__UPDATE, ctrl)) {
727                 /*
728                  * tpass is used below to store the _pam_md() return; it
729                  * should be _pam_delete()'d.
730                  */
731
732                 char *tpass = NULL;
733                 int retry = 0;
734
735                 /*
736                  * obtain the proposed password
737                  */
738
739                 D(("do update"));
740
741                 /*
742                  * get the old token back. NULL was ok only if root [at this
743                  * point we assume that this has already been enforced on a
744                  * previous call to this function].
745                  */
746
747                 retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &item);
748
749                 if (retval != PAM_SUCCESS) {
750                         pam_syslog(pamh, LOG_NOTICE, "user not authenticated");
751                         return retval;
752                 }
753                 pass_old = item;
754                 D(("pass_old [%s]", pass_old));
755
756                 D(("get new password now"));
757
758                 lctrl = ctrl;
759
760                 if (on(UNIX_USE_AUTHTOK, lctrl)) {
761                         set(UNIX_USE_FIRST_PASS, lctrl);
762                 }
763                 if (on(UNIX_USE_FIRST_PASS, lctrl)) {
764                         retry = MAX_PASSWD_TRIES-1;
765                 }
766                 retval = PAM_AUTHTOK_ERR;
767                 while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
768                         /*
769                          * use_authtok is to force the use of a previously entered
770                          * password -- needed for pluggable password strength checking
771                          */
772
773                         retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass_new, NULL);
774
775                         if (retval != PAM_SUCCESS) {
776                                 if (on(UNIX_DEBUG, ctrl)) {
777                                         pam_syslog(pamh, LOG_ERR,
778                                                  "password - new password not obtained");
779                                 }
780                                 pass_old = NULL;        /* tidy up */
781                                 return retval;
782                         }
783                         D(("returned to _unix_chauthtok"));
784
785                         /*
786                          * At this point we know who the user is and what they
787                          * propose as their new password. Verify that the new
788                          * password is acceptable.
789                          */
790
791                         if (*(const char *)pass_new == '\0') {  /* "\0" password = NULL */
792                                 pass_new = NULL;
793                         }
794                         retval = _pam_unix_approve_pass(pamh, ctrl, pass_old,
795                                                         pass_new, pass_min_len);
796
797                         if (retval != PAM_SUCCESS) {
798                                 pam_set_item(pamh, PAM_AUTHTOK, NULL);
799                         }
800                 }
801
802                 if (retval != PAM_SUCCESS) {
803                         pam_syslog(pamh, LOG_NOTICE,
804                                  "new password not acceptable");
805                         pass_new = pass_old = NULL;     /* tidy up */
806                         return retval;
807                 }
808                 if (lock_pwdf() != PAM_SUCCESS) {
809                         return PAM_AUTHTOK_LOCK_BUSY;
810                 }
811
812                 if (pass_old) {
813                         retval = _unix_verify_password(pamh, user, pass_old, ctrl);
814                         if (retval != PAM_SUCCESS) {
815                                 pam_syslog(pamh, LOG_NOTICE, "user password changed by another process");
816                                 unlock_pwdf();
817                                 return retval;
818                         }
819                 }
820
821                 retval = _unix_verify_shadow(pamh, user, ctrl);
822                 if (retval != PAM_SUCCESS) {
823                         pam_syslog(pamh, LOG_NOTICE, "user shadow entry expired");
824                         unlock_pwdf();
825                         return retval;
826                 }
827
828                 retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new,
829                                                 pass_min_len);
830                 if (retval != PAM_SUCCESS) {
831                         pam_syslog(pamh, LOG_NOTICE,
832                                  "new password not acceptable 2");
833                         pass_new = pass_old = NULL;     /* tidy up */
834                         unlock_pwdf();
835                         return retval;
836                 }
837
838                 /*
839                  * By reaching here we have approved the passwords and must now
840                  * rebuild the password database file.
841                  */
842
843                 /*
844                  * First we encrypt the new password.
845                  */
846
847                 tpass = create_password_hash(pamh, pass_new, ctrl, rounds);
848                 if (tpass == NULL) {
849                         pam_syslog(pamh, LOG_CRIT,
850                                 "crypt() failure or out of memory for password");
851                         pass_new = pass_old = NULL;     /* tidy up */
852                         unlock_pwdf();
853                         return PAM_BUF_ERR;
854                 }
855
856                 D(("password processed"));
857
858                 /* update the password database(s) -- race conditions..? */
859
860                 retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
861                                      remember);
862                 /* _do_setpass has called unlock_pwdf for us */
863
864                 _pam_delete(tpass);
865                 pass_old = pass_new = NULL;
866         } else {                /* something has broken with the module */
867                 pam_syslog(pamh, LOG_CRIT,
868                          "password received unknown request");
869                 retval = PAM_ABORT;
870         }
871
872         D(("retval was %d", retval));
873
874         return retval;
875 }