2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: unix_update_authtok_file.c /main/5 1996/05/09 04:35:55 drk $ */
25 * Copyright (c) 1992-1995, by Sun Microsystems, Inc.
26 * All rights reserved.
29 #ident "@(#)unix_update_authtok_file.c 1.26 95/09/11 SMI"
31 #include "unix_headers.h"
33 static int update_spent(pam_handle_t *, char *, char **,
34 struct passwd *, struct spwd *, int *, int, int);
35 static int process_passwd(pam_handle_t *, char *, char *,
36 struct passwd *, int);
39 * update_authtok_file():
40 * To update the authentication token file.
42 * This function is called by either __set_authtoken_attr() to
43 * update the token attributes or pam_chauthtok() to update the
44 * authentication token. The parameter "field" has to be specified
45 * as "attr" if the caller wants to update token attributes, and
46 * the attribute-value pairs to be set needs to be passed in by parameter
47 * "data". If the function is called to update authentication
48 * token itself, then "field" needs to be specified as "passwd"
49 * and the new authentication token has to be passed in by "data".
53 update_authtok_file(pamh, field, data, unix_pwd, privileged, nowarn)
57 struct passwd *unix_pwd;
64 register int found = 0;
68 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
69 int passwd_flag = 0; /* attrs in shadow or passwd file */
72 if ((retcode = pam_get_item(pamh, PAM_SERVICE, (void **)&prognamep))
74 (retcode = pam_get_item(pamh, PAM_USER, (void **)&usrname))
80 * Assume effective UID already set to 0
81 * so we can update the passwd file.
83 * This will be the last update (after nis/nis+)
88 /* lock the password file */
91 sprintf(messages[0], PAM_MSG(pamh, 90,
92 "%s%s: Password database busy. Try again later."),
94 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
97 return (PAM_AUTHTOK_LOCK_BUSY);
100 /* Mode of the shadow file should be 400 or 000 */
101 if (stat(SHADOW, &buf) < 0) {
102 syslog(LOG_ERR, "%s: stat of shadow file failed",
105 return (PAM_AUTHTOK_ERR);
108 (void) umask(S_IAMB & ~(buf.st_mode & S_IRUSR));
109 if ((tsfp = fopen(SHADTEMP, "w")) == NULL) {
111 sprintf(messages[0], PAM_MSG(pamh, 91,
112 "%s%s: Unexpected failure. Password database unchanged."),
113 prognamep, UNIX_MSG);
114 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
118 return (PAM_AUTHTOK_ERR);
122 * copy passwd files to temps, replacing matching lines
123 * with new password attributes.
125 if ((spfp = fopen(SHADOW, "r")) == NULL) {
129 while (fgetspent_r(spfp, &unix_sp, spbuf, sizeof (spbuf)) != NULL) {
130 if (strcmp(unix_sp.sp_namp, usrname) == 0) {
132 retcode = update_spent(pamh, field, data,
133 unix_pwd, &unix_sp, &passwd_flag,
135 if (retcode != PAM_SUCCESS) {
141 /* The attributes are in passwd file */
142 if ((retcode = process_passwd(pamh,
143 prognamep, usrname, unix_pwd,
144 nowarn)) != PAM_SUCCESS) {
151 if (putspent(&unix_sp, tsfp) != 0) {
153 sprintf(messages[0], PAM_MSG(pamh, 91,
154 "%s%s: Unexpected failure. Password database unchanged."),
155 prognamep, UNIX_MSG);
156 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
163 memset(spbuf, 0, sizeof (spbuf));
166 if ((fclose(tsfp)) || (fclose(spfp))) {
168 sprintf(messages[0], PAM_MSG(pamh, 91,
169 "%s%s: Unexpected failure. Password database unchanged."),
170 prognamep, UNIX_MSG);
171 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
177 /* Check if user name exists */
180 sprintf(messages[0], PAM_MSG(pamh, 91,
181 "%s%s: Unexpected failure. Password database unchanged."),
182 prognamep, UNIX_MSG);
183 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
190 * Rename temp file back to appropriate passwd file.
193 /* remove old shadow file */
194 if (unlink(OSHADOW) && access(OSHADOW, 0) == 0) {
196 sprintf(messages[0], PAM_MSG(pamh, 91,
197 "%s%s: Unexpected failure. Password database unchanged."),
198 prognamep, UNIX_MSG);
199 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
205 /* rename shadow file to old shadow file */
206 if (rename(SHADOW, OSHADOW) == -1) {
208 sprintf(messages[0], PAM_MSG(pamh, 91,
209 "%s%s: Unexpected failure. Password database unchanged."),
210 prognamep, UNIX_MSG);
211 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
217 /* rename temparory shadow file to shadow file */
218 if (rename(SHADTEMP, SHADOW) == -1) {
219 (void) unlink(SHADOW);
220 if (link(OSHADOW, SHADOW)) {
222 sprintf(messages[0], PAM_MSG(pamh, 92,
223 "%s%s: Unexpected failure. Password database missing."),
224 prognamep, UNIX_MSG);
225 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
231 sprintf(messages[0], PAM_MSG(pamh, 91,
232 "%s%s: Unexpected failure. Password database unchanged."),
233 prognamep, UNIX_MSG);
234 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
241 memset(spbuf, 0, sizeof (spbuf));
242 if (strcmp(field, "passwd") == 0) {
243 sprintf(messages[0], PAM_MSG(pamh, 93,
244 "%s%s: passwd successfully changed for %s"),
245 prognamep, UNIX_MSG, usrname);
246 (void) __pam_display_msg(pamh, PAM_TEXT_INFO,
249 return (PAM_SUCCESS);
253 memset(spbuf, 0, sizeof (spbuf));
254 return (PAM_AUTHTOK_ERR);
260 * To update a shadow password file entry in the Unix
261 * authentication token file.
262 * This function is called by update_authtok_file() to
263 * update the token to token attributes.
264 * The parameter "field" indicates whenther token attributes or
265 * token itself will be changes, and the parameter "data" has
266 * the new values for the attributes or token.
271 update_spent(pamh, field, data, unix_pwd, unix_sp,
272 passwd_flag, privileged, nowarn)
276 struct passwd *unix_pwd;
277 struct spwd *unix_sp;
285 char **data_p = data;
289 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
290 static char *lkstring = "*LK*"; /* lock string to lock */
291 /* user's password */
293 if (strcmp(field, "attr") == 0) {
294 while (*data != NULL) {
295 /* check attribute: AUTHTOK_DEL */
297 attr_match("AUTHTOK_DEL", *data))
299 if (strcmp(value, "1") == 0) {
301 /* delete password */
302 if (unix_sp->sp_pwdp)
303 memset(unix_sp->sp_pwdp, 0,
304 strlen(unix_sp->sp_pwdp));
307 * set "AUTHTOK_EXT" will clear
308 * the sp_lstchg field. We do not
309 * want sp_lstchg field to be set
310 * if one execute passwd -d -f
311 * name or passwd -l -f name.
313 if (attr_find("AUTHTOK_EXP",
315 unix_sp->sp_lstchg = DAY_NOW;
321 /* check attribute: AUTHTOK_LK */
322 if ((value = attr_match("AUTHTOK_LK", *data))
324 if (strcmp(value, "1") == 0) {
325 memset(unix_sp->sp_pwdp, 0,
326 strlen(unix_sp->sp_pwdp));
328 unix_sp->sp_pwdp = lkstring;
329 if (attr_find("AUTHTOK_EXP",
331 unix_sp->sp_lstchg = DAY_NOW;
337 /* check attribute: AUTHTOK_EXP */
338 if ((value = attr_match("AUTHTOK_EXP", *data))
340 if (strcmp(value, "1") == 0) {
341 /* expire password */
342 unix_sp->sp_lstchg = (long) 0;
348 /* check attribute: AUTHTOK_MAXAGE */
349 if ((value = attr_match("AUTHTOK_MAXAGE", *data))
352 maxdate = (int)strtol(value, &char_p, 10);
353 if ((attr_find("AUTHTOK_MINAGE", data_p) ==
354 0) && unix_sp->sp_min == -1)
356 if (maxdate == -1) { /* turn off aging */
357 unix_sp->sp_min = -1;
358 unix_sp->sp_warn = -1;
359 } else if (unix_sp->sp_max == -1)
361 * It was set to 0 before. That
362 * will force passwd change at the
363 * next login. There are several
364 * ways to force passwd change. I don't
365 * think turning on aging should imply
368 unix_sp->sp_lstchg = DAY_NOW;
370 unix_sp->sp_max = maxdate;
375 /* check attribute: AUTHTOK_MINAGE */
376 if ((value = attr_match("AUTHTOK_MINAGE", *data))
379 mindate = (int)strtol(value, &char_p, 10);
380 if ((attr_find("AUTHTOK_MAXAGE", data_p) ==
382 unix_sp->sp_max == -1 && mindate != -1) {
383 return (PAM_AUTHTOK_DISABLE_AGING);
385 unix_sp->sp_min = mindate;
390 /* check attribute: AUTHTOK_WARNDATE */
393 ("AUTHTOK_WARNDATE", *data)) != NULL) {
395 warndate = (int)strtol(value, &char_p, 10);
396 if (unix_sp->sp_max == -1 && warndate != -1) {
397 return (PAM_AUTHTOK_DISABLE_AGING);
399 unix_sp->sp_warn = warndate;
405 if ((value = attr_match("AUTHTOK_SHELL", *data))
407 if (unix_pwd == NULL) {
411 "%s: No local passwd record"),
413 (void) __pam_display_msg(
417 return (PAM_AUTHTOK_RECOVERY_ERR);
419 tmp_pwd_entry = unix_pwd->pw_shell;
421 getloginshell(pamh, unix_pwd->pw_shell,
425 /* if NULL, shell unchanged */
426 if (unix_pwd->pw_shell == NULL)
427 return (PAM_SUCCESS);
434 if ((value = attr_match("AUTHTOK_HOMEDIR", *data))
436 if (unix_pwd == NULL) {
440 "%s: No local passwd record"),
442 (void) __pam_display_msg(
443 pamh, PAM_ERROR_MSG, 1,
446 return (PAM_AUTHTOK_RECOVERY_ERR);
448 tmp_pwd_entry = unix_pwd->pw_dir;
450 gethomedir(pamh, unix_pwd->pw_dir,
454 /* if NULL, homedir unchanged */
455 if (unix_pwd->pw_dir == NULL)
456 return (PAM_SUCCESS);
463 if ((value = attr_match("AUTHTOK_GECOS", *data))
465 if (unix_pwd == NULL) {
469 "%s: No local passwd record"),
471 (void) __pam_display_msg(
472 pamh, PAM_ERROR_MSG, 1,
475 return (PAM_AUTHTOK_RECOVERY_ERR);
477 tmp_pwd_entry = unix_pwd->pw_gecos;
479 getfingerinfo(pamh, unix_pwd->pw_gecos,
483 /* if NULL, gecos unchanged */
484 if (unix_pwd->pw_gecos == NULL)
485 return (PAM_SUCCESS);
492 if (strcmp(field, "passwd") == 0) { /* change password */
493 unix_sp->sp_pwdp = *data_p;
495 /* update the last change field */
496 unix_sp->sp_lstchg = DAY_NOW;
497 if (unix_sp->sp_max == 0) { /* turn off aging */
498 unix_sp->sp_max = -1;
499 unix_sp->sp_min = -1;
503 return (PAM_SUCCESS);
507 * shell, homedir and gecos are in passwd file. The update is modeled
511 process_passwd(pamh, prognamep, usrname, unix_pwd, nowarn)
515 struct passwd *unix_pwd;
518 FILE *tpfp; /* tmp passwd file pointer */
519 FILE *pwfp; /* passwd file pointer */
520 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
521 struct passwd *unix_p;
522 struct passwd unix_tmp;
524 char buf[4 * BUFSIZ];
525 struct stat stat_buf;
527 if (stat(PASSWD, &stat_buf) < 0) {
529 sprintf(messages[0], PAM_MSG(pamh, 91,
530 "%s%s: Unexpected failure. Password database unchanged."),
531 prognamep, UNIX_MSG);
532 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
535 return (PAM_AUTHTOK_ERR);
538 (void) umask(S_IAMB & ~(stat_buf.st_mode & S_IRUSR));
539 if ((tpfp = fopen(PASSTEMP, "w")) == NULL) {
541 sprintf(messages[0], PAM_MSG(pamh, 91,
542 "%s%s: Unexpected failure. Password database unchanged."),
543 prognamep, UNIX_MSG);
544 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
547 return (PAM_AUTHTOK_ERR);
550 if ((pwfp = fopen(PASSWD, "r")) == NULL) {
556 while ((unix_p = fgetpwent_r(pwfp, &unix_tmp, buf, 4*BUFSIZ)) != NULL) {
557 if (strcmp(unix_p->pw_name, usrname) == 0) {
559 unix_p->pw_gecos = unix_pwd->pw_gecos;
560 unix_p->pw_dir = unix_pwd->pw_dir;
561 unix_p->pw_shell = unix_pwd->pw_shell;
563 if (putpwent(unix_p, tpfp) != 0) {
565 sprintf(messages[0], PAM_MSG(pamh, 91,
566 "%s%s: Unexpected failure. Password database unchanged."),
567 prognamep, UNIX_MSG);
568 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
575 memset(buf, 0, 4 * BUFSIZ);
579 if ((fclose(tpfp)) || (fclose(pwfp))) {
581 sprintf(messages[0], PAM_MSG(pamh, 91,
582 "%s%s: Unexpected failure. Password database unchanged."),
583 prognamep, UNIX_MSG);
584 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
590 /* Check if user name exists */
593 sprintf(messages[0], PAM_MSG(pamh, 91,
594 "%s%s: Unexpected failure. Password database unchanged."),
595 prognamep, UNIX_MSG);
596 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
603 * Rename temp file back to appropriate passwd file.
606 /* remove old passwd file */
607 if (unlink(OPASSWD) && access(OPASSWD, 0) == 0) {
609 sprintf(messages[0], PAM_MSG(pamh, 91,
611 prognamep, UNIX_MSG);
612 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
618 /* rename password file to old password file */
619 if (rename(PASSWD, OPASSWD) == -1) {
621 sprintf(messages[0], PAM_MSG(pamh, 91,
622 "%s%s: Unexpected failure. Password database unchanged."),
623 prognamep, UNIX_MSG);
624 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
630 /* rename temporary password file to password file */
631 if (rename(PASSTEMP, PASSWD) == -1) {
632 (void) unlink(PASSWD);
633 if (link(OPASSWD, PASSWD)) {
635 sprintf(messages[0], PAM_MSG(pamh, 91,
636 "%s%s: Unexpected failure. Password database unchanged."),
637 prognamep, UNIX_MSG);
638 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
644 sprintf(messages[0], PAM_MSG(pamh, 91,
645 "%s%s: Unexpected failure. Password database unchanged."),
646 prognamep, UNIX_MSG);
647 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
653 (void) chmod(PASSWD, 0644);
654 return (PAM_SUCCESS);
657 (void) unlink(PASSTEMP);
658 return (PAM_AUTHTOK_ERR);