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
);
60 * files function pointer table, used by passwdutil_init to initialize
61 * the global Repository-OPerations table "rops"
63 struct repops files_repops
= {
69 files_user_to_authenticate
,
75 * this structure defines the buffer used to keep state between
76 * get/update/put calls
88 * We should use sysconf, but there is no sysconf name for SHADOW
89 * so we use these from nss_dbdefs
91 #define PWD_SCRATCH_SIZE NSS_LINELEN_PASSWD
92 #define SPW_SCRATCH_SIZE NSS_LINELEN_SHADOW
95 * lock functions for files repository
124 return (PWU_SYSTEM_ERROR
);
126 return (PWU_SUCCESS
);
132 * Are we a privileged user with regard to the files repository?
135 files_privileged(void)
137 return (getuid() == 0);
142 * private_getpwnam_r()
144 * A private implementation of getpwnam_r which does *not* fall back to
145 * other services possibly defined in nsswitch.conf
147 * behaves like getpwnam_r().
150 private_getpwnam_r(const char *name
, struct passwd
*result
, char *buffer
,
156 if ((fp
= fopen(PASSWD
, "rF")) == NULL
)
160 while (!found
&& fgetpwent_r(fp
, result
, buffer
, buflen
) != NULL
) {
161 if (strcmp(name
, result
->pw_name
) == 0)
168 (void) memset(buffer
, 0, buflen
);
169 (void) memset(result
, 0, sizeof (*result
));
177 * private_getspnam_r()
179 * A private implementation of getspnam_r which does *not* fall back to
180 * other services possibly defined in nsswitch.conf.
182 * Behaves like getspnam_r(). Since we use fgetspent_t(), all numeric
183 * fields that are undefined in /etc/shadow will be set to -1.
187 private_getspnam_r(const char *name
, struct spwd
*result
, char *buffer
,
193 fp
= fopen(SHADOW
, "rF");
198 while (!found
&& fgetspent_r(fp
, result
, buffer
, buflen
) != NULL
) {
199 if (strcmp(name
, result
->sp_namp
) == 0)
206 (void) memset(buffer
, 0, buflen
);
207 (void) memset(result
, 0, sizeof (*result
));
214 * files_getpwnam(name, items, rep, buf)
219 files_getpwnam(char *name
, attrlist
*items
, pwu_repository_t
*rep
, void **buf
)
223 int err
= PWU_SUCCESS
;
225 *buf
= calloc(1, sizeof (struct pwbuf
));
226 pwbuf
= (struct pwbuf
*)*buf
;
231 * determine which password structure (/etc/passwd or /etc/shadow)
232 * we need for the items we need to update
234 for (p
= items
; p
!= NULL
; p
= p
->next
) {
244 if (pwbuf
->pwd
== NULL
) {
245 pwbuf
->pwd
= malloc(sizeof (struct passwd
));
246 if (pwbuf
->pwd
== NULL
) {
253 case ATTR_PASSWD_SERVER_POLICY
:
261 case ATTR_LOCK_ACCOUNT
:
262 case ATTR_EXPIRE_PASSWORD
:
263 case ATTR_FAILED_LOGINS
:
264 case ATTR_INCR_FAILED_LOGINS
:
265 case ATTR_RST_FAILED_LOGINS
:
266 case ATTR_NOLOGIN_ACCOUNT
:
267 case ATTR_UNLOCK_ACCOUNT
:
268 if (pwbuf
->spwd
== NULL
) {
269 pwbuf
->spwd
= malloc(sizeof (struct spwd
));
270 if (pwbuf
->spwd
== NULL
) {
278 * Some other repository might have different values
279 * so we ignore those.
286 if ((pwbuf
->pwd_scratch
= malloc(PWD_SCRATCH_SIZE
)) == NULL
) {
290 if (private_getpwnam_r(name
, pwbuf
->pwd
, pwbuf
->pwd_scratch
,
291 PWD_SCRATCH_SIZE
) == NULL
) {
298 if ((pwbuf
->spwd_scratch
= malloc(SPW_SCRATCH_SIZE
)) == NULL
) {
302 if (private_getspnam_r(name
, pwbuf
->spwd
, pwbuf
->spwd_scratch
,
303 SPW_SCRATCH_SIZE
) == NULL
) {
309 return (PWU_SUCCESS
);
311 if (pwbuf
->pwd
) free(pwbuf
->pwd
);
312 if (pwbuf
->pwd_scratch
) free(pwbuf
->pwd_scratch
);
313 if (pwbuf
->spwd
) free(pwbuf
->spwd
);
314 if (pwbuf
->spwd_scratch
) free(pwbuf
->spwd_scratch
);
322 * int files_user_to_authenticate(name, rep, auth_user, privileged)
323 * Determine which user needs to be authenticated. For files, the
324 * possible return values are:
326 * PWU_SUCCESS and (auth_user == NULL || auth_user = user)
332 files_user_to_authenticate(char *user
, pwu_repository_t
*rep
,
333 char **auth_user
, int *privileged
)
337 attrlist attr_tmp
[1] = { { ATTR_UID
, NULL
, NULL
} };
339 /* check to see if target user is present in files */
340 res
= files_getpwnam(user
, &attr_tmp
[0], rep
, (void **)&pwbuf
);
341 if (res
!= PWU_SUCCESS
)
344 if (files_privileged()) {
350 if (getuid() == pwbuf
->pwd
->pw_uid
) {
351 if ((*auth_user
= strdup(user
)) == NULL
) {
361 if (pwbuf
->pwd
) free(pwbuf
->pwd
);
362 if (pwbuf
->pwd_scratch
) free(pwbuf
->pwd_scratch
);
363 if (pwbuf
->spwd
) free(pwbuf
->spwd
);
364 if (pwbuf
->spwd_scratch
) free(pwbuf
->spwd_scratch
);
371 * Password history file format:
372 * user:crypw1: ... crypwn: such that n <= MAXHISTORY
374 #define HISTORY "/etc/security/passhistory"
375 #define HISTEMP "/etc/security/pwhistemp"
376 #define OHISTORY "/etc/security/opwhistory"
377 #define HISTMODE S_IRUSR /* mode to create history file */
380 * 3*LOGNAME_MAX just in case there are long user names.
381 * Traditionally Solaris LOGNAME_MAX (_POSIX_LOGIN_NAME_MAX) is 13,
382 * but some sites often user more.
383 * If LOGNAME_MAX ever becomes reasonable (128) and actually enforced,
387 #define MAX_LOGNAME (3 * LOGNAME_MAX)
390 * files_checkhistory - check if a user's new password is in the user's
391 * old password history.
395 * passwd = new clear text password.
398 * PWU_SUCCESS, passwd found in user's old password history.
399 * The caller should only be interested and fail if
400 * PWU_SUCCESS is returned.
401 * PWU_NOT_FOUND, passwd not in user's old password history.
402 * PWU_errors, PWU_ errors from other routines.
406 files_checkhistory(char *user
, char *passwd
, pwu_repository_t
*rep
)
411 attr
.type
= ATTR_HISTORY
;
412 attr
.data
.val_s
= NULL
;
415 debug("files_checkhistory(user=%s)", user
);
419 * This depends on the underlying files_getattr implementation
420 * treating user not found in backing store or no history as
425 if ((res
= files_getattr(user
, &attr
, rep
)) == PWU_SUCCESS
) {
429 char *last
= attr
.data
.val_s
;
431 if ((histsize
= def_getint("HISTORY=", DEFHISTORY
)) == 0) {
432 debug("files_checkhistory: no history requested");
437 debug("files_checkhistory: histsize = %d", histsize
);
438 if (histsize
> MAXHISTORY
)
439 histsize
= MAXHISTORY
;
441 debug("line to test\n\t%s", last
);
443 /* compare crypt_passwd to attr.data.val_s strings. */
445 while ((histsize
-- > 0) &&
446 (((s
= strtok_r(NULL
, ":", &last
)) != NULL
) &&
449 crypt_passwd
= crypt(passwd
, s
);
450 debug("files_checkhistory: user_pw=%s, history_pw=%s",
452 if (strcmp(crypt_passwd
, s
) == 0) {
457 debug("files_checkhistory(%s, %s) = %d", user
, crypt_passwd
,
461 if (attr
.data
.val_s
!= NULL
)
462 free(attr
.data
.val_s
);
468 * files_getattr(name, items, rep)
470 * Get attributes specified in list 'items'
473 files_getattr(char *name
, attrlist
*items
, pwu_repository_t
*rep
)
481 res
= files_getpwnam(name
, items
, rep
, (void **)&pwbuf
);
482 if (res
!= PWU_SUCCESS
)
488 for (w
= items
; res
== PWU_SUCCESS
&& w
!= NULL
; w
= w
->next
) {
491 if ((w
->data
.val_s
= strdup(pw
->pw_name
)) == NULL
)
495 if ((w
->data
.val_s
= strdup(pw
->pw_comment
)) == NULL
)
499 if ((w
->data
.val_s
= strdup(pw
->pw_gecos
)) == NULL
)
503 if ((w
->data
.val_s
= strdup(pw
->pw_dir
)) == NULL
)
507 if ((w
->data
.val_s
= strdup(pw
->pw_shell
)) == NULL
)
511 * Nothing special needs to be done for
515 case ATTR_PASSWD_SERVER_POLICY
:
516 if ((w
->data
.val_s
= strdup(spw
->sp_pwdp
)) == NULL
)
520 if ((w
->data
.val_s
= strdup(pw
->pw_age
)) == NULL
)
524 if ((w
->data
.val_s
= strdup("files")) == NULL
)
529 char buf
[MAX_LOGNAME
+ MAXHISTORY
+
530 (MAXHISTORY
* CRYPT_MAXCIPHERTEXTLEN
)+1];
533 debug("files_getattr: Get password history for %s ",
536 if ((history
= fopen(HISTORY
, "rF")) == NULL
) {
537 debug("files_getattr: %s not found", HISTORY
);
538 res
= PWU_OPEN_FAILED
;
542 while ((s
= fgets(buf
, sizeof (buf
), history
)) !=
552 debug("got history line for %s", s
);
554 if (strcmp(s
, name
) == 0) {
556 if ((items
->data
.val_s
=
557 strdup(s1
+1)) == NULL
)
564 (void) fclose(history
);
570 w
->data
.val_i
= pw
->pw_uid
;
573 w
->data
.val_i
= pw
->pw_gid
;
576 w
->data
.val_i
= spw
->sp_lstchg
;
579 w
->data
.val_i
= spw
->sp_min
;
582 w
->data
.val_i
= spw
->sp_max
;
585 w
->data
.val_i
= spw
->sp_warn
;
588 w
->data
.val_i
= spw
->sp_inact
;
591 w
->data
.val_i
= spw
->sp_expire
;
594 w
->data
.val_i
= spw
->sp_flag
;
596 case ATTR_FAILED_LOGINS
:
597 w
->data
.val_i
= spw
->sp_flag
& FAILCOUNT_MASK
;
605 if (pwbuf
->pwd
) free(pwbuf
->pwd
);
606 if (pwbuf
->pwd_scratch
) free(pwbuf
->pwd_scratch
);
607 if (pwbuf
->spwd
) free(pwbuf
->spwd
);
608 if (pwbuf
->spwd_scratch
) free(pwbuf
->spwd_scratch
);
617 * see if attribute ATTR_MAX, with value != -1, is present in
618 * attribute-list "list".
620 * returns 1 if present, 0 otherwise.
623 max_present(attrlist
*list
)
626 if (list
->type
== ATTR_MAX
&& list
->data
.val_i
!= -1)
635 * files_update(items, rep, buf)
637 * update the information in buf with the attributes specified in
642 files_update(attrlist
*items
, pwu_repository_t
*rep
, void *buf
)
644 struct pwbuf
*pwbuf
= (struct pwbuf
*)buf
;
648 int aging_needed
= 0;
656 pwbuf
->update_history
= 0;
659 * if sp_max==0 : disable passwd aging after updating the password
661 disable_aging
= (spw
!= NULL
&& spw
->sp_max
== 0);
663 for (p
= items
; p
!= NULL
; p
= p
->next
) {
666 break; /* We are able to handle this, but... */
668 pw
->pw_uid
= (uid_t
)p
->data
.val_i
;
671 pw
->pw_gid
= (gid_t
)p
->data
.val_i
;
674 pw
->pw_age
= p
->data
.val_s
;
677 pw
->pw_comment
= p
->data
.val_s
;
680 pw
->pw_gecos
= p
->data
.val_s
;
683 pw
->pw_dir
= p
->data
.val_s
;
686 pw
->pw_shell
= p
->data
.val_s
;
690 * Nothing special needs to be done for
694 case ATTR_PASSWD_SERVER_POLICY
:
696 * There is a special case only for files: if the
697 * password is to be deleted (-d to passwd),
698 * p->data.val_s will be NULL.
700 if (p
->data
.val_s
== NULL
) {
706 salt
= crypt_gensalt(spw
->sp_pwdp
, pw
);
711 /* algorithm problem? */
712 syslog(LOG_AUTH
| LOG_ALERT
,
713 "passwdutil: crypt_gensalt %m");
714 return (PWU_UPDATE_FAILED
);
716 hash
= crypt(p
->data
.val_s
, salt
);
722 pword
= strdup(hash
);
728 if (pwbuf
->new_sp_pwdp
)
729 free(pwbuf
->new_sp_pwdp
);
730 pwbuf
->new_sp_pwdp
= pword
;
731 spw
->sp_pwdp
= pword
;
733 pwbuf
->update_history
= 1;
735 spw
->sp_flag
&= ~FAILCOUNT_MASK
; /* reset count */
736 spw
->sp_lstchg
= DAY_NOW_32
;
738 case ATTR_LOCK_ACCOUNT
:
739 if (spw
->sp_pwdp
== NULL
) {
740 spw
->sp_pwdp
= LOCKSTRING
;
741 } else if ((strncmp(spw
->sp_pwdp
, LOCKSTRING
,
742 sizeof (LOCKSTRING
)-1) != 0) &&
743 (strcmp(spw
->sp_pwdp
, NOLOGINSTRING
) != 0)) {
744 len
= sizeof (LOCKSTRING
)-1 +
745 strlen(spw
->sp_pwdp
) + 1;
751 (void) strlcpy(pword
, LOCKSTRING
, len
);
752 (void) strlcat(pword
, spw
->sp_pwdp
, len
);
753 if (pwbuf
->new_sp_pwdp
)
754 free(pwbuf
->new_sp_pwdp
);
755 pwbuf
->new_sp_pwdp
= pword
;
756 spw
->sp_pwdp
= pword
;
758 spw
->sp_lstchg
= DAY_NOW_32
;
760 case ATTR_UNLOCK_ACCOUNT
:
761 if (spw
->sp_pwdp
!= NULL
&&
762 strncmp(spw
->sp_pwdp
, LOCKSTRING
,
763 sizeof (LOCKSTRING
)-1) == 0) {
764 (void) strcpy(spw
->sp_pwdp
, spw
->sp_pwdp
+
765 sizeof (LOCKSTRING
)-1);
767 spw
->sp_lstchg
= DAY_NOW_32
;
769 case ATTR_NOLOGIN_ACCOUNT
:
770 spw
->sp_pwdp
= NOLOGINSTRING
;
771 if (pwbuf
->new_sp_pwdp
) {
772 free(pwbuf
->new_sp_pwdp
);
773 pwbuf
->new_sp_pwdp
= NULL
;
775 spw
->sp_lstchg
= DAY_NOW_32
;
777 case ATTR_EXPIRE_PASSWORD
:
781 spw
->sp_lstchg
= p
->data
.val_i
;
784 if (spw
->sp_max
== -1 &&
785 p
->data
.val_i
!= -1 && max_present(p
->next
) == 0)
786 return (PWU_AGING_DISABLED
);
787 spw
->sp_min
= p
->data
.val_i
;
791 if (p
->data
.val_i
== -1) {
792 /* Turn aging off -> Reset min and warn too */
799 if (spw
->sp_min
== -1) {
801 * If minage has not been set with
802 * a command-line option, we set it
809 * If aging was turned off, we update lstchg.
811 * We take care not to update lstchg if the
812 * user has no password, otherwise the user
813 * might not be required to provide a password
814 * the next time they log-in.
816 * Also, if lstchg != -1 (i.e., not set in
817 * /etc/shadow), we keep the old value.
819 if (spw
->sp_max
== -1 &&
820 spw
->sp_pwdp
!= NULL
&& *spw
->sp_pwdp
&&
821 spw
->sp_lstchg
== -1) {
822 spw
->sp_lstchg
= DAY_NOW_32
;
826 spw
->sp_max
= p
->data
.val_i
;
832 if (spw
->sp_max
== -1 && p
->data
.val_i
!= -1 &&
833 max_present(p
->next
) == 0)
834 return (PWU_AGING_DISABLED
);
835 spw
->sp_warn
= p
->data
.val_i
;
838 spw
->sp_inact
= p
->data
.val_i
;
841 spw
->sp_expire
= p
->data
.val_i
;
844 spw
->sp_flag
= p
->data
.val_i
;
846 case ATTR_INCR_FAILED_LOGINS
:
848 int count
= (spw
->sp_flag
& FAILCOUNT_MASK
) + 1;
849 spw
->sp_flag
&= ~FAILCOUNT_MASK
;
850 spw
->sp_flag
|= min(FAILCOUNT_MASK
, count
);
851 p
->data
.val_i
= count
;
854 case ATTR_RST_FAILED_LOGINS
:
855 p
->data
.val_i
= spw
->sp_flag
& FAILCOUNT_MASK
;
856 spw
->sp_flag
&= ~FAILCOUNT_MASK
;
864 * What should the new aging values look like?
866 * There are a number of different conditions
868 * a) aging is already configured: don't touch it
870 * b) disable_aging is set: disable aging
872 * c) aging is not configured: turn on default aging;
874 * b) and c) of course only if aging_needed and !aging_set.
875 * (i.e., password changed, and aging values not changed)
878 if (spw
!= NULL
&& spw
->sp_max
<= 0) {
879 /* a) aging not yet configured */
880 if (aging_needed
&& !aging_set
) {
882 /* b) turn off aging */
883 spw
->sp_min
= spw
->sp_max
= spw
->sp_warn
= -1;
886 turn_on_default_aging(spw
);
891 return (PWU_SUCCESS
);
895 * files_update_shadow(char *name, struct spwd *spwd)
897 * update the shadow password file SHADOW to contain the spwd structure
898 * "spwd" for user "name"
901 files_update_shadow(char *name
, struct spwd
*spwd
)
907 char buf
[SPW_SCRATCH_SIZE
];
911 int err
= PWU_SUCCESS
;
913 /* Mode of the shadow file should be 400 or 000 */
914 if (stat64(SHADOW
, &stbuf
) < 0) {
915 err
= PWU_STAT_FAILED
;
919 /* copy mode from current shadow file (0400 or 0000) */
920 filemode
= stbuf
.st_mode
& S_IRUSR
;
923 * we can't specify filemodes to fopen(), and we SHOULD NOT
924 * set umask in multi-thread safe libraries, so we use
925 * a combination of open() and fdopen()
927 tempfd
= open(SHADTEMP
, O_WRONLY
|O_CREAT
|O_TRUNC
, filemode
);
929 err
= PWU_OPEN_FAILED
;
932 (void) fchown(tempfd
, (uid_t
)0, stbuf
.st_gid
);
934 if ((dst
= fdopen(tempfd
, "wF")) == NULL
) {
935 err
= PWU_OPEN_FAILED
;
939 if ((src
= fopen(SHADOW
, "rF")) == NULL
) {
940 err
= PWU_OPEN_FAILED
;
942 (void) unlink(SHADTEMP
);
947 * copy old shadow to temporary file while replacing the entry
948 * that matches "name".
950 while (fgetspent_r(src
, &cur
, buf
, sizeof (buf
)) != NULL
) {
952 if (strcmp(cur
.sp_namp
, name
) == 0)
953 result
= putspent(spwd
, dst
);
955 result
= putspent(&cur
, dst
);
958 err
= PWU_WRITE_FAILED
;
967 if (fclose(dst
) != 0) {
969 * Something went wrong (ENOSPC for example). Don't
970 * use the resulting temporary file!
972 err
= PWU_CLOSE_FAILED
;
973 (void) unlink(SHADTEMP
);
978 * Rename stmp to shadow:
979 * 1. make sure /etc/oshadow is gone
980 * 2. ln /etc/shadow /etc/oshadow
981 * 3. mv /etc/stmp /etc/shadow
983 if (unlink(OSHADOW
) && access(OSHADOW
, 0) == 0) {
984 err
= PWU_UPDATE_FAILED
;
985 (void) unlink(SHADTEMP
);
989 if (link(SHADOW
, OSHADOW
) == -1) {
990 err
= PWU_UPDATE_FAILED
;
991 (void) unlink(SHADTEMP
);
995 if (rename(SHADTEMP
, SHADOW
) == -1) {
996 err
= PWU_UPDATE_FAILED
;
997 (void) unlink(SHADTEMP
);
1000 (void) unlink(OSHADOW
);
1007 files_update_passwd(char *name
, struct passwd
*pwd
)
1009 struct stat64 stbuf
;
1013 char buf
[PWD_SCRATCH_SIZE
];
1015 int err
= PWU_SUCCESS
;
1017 if (stat64(PASSWD
, &stbuf
) < 0) {
1018 err
= PWU_STAT_FAILED
;
1022 /* see files_update_shadow() for open()+fdopen() rationale */
1024 if ((tempfd
= open(PASSTEMP
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) < 0) {
1025 err
= PWU_OPEN_FAILED
;
1028 if ((dst
= fdopen(tempfd
, "wF")) == NULL
) {
1029 err
= PWU_OPEN_FAILED
;
1032 if ((src
= fopen(PASSWD
, "rF")) == NULL
) {
1033 err
= PWU_OPEN_FAILED
;
1035 (void) unlink(PASSTEMP
);
1040 * copy old password entries to temporary file while replacing
1041 * the entry that matches "name"
1043 while (fgetpwent_r(src
, &cur
, buf
, sizeof (buf
)) != NULL
) {
1044 if (strcmp(cur
.pw_name
, name
) == 0)
1045 result
= putpwent(pwd
, dst
);
1047 result
= putpwent(&cur
, dst
);
1049 err
= PWU_WRITE_FAILED
;
1057 if (fclose(dst
) != 0) {
1058 err
= PWU_CLOSE_FAILED
;
1059 goto passwd_exit
; /* Don't trust the temporary file */
1062 /* Rename temp to passwd */
1063 if (unlink(OPASSWD
) && access(OPASSWD
, 0) == 0) {
1064 err
= PWU_UPDATE_FAILED
;
1065 (void) unlink(PASSTEMP
);
1069 if (link(PASSWD
, OPASSWD
) == -1) {
1070 err
= PWU_UPDATE_FAILED
;
1071 (void) unlink(PASSTEMP
);
1075 if (rename(PASSTEMP
, PASSWD
) == -1) {
1076 err
= PWU_UPDATE_FAILED
;
1077 (void) unlink(PASSTEMP
);
1081 (void) chmod(PASSWD
, 0644);
1089 * files_putpwnam(name, oldpw, rep, buf)
1091 * store the password attributes contained in "buf" in /etc/passwd and
1096 files_putpwnam(char *name
, char *oldpw
, pwu_repository_t
*rep
, void *buf
)
1098 struct pwbuf
*pwbuf
= (struct pwbuf
*)buf
;
1099 int result
= PWU_SUCCESS
;
1102 result
= files_update_passwd(name
, pwbuf
->pwd
);
1105 if (result
== PWU_SUCCESS
&& pwbuf
->spwd
) {
1106 if (pwbuf
->update_history
!= 0) {
1107 debug("update_history = %d", pwbuf
->update_history
);
1108 result
= files_update_history(name
, pwbuf
->spwd
);
1110 debug("no password change");
1112 if (result
== PWU_SUCCESS
) {
1113 result
= files_update_shadow(name
, pwbuf
->spwd
);
1118 (void) memset(pwbuf
->pwd
, 0, sizeof (struct passwd
));
1119 (void) memset(pwbuf
->pwd_scratch
, 0, PWD_SCRATCH_SIZE
);
1121 free(pwbuf
->pwd_scratch
);
1124 (void) memset(pwbuf
->spwd
, 0, sizeof (struct spwd
));
1125 (void) memset(pwbuf
->spwd_scratch
, 0, SPW_SCRATCH_SIZE
);
1127 free(pwbuf
->spwd_scratch
);
1129 if (pwbuf
->new_sp_pwdp
) {
1130 free(pwbuf
->new_sp_pwdp
);
1137 * NOTE: This is all covered under the repository lock held for updating
1138 * passwd(4) and shadow(4).
1141 files_update_history(char *name
, struct spwd
*spwd
)
1145 FILE *src
; /* history database file */
1146 FILE *dst
; /* temp history database being updated */
1147 struct stat64 statbuf
;
1148 char buf
[MAX_LOGNAME
+ MAXHISTORY
+
1149 (MAXHISTORY
* CRYPT_MAXCIPHERTEXTLEN
)+1];
1152 if ((histsize
= def_getint("HISTORY=", DEFHISTORY
)) == 0) {
1153 debug("files_update_history(%s) no history, unlinking", name
);
1154 (void) unlink(HISTORY
);
1155 return (PWU_SUCCESS
); /* no history update defined */
1157 debug("files_update_history(%s, %s) histsize = %d", name
, spwd
->sp_pwdp
,
1160 if (histsize
> MAXHISTORY
)
1161 histsize
= MAXHISTORY
;
1162 if ((tmpfd
= open(HISTEMP
, O_WRONLY
|O_CREAT
|O_TRUNC
, HISTMODE
)) < 0) {
1163 return (PWU_OPEN_FAILED
);
1165 (void) fchown(tmpfd
, (uid_t
)0, (gid_t
)0);
1167 /* get ready to copy */
1168 if (((src
= fopen(HISTORY
, "rF")) == NULL
) &&
1169 (errno
!= ENOENT
)) {
1170 (void) unlink(HISTEMP
);
1171 return (PWU_OPEN_FAILED
);
1173 if ((dst
= fdopen(tmpfd
, "wF")) == NULL
) {
1175 (void) unlink(HISTEMP
);
1176 return (PWU_OPEN_FAILED
);
1179 /* Copy and update if found. Add if not found. */
1183 while ((src
!= NULL
) &&
1184 (fgets(buf
, sizeof (buf
), src
) != NULL
)) {
1188 /* get username field */
1189 user
= strtok_r(buf
, ":", &last
);
1192 debug("files_update_history: read=\"%s\"", user
);
1195 if (strcmp(user
, name
) == 0) {
1199 /* found user, update */
1201 (void) fprintf(dst
, "%s:%s:", name
, spwd
->sp_pwdp
);
1202 debug("files_update_history: update user\n"
1203 "\t%s:%s:", name
, spwd
->sp_pwdp
);
1205 /* get old crypted password history */
1206 for (i
= 0; i
< MAXHISTORY
-1; i
++) {
1207 crypt
= strtok_r(NULL
, ":", &last
);
1208 if (crypt
== NULL
||
1212 (void) fprintf(dst
, "%s:", crypt
);
1213 debug("\t%d = %s:", i
+1, crypt
);
1215 (void) fprintf(dst
, "\n");
1218 /* copy other users to updated file */
1219 (void) fprintf(dst
, "%s:%s", user
, last
);
1221 debug("files_update_history: copy line %s",
1229 /* user not found, add to history file */
1230 (void) fprintf(dst
, "%s:%s:\n", name
, spwd
->sp_pwdp
);
1231 debug("files_update_history: add line\n"
1232 "\t%s:%s:", name
, spwd
->sp_pwdp
);
1237 /* If something messed up in file system, loose the update */
1238 if (fclose(dst
) != 0) {
1240 debug("files_update_history: update file close failed %d",
1242 (void) unlink(HISTEMP
);
1243 return (PWU_CLOSE_FAILED
);
1247 * rename history to ohistory,
1248 * rename tmp to history,
1252 (void) unlink(OHISTORY
);
1254 if (stat64(OHISTORY
, &statbuf
) == 0 ||
1255 ((src
!= NULL
) && (link(HISTORY
, OHISTORY
) != 0)) ||
1256 rename(HISTEMP
, HISTORY
) != 0) {
1258 /* old history won't go away, loose the update */
1259 debug("files_update_history: update file rename failed %d",
1261 (void) unlink(HISTEMP
);
1262 return (PWU_UPDATE_FAILED
);
1265 (void) unlink(OHISTORY
);
1266 return (PWU_SUCCESS
);