4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
27 #include <sys/types.h>
38 #include <nss_dbdefs.h>
42 #include <limits.h> /* LOGNAME_MAX -- max Solaris user name */
44 #include "passwdutil.h"
47 int files_unlock(void);
48 int files_checkhistory(char *user
, char *passwd
, pwu_repository_t
*rep
);
49 int files_getattr(char *name
, attrlist
*item
, pwu_repository_t
*rep
);
50 int files_getpwnam(char *name
, attrlist
*items
, pwu_repository_t
*rep
,
52 int files_update(attrlist
*items
, pwu_repository_t
*rep
, void *buf
);
53 int files_putpwnam(char *name
, char *oldpw
, pwu_repository_t
*rep
, void *buf
);
54 int files_user_to_authenticate(char *name
, pwu_repository_t
*rep
,
55 char **auth_user
, int *privileged
);
57 static int files_update_history(char *name
, struct spwd
*spwd
);
59 void _nss_XbyY_fgets(FILE *, nss_XbyY_args_t
*);
60 int str2passwd(const char *, int, void *, char *, int);
62 static struct passwd
*
63 fgetpwent_r(FILE *f
, struct passwd
*result
, char *buffer
, int buflen
)
67 /* No key to fill in */
68 NSS_XbyY_INIT(&arg
, result
, buffer
, buflen
, str2passwd
);
69 _nss_XbyY_fgets(f
, &arg
);
70 return ((struct passwd
*)NSS_XbyY_FINI(&arg
));
74 * files function pointer table, used by passwdutil_init to initialize
75 * the global Repository-OPerations table "rops"
77 struct repops files_repops
= {
83 files_user_to_authenticate
,
89 * this structure defines the buffer used to keep state between
90 * get/update/put calls
102 * We should use sysconf, but there is no sysconf name for SHADOW
103 * so we use these from nss_dbdefs
105 #define PWD_SCRATCH_SIZE NSS_LINELEN_PASSWD
106 #define SPW_SCRATCH_SIZE NSS_LINELEN_SHADOW
109 * lock functions for files repository
138 return (PWU_SYSTEM_ERROR
);
140 return (PWU_SUCCESS
);
146 * Are we a privileged user with regard to the files repository?
149 files_privileged(void)
151 return (getuid() == 0);
156 * private_getpwnam_r()
158 * A private implementation of getpwnam_r which does *not* fall back to
159 * other services possibly defined in nsswitch.conf
161 * behaves like getpwnam_r().
164 private_getpwnam_r(const char *name
, struct passwd
*result
, char *buffer
,
170 if ((fp
= fopen(PASSWD
, "rF")) == NULL
)
174 while (!found
&& fgetpwent_r(fp
, result
, buffer
, buflen
) != NULL
) {
175 if (strcmp(name
, result
->pw_name
) == 0)
182 (void) memset(buffer
, 0, buflen
);
183 (void) memset(result
, 0, sizeof (*result
));
191 * private_getspnam_r()
193 * A private implementation of getspnam_r which does *not* fall back to
194 * other services possibly defined in nsswitch.conf.
196 * Behaves like getspnam_r(). Since we use fgetspent_t(), all numeric
197 * fields that are undefined in /etc/shadow will be set to -1.
201 private_getspnam_r(const char *name
, struct spwd
*result
, char *buffer
,
207 fp
= fopen(SHADOW
, "rF");
212 while (!found
&& fgetspent_r(fp
, result
, buffer
, buflen
) != NULL
) {
213 if (strcmp(name
, result
->sp_namp
) == 0)
220 (void) memset(buffer
, 0, buflen
);
221 (void) memset(result
, 0, sizeof (*result
));
228 * files_getpwnam(name, items, rep, buf)
233 files_getpwnam(char *name
, attrlist
*items
, pwu_repository_t
*rep
, void **buf
)
237 int err
= PWU_SUCCESS
;
239 *buf
= calloc(1, sizeof (struct pwbuf
));
240 pwbuf
= (struct pwbuf
*)*buf
;
245 * determine which password structure (/etc/passwd or /etc/shadow)
246 * we need for the items we need to update
248 for (p
= items
; p
!= NULL
; p
= p
->next
) {
258 if (pwbuf
->pwd
== NULL
) {
259 pwbuf
->pwd
= malloc(sizeof (struct passwd
));
260 if (pwbuf
->pwd
== NULL
) {
267 case ATTR_PASSWD_SERVER_POLICY
:
275 case ATTR_LOCK_ACCOUNT
:
276 case ATTR_EXPIRE_PASSWORD
:
277 case ATTR_FAILED_LOGINS
:
278 case ATTR_INCR_FAILED_LOGINS
:
279 case ATTR_RST_FAILED_LOGINS
:
280 case ATTR_NOLOGIN_ACCOUNT
:
281 case ATTR_UNLOCK_ACCOUNT
:
282 if (pwbuf
->spwd
== NULL
) {
283 pwbuf
->spwd
= malloc(sizeof (struct spwd
));
284 if (pwbuf
->spwd
== NULL
) {
292 * Some other repository might have different values
293 * so we ignore those.
300 if ((pwbuf
->pwd_scratch
= malloc(PWD_SCRATCH_SIZE
)) == NULL
) {
304 if (private_getpwnam_r(name
, pwbuf
->pwd
, pwbuf
->pwd_scratch
,
305 PWD_SCRATCH_SIZE
) == NULL
) {
312 if ((pwbuf
->spwd_scratch
= malloc(SPW_SCRATCH_SIZE
)) == NULL
) {
316 if (private_getspnam_r(name
, pwbuf
->spwd
, pwbuf
->spwd_scratch
,
317 SPW_SCRATCH_SIZE
) == NULL
) {
323 return (PWU_SUCCESS
);
326 free(pwbuf
->pwd_scratch
);
328 free(pwbuf
->spwd_scratch
);
336 * int files_user_to_authenticate(name, rep, auth_user, privileged)
337 * Determine which user needs to be authenticated. For files, the
338 * possible return values are:
340 * PWU_SUCCESS and (auth_user == NULL || auth_user = user)
346 files_user_to_authenticate(char *user
, pwu_repository_t
*rep
,
347 char **auth_user
, int *privileged
)
351 attrlist attr_tmp
[1] = { { ATTR_UID
, NULL
, NULL
} };
353 /* check to see if target user is present in files */
354 res
= files_getpwnam(user
, &attr_tmp
[0], rep
, (void **)&pwbuf
);
355 if (res
!= PWU_SUCCESS
)
358 if (files_privileged()) {
364 if (getuid() == pwbuf
->pwd
->pw_uid
) {
365 if ((*auth_user
= strdup(user
)) == NULL
) {
376 free(pwbuf
->pwd_scratch
);
378 free(pwbuf
->spwd_scratch
);
385 * Password history file format:
386 * user:crypw1: ... crypwn: such that n <= MAXHISTORY
388 #define HISTORY "/etc/security/passhistory"
389 #define HISTEMP "/etc/security/pwhistemp"
390 #define OHISTORY "/etc/security/opwhistory"
391 #define HISTMODE S_IRUSR /* mode to create history file */
394 * 3*LOGNAME_MAX just in case there are long user names.
395 * Traditionally Solaris LOGNAME_MAX (_POSIX_LOGIN_NAME_MAX) is 13,
396 * but some sites often user more.
397 * If LOGNAME_MAX ever becomes reasonable (128) and actually enforced,
401 #define MAX_LOGNAME (3 * LOGNAME_MAX)
404 * files_checkhistory - check if a user's new password is in the user's
405 * old password history.
409 * passwd = new clear text password.
412 * PWU_SUCCESS, passwd found in user's old password history.
413 * The caller should only be interested and fail if
414 * PWU_SUCCESS is returned.
415 * PWU_NOT_FOUND, passwd not in user's old password history.
416 * PWU_errors, PWU_ errors from other routines.
420 files_checkhistory(char *user
, char *passwd
, pwu_repository_t
*rep
)
425 attr
.type
= ATTR_HISTORY
;
426 attr
.data
.val_s
= NULL
;
429 debug("files_checkhistory(user=%s)", user
);
433 * This depends on the underlying files_getattr implementation
434 * treating user not found in backing store or no history as
439 if ((res
= files_getattr(user
, &attr
, rep
)) == PWU_SUCCESS
) {
443 char *last
= attr
.data
.val_s
;
445 if ((histsize
= def_getint("HISTORY=", DEFHISTORY
)) == 0) {
446 debug("files_checkhistory: no history requested");
451 debug("files_checkhistory: histsize = %d", histsize
);
452 if (histsize
> MAXHISTORY
)
453 histsize
= MAXHISTORY
;
455 debug("line to test\n\t%s", last
);
457 /* compare crypt_passwd to attr.data.val_s strings. */
459 while ((histsize
-- > 0) &&
460 (((s
= strtok_r(NULL
, ":", &last
)) != NULL
) &&
463 crypt_passwd
= crypt(passwd
, s
);
464 debug("files_checkhistory: user_pw=%s, history_pw=%s",
466 if (strcmp(crypt_passwd
, s
) == 0) {
471 debug("files_checkhistory(%s, %s) = %d", user
, crypt_passwd
,
475 free(attr
.data
.val_s
);
481 * files_getattr(name, items, rep)
483 * Get attributes specified in list 'items'
486 files_getattr(char *name
, attrlist
*items
, pwu_repository_t
*rep
)
494 res
= files_getpwnam(name
, items
, rep
, (void **)&pwbuf
);
495 if (res
!= PWU_SUCCESS
)
501 for (w
= items
; res
== PWU_SUCCESS
&& w
!= NULL
; w
= w
->next
) {
504 if ((w
->data
.val_s
= strdup(pw
->pw_name
)) == NULL
)
508 if ((w
->data
.val_s
= strdup(pw
->pw_comment
)) == NULL
)
512 if ((w
->data
.val_s
= strdup(pw
->pw_gecos
)) == NULL
)
516 if ((w
->data
.val_s
= strdup(pw
->pw_dir
)) == NULL
)
520 if ((w
->data
.val_s
= strdup(pw
->pw_shell
)) == NULL
)
524 * Nothing special needs to be done for
528 case ATTR_PASSWD_SERVER_POLICY
:
529 if ((w
->data
.val_s
= strdup(spw
->sp_pwdp
)) == NULL
)
533 if ((w
->data
.val_s
= strdup(pw
->pw_age
)) == NULL
)
537 if ((w
->data
.val_s
= strdup("files")) == NULL
)
542 char buf
[MAX_LOGNAME
+ MAXHISTORY
+
543 (MAXHISTORY
* CRYPT_MAXCIPHERTEXTLEN
)+1];
546 debug("files_getattr: Get password history for %s ",
549 if ((history
= fopen(HISTORY
, "rF")) == NULL
) {
550 debug("files_getattr: %s not found", HISTORY
);
551 res
= PWU_OPEN_FAILED
;
555 while ((s
= fgets(buf
, sizeof (buf
), history
)) !=
565 debug("got history line for %s", s
);
567 if (strcmp(s
, name
) == 0) {
569 if ((items
->data
.val_s
=
570 strdup(s1
+1)) == NULL
)
577 (void) fclose(history
);
583 w
->data
.val_i
= pw
->pw_uid
;
586 w
->data
.val_i
= pw
->pw_gid
;
589 w
->data
.val_i
= spw
->sp_lstchg
;
592 w
->data
.val_i
= spw
->sp_min
;
595 w
->data
.val_i
= spw
->sp_max
;
598 w
->data
.val_i
= spw
->sp_warn
;
601 w
->data
.val_i
= spw
->sp_inact
;
604 w
->data
.val_i
= spw
->sp_expire
;
607 w
->data
.val_i
= spw
->sp_flag
;
609 case ATTR_FAILED_LOGINS
:
610 w
->data
.val_i
= spw
->sp_flag
& FAILCOUNT_MASK
;
619 free(pwbuf
->pwd_scratch
);
621 free(pwbuf
->spwd_scratch
);
630 * see if attribute ATTR_MAX, with value != -1, is present in
631 * attribute-list "list".
633 * returns 1 if present, 0 otherwise.
636 max_present(attrlist
*list
)
639 if (list
->type
== ATTR_MAX
&& list
->data
.val_i
!= -1)
648 * files_update(items, rep, buf)
650 * update the information in buf with the attributes specified in
655 files_update(attrlist
*items
, pwu_repository_t
*rep
, void *buf
)
657 struct pwbuf
*pwbuf
= (struct pwbuf
*)buf
;
661 int aging_needed
= 0;
669 pwbuf
->update_history
= 0;
672 * if sp_max==0 : disable passwd aging after updating the password
674 disable_aging
= (spw
!= NULL
&& spw
->sp_max
== 0);
676 for (p
= items
; p
!= NULL
; p
= p
->next
) {
679 break; /* We are able to handle this, but... */
681 pw
->pw_uid
= (uid_t
)p
->data
.val_i
;
684 pw
->pw_gid
= (gid_t
)p
->data
.val_i
;
687 pw
->pw_age
= p
->data
.val_s
;
690 pw
->pw_comment
= p
->data
.val_s
;
693 pw
->pw_gecos
= p
->data
.val_s
;
696 pw
->pw_dir
= p
->data
.val_s
;
699 pw
->pw_shell
= p
->data
.val_s
;
703 * Nothing special needs to be done for
707 case ATTR_PASSWD_SERVER_POLICY
:
709 * There is a special case only for files: if the
710 * password is to be deleted (-d to passwd),
711 * p->data.val_s will be NULL.
713 if (p
->data
.val_s
== NULL
) {
719 salt
= crypt_gensalt(spw
->sp_pwdp
, pw
);
724 /* algorithm problem? */
725 syslog(LOG_AUTH
| LOG_ALERT
,
726 "passwdutil: crypt_gensalt %m");
727 return (PWU_UPDATE_FAILED
);
729 hash
= crypt(p
->data
.val_s
, salt
);
735 pword
= strdup(hash
);
741 free(pwbuf
->new_sp_pwdp
);
742 pwbuf
->new_sp_pwdp
= pword
;
743 spw
->sp_pwdp
= pword
;
745 pwbuf
->update_history
= 1;
747 spw
->sp_flag
&= ~FAILCOUNT_MASK
; /* reset count */
748 spw
->sp_lstchg
= DAY_NOW_32
;
750 case ATTR_LOCK_ACCOUNT
:
751 if (spw
->sp_pwdp
== NULL
) {
752 spw
->sp_pwdp
= LOCKSTRING
;
753 } else if ((strncmp(spw
->sp_pwdp
, LOCKSTRING
,
754 sizeof (LOCKSTRING
)-1) != 0) &&
755 (strcmp(spw
->sp_pwdp
, NOLOGINSTRING
) != 0)) {
756 len
= sizeof (LOCKSTRING
)-1 +
757 strlen(spw
->sp_pwdp
) + 1;
763 (void) strlcpy(pword
, LOCKSTRING
, len
);
764 (void) strlcat(pword
, spw
->sp_pwdp
, len
);
765 free(pwbuf
->new_sp_pwdp
);
766 pwbuf
->new_sp_pwdp
= pword
;
767 spw
->sp_pwdp
= pword
;
769 spw
->sp_lstchg
= DAY_NOW_32
;
771 case ATTR_UNLOCK_ACCOUNT
:
772 if (spw
->sp_pwdp
!= NULL
&&
773 strncmp(spw
->sp_pwdp
, LOCKSTRING
,
774 sizeof (LOCKSTRING
)-1) == 0) {
775 (void) strcpy(spw
->sp_pwdp
, spw
->sp_pwdp
+
776 sizeof (LOCKSTRING
)-1);
778 spw
->sp_lstchg
= DAY_NOW_32
;
780 case ATTR_NOLOGIN_ACCOUNT
:
781 spw
->sp_pwdp
= NOLOGINSTRING
;
782 if (pwbuf
->new_sp_pwdp
) {
783 free(pwbuf
->new_sp_pwdp
);
784 pwbuf
->new_sp_pwdp
= NULL
;
786 spw
->sp_lstchg
= DAY_NOW_32
;
788 case ATTR_EXPIRE_PASSWORD
:
792 spw
->sp_lstchg
= p
->data
.val_i
;
795 if (spw
->sp_max
== -1 &&
796 p
->data
.val_i
!= -1 && max_present(p
->next
) == 0)
797 return (PWU_AGING_DISABLED
);
798 spw
->sp_min
= p
->data
.val_i
;
802 if (p
->data
.val_i
== -1) {
803 /* Turn aging off -> Reset min and warn too */
810 if (spw
->sp_min
== -1) {
812 * If minage has not been set with
813 * a command-line option, we set it
820 * If aging was turned off, we update lstchg.
822 * We take care not to update lstchg if the
823 * user has no password, otherwise the user
824 * might not be required to provide a password
825 * the next time they log-in.
827 * Also, if lstchg != -1 (i.e., not set in
828 * /etc/shadow), we keep the old value.
830 if (spw
->sp_max
== -1 &&
831 spw
->sp_pwdp
!= NULL
&& *spw
->sp_pwdp
&&
832 spw
->sp_lstchg
== -1) {
833 spw
->sp_lstchg
= DAY_NOW_32
;
837 spw
->sp_max
= p
->data
.val_i
;
843 if (spw
->sp_max
== -1 && p
->data
.val_i
!= -1 &&
844 max_present(p
->next
) == 0)
845 return (PWU_AGING_DISABLED
);
846 spw
->sp_warn
= p
->data
.val_i
;
849 spw
->sp_inact
= p
->data
.val_i
;
852 spw
->sp_expire
= p
->data
.val_i
;
855 spw
->sp_flag
= p
->data
.val_i
;
857 case ATTR_INCR_FAILED_LOGINS
:
859 int count
= (spw
->sp_flag
& FAILCOUNT_MASK
) + 1;
860 spw
->sp_flag
&= ~FAILCOUNT_MASK
;
861 spw
->sp_flag
|= min(FAILCOUNT_MASK
, count
);
862 p
->data
.val_i
= count
;
865 case ATTR_RST_FAILED_LOGINS
:
866 p
->data
.val_i
= spw
->sp_flag
& FAILCOUNT_MASK
;
867 spw
->sp_flag
&= ~FAILCOUNT_MASK
;
875 * What should the new aging values look like?
877 * There are a number of different conditions
879 * a) aging is already configured: don't touch it
881 * b) disable_aging is set: disable aging
883 * c) aging is not configured: turn on default aging;
885 * b) and c) of course only if aging_needed and !aging_set.
886 * (i.e., password changed, and aging values not changed)
889 if (spw
!= NULL
&& spw
->sp_max
<= 0) {
890 /* a) aging not yet configured */
891 if (aging_needed
&& !aging_set
) {
893 /* b) turn off aging */
894 spw
->sp_min
= spw
->sp_max
= spw
->sp_warn
= -1;
897 turn_on_default_aging(spw
);
902 return (PWU_SUCCESS
);
906 * files_update_shadow(char *name, struct spwd *spwd)
908 * update the shadow password file SHADOW to contain the spwd structure
909 * "spwd" for user "name"
912 files_update_shadow(char *name
, struct spwd
*spwd
)
918 char buf
[SPW_SCRATCH_SIZE
];
922 int err
= PWU_SUCCESS
;
924 /* Mode of the shadow file should be 400 or 000 */
925 if (stat64(SHADOW
, &stbuf
) < 0) {
926 err
= PWU_STAT_FAILED
;
930 /* copy mode from current shadow file (0400 or 0000) */
931 filemode
= stbuf
.st_mode
& S_IRUSR
;
934 * we can't specify filemodes to fopen(), and we SHOULD NOT
935 * set umask in multi-thread safe libraries, so we use
936 * a combination of open() and fdopen()
938 tempfd
= open(SHADTEMP
, O_WRONLY
|O_CREAT
|O_TRUNC
, filemode
);
940 err
= PWU_OPEN_FAILED
;
943 (void) fchown(tempfd
, (uid_t
)0, stbuf
.st_gid
);
945 if ((dst
= fdopen(tempfd
, "wF")) == NULL
) {
946 err
= PWU_OPEN_FAILED
;
950 if ((src
= fopen(SHADOW
, "rF")) == NULL
) {
951 err
= PWU_OPEN_FAILED
;
953 (void) unlink(SHADTEMP
);
958 * copy old shadow to temporary file while replacing the entry
959 * that matches "name".
961 while (fgetspent_r(src
, &cur
, buf
, sizeof (buf
)) != NULL
) {
963 if (strcmp(cur
.sp_namp
, name
) == 0)
964 result
= putspent(spwd
, dst
);
966 result
= putspent(&cur
, dst
);
969 err
= PWU_WRITE_FAILED
;
978 if (fclose(dst
) != 0) {
980 * Something went wrong (ENOSPC for example). Don't
981 * use the resulting temporary file!
983 err
= PWU_CLOSE_FAILED
;
984 (void) unlink(SHADTEMP
);
989 * Rename stmp to shadow:
990 * 1. make sure /etc/oshadow is gone
991 * 2. ln /etc/shadow /etc/oshadow
992 * 3. mv /etc/stmp /etc/shadow
994 if (unlink(OSHADOW
) && access(OSHADOW
, 0) == 0) {
995 err
= PWU_UPDATE_FAILED
;
996 (void) unlink(SHADTEMP
);
1000 if (link(SHADOW
, OSHADOW
) == -1) {
1001 err
= PWU_UPDATE_FAILED
;
1002 (void) unlink(SHADTEMP
);
1006 if (rename(SHADTEMP
, SHADOW
) == -1) {
1007 err
= PWU_UPDATE_FAILED
;
1008 (void) unlink(SHADTEMP
);
1011 (void) unlink(OSHADOW
);
1018 files_update_passwd(char *name
, struct passwd
*pwd
)
1020 struct stat64 stbuf
;
1024 char buf
[PWD_SCRATCH_SIZE
];
1026 int err
= PWU_SUCCESS
;
1028 if (stat64(PASSWD
, &stbuf
) < 0) {
1029 err
= PWU_STAT_FAILED
;
1033 /* see files_update_shadow() for open()+fdopen() rationale */
1035 if ((tempfd
= open(PASSTEMP
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) < 0) {
1036 err
= PWU_OPEN_FAILED
;
1039 if ((dst
= fdopen(tempfd
, "wF")) == NULL
) {
1040 err
= PWU_OPEN_FAILED
;
1043 if ((src
= fopen(PASSWD
, "rF")) == NULL
) {
1044 err
= PWU_OPEN_FAILED
;
1046 (void) unlink(PASSTEMP
);
1051 * copy old password entries to temporary file while replacing
1052 * the entry that matches "name"
1054 while (fgetpwent_r(src
, &cur
, buf
, sizeof (buf
)) != NULL
) {
1055 if (strcmp(cur
.pw_name
, name
) == 0)
1056 result
= putpwent(pwd
, dst
);
1058 result
= putpwent(&cur
, dst
);
1060 err
= PWU_WRITE_FAILED
;
1068 if (fclose(dst
) != 0) {
1069 err
= PWU_CLOSE_FAILED
;
1070 goto passwd_exit
; /* Don't trust the temporary file */
1073 /* Rename temp to passwd */
1074 if (unlink(OPASSWD
) && access(OPASSWD
, 0) == 0) {
1075 err
= PWU_UPDATE_FAILED
;
1076 (void) unlink(PASSTEMP
);
1080 if (link(PASSWD
, OPASSWD
) == -1) {
1081 err
= PWU_UPDATE_FAILED
;
1082 (void) unlink(PASSTEMP
);
1086 if (rename(PASSTEMP
, PASSWD
) == -1) {
1087 err
= PWU_UPDATE_FAILED
;
1088 (void) unlink(PASSTEMP
);
1092 (void) chmod(PASSWD
, 0644);
1100 * files_putpwnam(name, oldpw, rep, buf)
1102 * store the password attributes contained in "buf" in /etc/passwd and
1107 files_putpwnam(char *name
, char *oldpw
, pwu_repository_t
*rep
, void *buf
)
1109 struct pwbuf
*pwbuf
= (struct pwbuf
*)buf
;
1110 int result
= PWU_SUCCESS
;
1113 result
= files_update_passwd(name
, pwbuf
->pwd
);
1116 if (result
== PWU_SUCCESS
&& pwbuf
->spwd
) {
1117 if (pwbuf
->update_history
!= 0) {
1118 debug("update_history = %d", pwbuf
->update_history
);
1119 result
= files_update_history(name
, pwbuf
->spwd
);
1121 debug("no password change");
1123 if (result
== PWU_SUCCESS
) {
1124 result
= files_update_shadow(name
, pwbuf
->spwd
);
1129 (void) memset(pwbuf
->pwd
, 0, sizeof (struct passwd
));
1130 (void) memset(pwbuf
->pwd_scratch
, 0, PWD_SCRATCH_SIZE
);
1132 free(pwbuf
->pwd_scratch
);
1135 (void) memset(pwbuf
->spwd
, 0, sizeof (struct spwd
));
1136 (void) memset(pwbuf
->spwd_scratch
, 0, SPW_SCRATCH_SIZE
);
1138 free(pwbuf
->spwd_scratch
);
1140 if (pwbuf
->new_sp_pwdp
) {
1141 free(pwbuf
->new_sp_pwdp
);
1148 * NOTE: This is all covered under the repository lock held for updating
1149 * passwd(4) and shadow(4).
1152 files_update_history(char *name
, struct spwd
*spwd
)
1156 FILE *src
; /* history database file */
1157 FILE *dst
; /* temp history database being updated */
1158 struct stat64 statbuf
;
1159 char buf
[MAX_LOGNAME
+ MAXHISTORY
+
1160 (MAXHISTORY
* CRYPT_MAXCIPHERTEXTLEN
)+1];
1163 if ((histsize
= def_getint("HISTORY=", DEFHISTORY
)) == 0) {
1164 debug("files_update_history(%s) no history, unlinking", name
);
1165 (void) unlink(HISTORY
);
1166 return (PWU_SUCCESS
); /* no history update defined */
1168 debug("files_update_history(%s, %s) histsize = %d", name
, spwd
->sp_pwdp
,
1171 if (histsize
> MAXHISTORY
)
1172 histsize
= MAXHISTORY
;
1173 if ((tmpfd
= open(HISTEMP
, O_WRONLY
|O_CREAT
|O_TRUNC
, HISTMODE
)) < 0) {
1174 return (PWU_OPEN_FAILED
);
1176 (void) fchown(tmpfd
, (uid_t
)0, (gid_t
)0);
1178 /* get ready to copy */
1179 if (((src
= fopen(HISTORY
, "rF")) == NULL
) &&
1180 (errno
!= ENOENT
)) {
1181 (void) unlink(HISTEMP
);
1182 return (PWU_OPEN_FAILED
);
1184 if ((dst
= fdopen(tmpfd
, "wF")) == NULL
) {
1186 (void) unlink(HISTEMP
);
1187 return (PWU_OPEN_FAILED
);
1190 /* Copy and update if found. Add if not found. */
1194 while ((src
!= NULL
) &&
1195 (fgets(buf
, sizeof (buf
), src
) != NULL
)) {
1199 /* get username field */
1200 user
= strtok_r(buf
, ":", &last
);
1203 debug("files_update_history: read=\"%s\"", user
);
1206 if (strcmp(user
, name
) == 0) {
1210 /* found user, update */
1212 (void) fprintf(dst
, "%s:%s:", name
, spwd
->sp_pwdp
);
1213 debug("files_update_history: update user\n"
1214 "\t%s:%s:", name
, spwd
->sp_pwdp
);
1216 /* get old crypted password history */
1217 for (i
= 0; i
< MAXHISTORY
-1; i
++) {
1218 crypt
= strtok_r(NULL
, ":", &last
);
1219 if (crypt
== NULL
||
1223 (void) fprintf(dst
, "%s:", crypt
);
1224 debug("\t%d = %s:", i
+1, crypt
);
1226 (void) fprintf(dst
, "\n");
1229 /* copy other users to updated file */
1230 (void) fprintf(dst
, "%s:%s", user
, last
);
1232 debug("files_update_history: copy line %s",
1240 /* user not found, add to history file */
1241 (void) fprintf(dst
, "%s:%s:\n", name
, spwd
->sp_pwdp
);
1242 debug("files_update_history: add line\n"
1243 "\t%s:%s:", name
, spwd
->sp_pwdp
);
1248 /* If something messed up in file system, loose the update */
1249 if (fclose(dst
) != 0) {
1251 debug("files_update_history: update file close failed %d",
1253 (void) unlink(HISTEMP
);
1254 return (PWU_CLOSE_FAILED
);
1258 * rename history to ohistory,
1259 * rename tmp to history,
1263 (void) unlink(OHISTORY
);
1265 if (stat64(OHISTORY
, &statbuf
) == 0 ||
1266 ((src
!= NULL
) && (link(HISTORY
, OHISTORY
) != 0)) ||
1267 rename(HISTEMP
, HISTORY
) != 0) {
1269 /* old history won't go away, loose the update */
1270 debug("files_update_history: update file rename failed %d",
1272 (void) unlink(HISTEMP
);
1273 return (PWU_UPDATE_FAILED
);
1276 (void) unlink(OHISTORY
);
1277 return (PWU_SUCCESS
);