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