10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51914Scasper * Common Development and Distribution License (the "License"). 61914Scasper * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 221914Scasper * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/types.h> 290Sstevel@tonic-gate #include <fcntl.h> 300Sstevel@tonic-gate #include <errno.h> 310Sstevel@tonic-gate #include <stdlib.h> 320Sstevel@tonic-gate #include <sys/stat.h> 330Sstevel@tonic-gate #include <pwd.h> 340Sstevel@tonic-gate #include <shadow.h> 350Sstevel@tonic-gate #include <string.h> 360Sstevel@tonic-gate #include <strings.h> 370Sstevel@tonic-gate #include <stdlib.h> 380Sstevel@tonic-gate #include <unistd.h> 390Sstevel@tonic-gate #include <nss_dbdefs.h> 400Sstevel@tonic-gate #include <macros.h> 410Sstevel@tonic-gate #include <syslog.h> 420Sstevel@tonic-gate 430Sstevel@tonic-gate #include <limits.h> /* LOGNAME_MAX -- max Solaris user name */ 440Sstevel@tonic-gate 450Sstevel@tonic-gate #include "passwdutil.h" 460Sstevel@tonic-gate 470Sstevel@tonic-gate int files_lock(void); 480Sstevel@tonic-gate int files_unlock(void); 490Sstevel@tonic-gate int files_checkhistory(char *user, char *passwd, pwu_repository_t *rep); 500Sstevel@tonic-gate int files_getattr(char *name, attrlist *item, pwu_repository_t *rep); 510Sstevel@tonic-gate int files_getpwnam(char *name, attrlist *items, pwu_repository_t *rep, 520Sstevel@tonic-gate void **buf); 530Sstevel@tonic-gate int files_update(attrlist *items, pwu_repository_t *rep, void *buf); 540Sstevel@tonic-gate int files_putpwnam(char *name, char *oldpw, char *dummy, 550Sstevel@tonic-gate pwu_repository_t *rep, void *buf); 560Sstevel@tonic-gate int files_user_to_authenticate(char *name, pwu_repository_t *rep, 570Sstevel@tonic-gate char **auth_user, int *privileged); 580Sstevel@tonic-gate 590Sstevel@tonic-gate static int files_update_history(char *name, struct spwd *spwd); 600Sstevel@tonic-gate 610Sstevel@tonic-gate /* 620Sstevel@tonic-gate * files function pointer table, used by passwdutil_init to initialize 630Sstevel@tonic-gate * the global Repository-OPerations table "rops" 640Sstevel@tonic-gate */ 650Sstevel@tonic-gate struct repops files_repops = { 660Sstevel@tonic-gate files_checkhistory, 670Sstevel@tonic-gate files_getattr, 680Sstevel@tonic-gate files_getpwnam, 690Sstevel@tonic-gate files_update, 700Sstevel@tonic-gate files_putpwnam, 710Sstevel@tonic-gate files_user_to_authenticate, 720Sstevel@tonic-gate files_lock, 730Sstevel@tonic-gate files_unlock 740Sstevel@tonic-gate }; 750Sstevel@tonic-gate 760Sstevel@tonic-gate /* 770Sstevel@tonic-gate * this structure defines the buffer used to keep state between 780Sstevel@tonic-gate * get/update/put calls 790Sstevel@tonic-gate */ 800Sstevel@tonic-gate struct pwbuf { 810Sstevel@tonic-gate int update_history; 820Sstevel@tonic-gate struct passwd *pwd; 830Sstevel@tonic-gate char *pwd_scratch; 840Sstevel@tonic-gate struct spwd *spwd; 850Sstevel@tonic-gate char *spwd_scratch; 860Sstevel@tonic-gate char *new_sp_pwdp; 870Sstevel@tonic-gate }; 880Sstevel@tonic-gate 890Sstevel@tonic-gate /* 900Sstevel@tonic-gate * We should use sysconf, but there is no sysconf name for SHADOW 910Sstevel@tonic-gate * so we use these from nss_dbdefs 920Sstevel@tonic-gate */ 930Sstevel@tonic-gate #define PWD_SCRATCH_SIZE NSS_LINELEN_PASSWD 940Sstevel@tonic-gate #define SPW_SCRATCH_SIZE NSS_LINELEN_SHADOW 950Sstevel@tonic-gate 960Sstevel@tonic-gate /* 970Sstevel@tonic-gate * lock functions for files repository 980Sstevel@tonic-gate */ 990Sstevel@tonic-gate int 1000Sstevel@tonic-gate files_lock(void) 1010Sstevel@tonic-gate { 1020Sstevel@tonic-gate int res; 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate if (lckpwdf()) { 1050Sstevel@tonic-gate switch (errno) { 1060Sstevel@tonic-gate case EINTR: 1070Sstevel@tonic-gate res = PWU_BUSY; 1080Sstevel@tonic-gate break; 1090Sstevel@tonic-gate case EACCES: 1100Sstevel@tonic-gate res = PWU_DENIED; 1110Sstevel@tonic-gate break; 1120Sstevel@tonic-gate case 0: 1130Sstevel@tonic-gate res = PWU_SUCCESS; 1140Sstevel@tonic-gate break; 1150Sstevel@tonic-gate } 1160Sstevel@tonic-gate } else 1170Sstevel@tonic-gate res = PWU_SUCCESS; 1180Sstevel@tonic-gate 1190Sstevel@tonic-gate return (res); 1200Sstevel@tonic-gate } 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate int 1230Sstevel@tonic-gate files_unlock(void) 1240Sstevel@tonic-gate { 1250Sstevel@tonic-gate if (ulckpwdf()) 1260Sstevel@tonic-gate return (PWU_SYSTEM_ERROR); 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate return (PWU_SUCCESS); 1290Sstevel@tonic-gate } 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate /* 1320Sstevel@tonic-gate * files_privileged 1330Sstevel@tonic-gate * 1340Sstevel@tonic-gate * Are we a privileged user with regard to the files repository? 1350Sstevel@tonic-gate */ 1360Sstevel@tonic-gate int 1370Sstevel@tonic-gate files_privileged(void) 1380Sstevel@tonic-gate { 1390Sstevel@tonic-gate return (getuid() == 0); 1400Sstevel@tonic-gate } 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate /* 1430Sstevel@tonic-gate * 1440Sstevel@tonic-gate * private_getpwnam_r() 1450Sstevel@tonic-gate * 1460Sstevel@tonic-gate * A private implementation of getpwnam_r which does *not* fall back to 1470Sstevel@tonic-gate * other services possibly defined in nsswitch.conf 1480Sstevel@tonic-gate * 1490Sstevel@tonic-gate * behaves like getpwnam_r(). 1500Sstevel@tonic-gate */ 1510Sstevel@tonic-gate struct passwd * 1520Sstevel@tonic-gate private_getpwnam_r(const char *name, struct passwd *result, char *buffer, 1530Sstevel@tonic-gate int buflen) 1540Sstevel@tonic-gate { 1550Sstevel@tonic-gate FILE *fp; 1560Sstevel@tonic-gate int found; 1570Sstevel@tonic-gate 1581914Scasper if ((fp = fopen(PASSWD, "rF")) == NULL) 1590Sstevel@tonic-gate return (NULL); 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate found = 0; 1620Sstevel@tonic-gate while (!found && fgetpwent_r(fp, result, buffer, buflen) != NULL) { 1630Sstevel@tonic-gate if (strcmp(name, result->pw_name) == 0) 1640Sstevel@tonic-gate found = 1; 1650Sstevel@tonic-gate } 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate (void) fclose(fp); 1680Sstevel@tonic-gate 1690Sstevel@tonic-gate if (!found) { 1700Sstevel@tonic-gate (void) memset(buffer, 0, buflen); 1710Sstevel@tonic-gate (void) memset(result, 0, sizeof (*result)); 1720Sstevel@tonic-gate return (NULL); 1730Sstevel@tonic-gate } 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate return (result); 1760Sstevel@tonic-gate } 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate /* 1790Sstevel@tonic-gate * private_getspnam_r() 1800Sstevel@tonic-gate * 1810Sstevel@tonic-gate * A private implementation of getspnam_r which does *not* fall back to 1820Sstevel@tonic-gate * other services possibly defined in nsswitch.conf. 1830Sstevel@tonic-gate * 1840Sstevel@tonic-gate * Behaves like getspnam_r(). Since we use fgetspent_t(), all numeric 1850Sstevel@tonic-gate * fields that are undefined in /etc/shadow will be set to -1. 1860Sstevel@tonic-gate * 1870Sstevel@tonic-gate */ 1880Sstevel@tonic-gate struct spwd * 1890Sstevel@tonic-gate private_getspnam_r(const char *name, struct spwd *result, char *buffer, 1900Sstevel@tonic-gate int buflen) 1910Sstevel@tonic-gate { 1920Sstevel@tonic-gate FILE *fp; 1930Sstevel@tonic-gate int found; 1940Sstevel@tonic-gate 1951914Scasper fp = fopen(SHADOW, "rF"); 1960Sstevel@tonic-gate if (fp == NULL) 1970Sstevel@tonic-gate return (NULL); 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate found = 0; 2000Sstevel@tonic-gate while (!found && fgetspent_r(fp, result, buffer, buflen) != NULL) { 2010Sstevel@tonic-gate if (strcmp(name, result->sp_namp) == 0) 2020Sstevel@tonic-gate found = 1; 2030Sstevel@tonic-gate } 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate (void) fclose(fp); 2060Sstevel@tonic-gate 2070Sstevel@tonic-gate if (!found) { 2080Sstevel@tonic-gate (void) memset(buffer, 0, buflen); 2090Sstevel@tonic-gate (void) memset(result, 0, sizeof (*result)); 2100Sstevel@tonic-gate return (NULL); 2110Sstevel@tonic-gate } 2120Sstevel@tonic-gate return (result); 2130Sstevel@tonic-gate } 2140Sstevel@tonic-gate 2150Sstevel@tonic-gate /* 2160Sstevel@tonic-gate * files_getpwnam(name, items, rep, buf) 2170Sstevel@tonic-gate * 2180Sstevel@tonic-gate */ 2190Sstevel@tonic-gate /*ARGSUSED*/ 2200Sstevel@tonic-gate int 2210Sstevel@tonic-gate files_getpwnam(char *name, attrlist *items, pwu_repository_t *rep, void **buf) 2220Sstevel@tonic-gate { 2230Sstevel@tonic-gate attrlist *p; 2240Sstevel@tonic-gate struct pwbuf *pwbuf; 2250Sstevel@tonic-gate int err = PWU_SUCCESS; 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate *buf = calloc(1, sizeof (struct pwbuf)); 2280Sstevel@tonic-gate pwbuf = (struct pwbuf *)*buf; 2290Sstevel@tonic-gate 2300Sstevel@tonic-gate /* 2310Sstevel@tonic-gate * determine which password structure (/etc/passwd or /etc/shadow) 2320Sstevel@tonic-gate * we need for the items we need to update 2330Sstevel@tonic-gate */ 2340Sstevel@tonic-gate for (p = items; p != NULL; p = p->next) { 2350Sstevel@tonic-gate switch (p->type) { 2360Sstevel@tonic-gate case ATTR_NAME: 2370Sstevel@tonic-gate case ATTR_UID: 2380Sstevel@tonic-gate case ATTR_GID: 2390Sstevel@tonic-gate case ATTR_AGE: 2400Sstevel@tonic-gate case ATTR_COMMENT: 2410Sstevel@tonic-gate case ATTR_GECOS: 2420Sstevel@tonic-gate case ATTR_HOMEDIR: 2430Sstevel@tonic-gate case ATTR_SHELL: 2440Sstevel@tonic-gate if (pwbuf->pwd == NULL) { 2450Sstevel@tonic-gate pwbuf->pwd = malloc(sizeof (struct passwd)); 2460Sstevel@tonic-gate if (pwbuf->pwd == NULL) { 2470Sstevel@tonic-gate err = PWU_NOMEM; 2480Sstevel@tonic-gate goto error; 2490Sstevel@tonic-gate } 2500Sstevel@tonic-gate } 2510Sstevel@tonic-gate break; 2520Sstevel@tonic-gate case ATTR_PASSWD: 2530Sstevel@tonic-gate case ATTR_PASSWD_SERVER_POLICY: 2540Sstevel@tonic-gate case ATTR_LSTCHG: 2550Sstevel@tonic-gate case ATTR_MIN: 2560Sstevel@tonic-gate case ATTR_MAX: 2570Sstevel@tonic-gate case ATTR_WARN: 2580Sstevel@tonic-gate case ATTR_INACT: 2590Sstevel@tonic-gate case ATTR_EXPIRE: 2600Sstevel@tonic-gate case ATTR_FLAG: 2610Sstevel@tonic-gate case ATTR_LOCK_ACCOUNT: 2620Sstevel@tonic-gate case ATTR_EXPIRE_PASSWORD: 2630Sstevel@tonic-gate case ATTR_FAILED_LOGINS: 2640Sstevel@tonic-gate case ATTR_INCR_FAILED_LOGINS: 2650Sstevel@tonic-gate case ATTR_RST_FAILED_LOGINS: 2660Sstevel@tonic-gate case ATTR_NOLOGIN_ACCOUNT: 2670Sstevel@tonic-gate case ATTR_UNLOCK_ACCOUNT: 2680Sstevel@tonic-gate if (pwbuf->spwd == NULL) { 2690Sstevel@tonic-gate pwbuf->spwd = malloc(sizeof (struct spwd)); 2700Sstevel@tonic-gate if (pwbuf->spwd == NULL) { 2710Sstevel@tonic-gate err = PWU_NOMEM; 2720Sstevel@tonic-gate goto error; 2730Sstevel@tonic-gate } 2740Sstevel@tonic-gate } 2750Sstevel@tonic-gate break; 2760Sstevel@tonic-gate default: 2770Sstevel@tonic-gate /* 2780Sstevel@tonic-gate * Some other repository might have different values 2790Sstevel@tonic-gate * so we ignore those. 2800Sstevel@tonic-gate */ 2810Sstevel@tonic-gate break; 2820Sstevel@tonic-gate } 2830Sstevel@tonic-gate } 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate if (pwbuf->pwd) { 2860Sstevel@tonic-gate if ((pwbuf->pwd_scratch = malloc(PWD_SCRATCH_SIZE)) == NULL) { 2870Sstevel@tonic-gate err = PWU_NOMEM; 2880Sstevel@tonic-gate goto error; 2890Sstevel@tonic-gate } 2900Sstevel@tonic-gate if (private_getpwnam_r(name, pwbuf->pwd, pwbuf->pwd_scratch, 2910Sstevel@tonic-gate PWD_SCRATCH_SIZE) == NULL) { 2920Sstevel@tonic-gate err = PWU_NOT_FOUND; 2930Sstevel@tonic-gate goto error; 2940Sstevel@tonic-gate } 2950Sstevel@tonic-gate } 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate if (pwbuf->spwd) { 2980Sstevel@tonic-gate if ((pwbuf->spwd_scratch = malloc(SPW_SCRATCH_SIZE)) == NULL) { 2990Sstevel@tonic-gate err = PWU_NOMEM; 3000Sstevel@tonic-gate goto error; 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate if (private_getspnam_r(name, pwbuf->spwd, pwbuf->spwd_scratch, 3030Sstevel@tonic-gate SPW_SCRATCH_SIZE) == NULL) { 3040Sstevel@tonic-gate err = PWU_NOT_FOUND; 3050Sstevel@tonic-gate goto error; 3060Sstevel@tonic-gate } 3070Sstevel@tonic-gate } 3080Sstevel@tonic-gate 3090Sstevel@tonic-gate return (PWU_SUCCESS); 3100Sstevel@tonic-gate error: 3110Sstevel@tonic-gate if (pwbuf->pwd) free(pwbuf->pwd); 3120Sstevel@tonic-gate if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch); 3130Sstevel@tonic-gate if (pwbuf->spwd) free(pwbuf->spwd); 3140Sstevel@tonic-gate if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch); 3150Sstevel@tonic-gate free(pwbuf); 3160Sstevel@tonic-gate *buf = NULL; 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate return (err); 3190Sstevel@tonic-gate } 3200Sstevel@tonic-gate 3210Sstevel@tonic-gate /* 3220Sstevel@tonic-gate * int files_user_to_authenticate(name, rep, auth_user, privileged) 3230Sstevel@tonic-gate * Determine which user needs to be authenticated. For files, the 3240Sstevel@tonic-gate * possible return values are: 3250Sstevel@tonic-gate * PWU_NOT_FOUND 3260Sstevel@tonic-gate * PWU_SUCCESS and (auth_user == NULL || auth_user = user) 3270Sstevel@tonic-gate * PWU_DENIED 3280Sstevel@tonic-gate */ 3290Sstevel@tonic-gate /*ARGSUSED*/ 3300Sstevel@tonic-gate int 3310Sstevel@tonic-gate files_user_to_authenticate(char *user, pwu_repository_t *rep, 3320Sstevel@tonic-gate char **auth_user, int *privileged) 3330Sstevel@tonic-gate { 3340Sstevel@tonic-gate struct pwbuf *pwbuf; 3350Sstevel@tonic-gate int res; 3360Sstevel@tonic-gate attrlist attr_tmp[1] = { { ATTR_UID, NULL, NULL } }; 3370Sstevel@tonic-gate 3380Sstevel@tonic-gate /* check to see if target user is present in files */ 3390Sstevel@tonic-gate res = files_getpwnam(user, &attr_tmp[0], rep, (void **)&pwbuf); 3400Sstevel@tonic-gate if (res != PWU_SUCCESS) 3410Sstevel@tonic-gate return (res); 3420Sstevel@tonic-gate 3430Sstevel@tonic-gate if (files_privileged()) { 3440Sstevel@tonic-gate *auth_user = NULL; 3450Sstevel@tonic-gate *privileged = 1; 3460Sstevel@tonic-gate res = PWU_SUCCESS; 3470Sstevel@tonic-gate } else { 3480Sstevel@tonic-gate *privileged = 0; 3490Sstevel@tonic-gate if (getuid() == pwbuf->pwd->pw_uid) { 3500Sstevel@tonic-gate *auth_user = strdup(user); 3510Sstevel@tonic-gate res = PWU_SUCCESS; 3520Sstevel@tonic-gate } else { 3530Sstevel@tonic-gate res = PWU_DENIED; 3540Sstevel@tonic-gate } 3550Sstevel@tonic-gate } 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate if (pwbuf->pwd) free(pwbuf->pwd); 3580Sstevel@tonic-gate if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch); 3590Sstevel@tonic-gate if (pwbuf->spwd) free(pwbuf->spwd); 3600Sstevel@tonic-gate if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch); 3610Sstevel@tonic-gate free(pwbuf); 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate return (res); 3640Sstevel@tonic-gate } 3650Sstevel@tonic-gate 3660Sstevel@tonic-gate /* 3670Sstevel@tonic-gate * Password history file format: 3680Sstevel@tonic-gate * user:crypw1: ... crypwn: such that n <= MAXHISTORY 3690Sstevel@tonic-gate */ 3700Sstevel@tonic-gate #define HISTORY "/etc/security/passhistory" 3710Sstevel@tonic-gate #define HISTEMP "/etc/security/pwhistemp" 3720Sstevel@tonic-gate #define OHISTORY "/etc/security/opwhistory" 3730Sstevel@tonic-gate #define HISTMODE S_IRUSR /* mode to create history file */ 3740Sstevel@tonic-gate /* 3750Sstevel@tonic-gate * XXX 3760Sstevel@tonic-gate * 3*LOGNAME_MAX just in case there are long user names. 3770Sstevel@tonic-gate * Traditionally Solaris LOGNAME_MAX (_POSIX_LOGIN_NAME_MAX) is 13, 3780Sstevel@tonic-gate * but some sites often user more. 3790Sstevel@tonic-gate * If LOGNAME_MAX ever becomes reasonable (128) and actually enforced, 3800Sstevel@tonic-gate * fix up here. 3810Sstevel@tonic-gate * XXX 3820Sstevel@tonic-gate */ 3830Sstevel@tonic-gate #define MAX_LOGNAME (3 * LOGNAME_MAX) 3840Sstevel@tonic-gate 3850Sstevel@tonic-gate /* 3860Sstevel@tonic-gate * files_checkhistory - check if a user's new password is in the user's 3870Sstevel@tonic-gate * old password history. 3880Sstevel@tonic-gate * 3890Sstevel@tonic-gate * Entry 3900Sstevel@tonic-gate * user = username. 3910Sstevel@tonic-gate * passwd = new clear text password. 3920Sstevel@tonic-gate * 3930Sstevel@tonic-gate * Exit 3940Sstevel@tonic-gate * PWU_SUCCESS, passwd found in user's old password history. 3950Sstevel@tonic-gate * The caller should only be interested and fail if 3960Sstevel@tonic-gate * PWU_SUCCESS is returned. 3970Sstevel@tonic-gate * PWU_NOT_FOUND, passwd not in user's old password history. 3980Sstevel@tonic-gate * PWU_errors, PWU_ errors from other routines. 3990Sstevel@tonic-gate * 4000Sstevel@tonic-gate */ 4010Sstevel@tonic-gate int 4020Sstevel@tonic-gate files_checkhistory(char *user, char *passwd, pwu_repository_t *rep) 4030Sstevel@tonic-gate { 4040Sstevel@tonic-gate attrlist attr; 4050Sstevel@tonic-gate int res; 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate attr.type = ATTR_HISTORY; 4080Sstevel@tonic-gate attr.data.val_s = NULL; 4090Sstevel@tonic-gate attr.next = NULL; 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate debug("files_checkhistory(user=%s)", user); 4120Sstevel@tonic-gate 4130Sstevel@tonic-gate /* 4140Sstevel@tonic-gate * XXX 4150Sstevel@tonic-gate * This depends on the underlying files_getattr implementation 4160Sstevel@tonic-gate * treating user not found in backing store or no history as 4170Sstevel@tonic-gate * an error. 4180Sstevel@tonic-gate * XXX 4190Sstevel@tonic-gate */ 4200Sstevel@tonic-gate 4210Sstevel@tonic-gate if ((res = files_getattr(user, &attr, rep)) == PWU_SUCCESS) { 4220Sstevel@tonic-gate char *s; 4230Sstevel@tonic-gate char *crypt_passwd; 4240Sstevel@tonic-gate int histsize; 4250Sstevel@tonic-gate char *last = attr.data.val_s; 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate if ((histsize = def_getint("HISTORY=", DEFHISTORY)) == 0) { 4280Sstevel@tonic-gate debug("files_checkhistory: no history requested"); 4290Sstevel@tonic-gate res = PWU_NOT_FOUND; 4300Sstevel@tonic-gate goto out; 4310Sstevel@tonic-gate } 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate debug("files_checkhistory: histsize = %d", histsize); 4340Sstevel@tonic-gate if (histsize > MAXHISTORY) 4350Sstevel@tonic-gate histsize = MAXHISTORY; 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate debug("line to test\n\t%s", last); 4380Sstevel@tonic-gate 4390Sstevel@tonic-gate /* compare crypt_passwd to attr.data.val_s strings. */ 4400Sstevel@tonic-gate res = PWU_NOT_FOUND; 4410Sstevel@tonic-gate while ((histsize-- > 0) && 4420Sstevel@tonic-gate (((s = strtok_r(NULL, ":", &last)) != NULL) && 4430Sstevel@tonic-gate (*s != '\n'))) { 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate crypt_passwd = crypt(passwd, s); 4460Sstevel@tonic-gate debug("files_checkhistory: user_pw=%s, history_pw=%s", 4470Sstevel@tonic-gate crypt_passwd, s); 4480Sstevel@tonic-gate if (strcmp(crypt_passwd, s) == 0) { 4490Sstevel@tonic-gate res = PWU_SUCCESS; 4500Sstevel@tonic-gate break; 4510Sstevel@tonic-gate } 4520Sstevel@tonic-gate } 4530Sstevel@tonic-gate debug("files_checkhistory(%s, %s) = %d", user, crypt_passwd, 4540Sstevel@tonic-gate res); 4550Sstevel@tonic-gate } 4560Sstevel@tonic-gate out: 4570Sstevel@tonic-gate if (attr.data.val_s != NULL) 4580Sstevel@tonic-gate free(attr.data.val_s); 4590Sstevel@tonic-gate 4600Sstevel@tonic-gate return (res); 4610Sstevel@tonic-gate } 4620Sstevel@tonic-gate 4630Sstevel@tonic-gate /* 4640Sstevel@tonic-gate * files_getattr(name, items, rep) 4650Sstevel@tonic-gate * 4660Sstevel@tonic-gate * Get attributes specified in list 'items' 4670Sstevel@tonic-gate */ 4680Sstevel@tonic-gate int 4690Sstevel@tonic-gate files_getattr(char *name, attrlist *items, pwu_repository_t *rep) 4700Sstevel@tonic-gate { 4710Sstevel@tonic-gate struct pwbuf *pwbuf; 4720Sstevel@tonic-gate struct passwd *pw; 4730Sstevel@tonic-gate struct spwd *spw; 4740Sstevel@tonic-gate attrlist *w; 4750Sstevel@tonic-gate int res; 4760Sstevel@tonic-gate 4770Sstevel@tonic-gate res = files_getpwnam(name, items, rep, (void **)&pwbuf); 4780Sstevel@tonic-gate if (res != PWU_SUCCESS) 4790Sstevel@tonic-gate return (res); 4800Sstevel@tonic-gate 4810Sstevel@tonic-gate pw = pwbuf->pwd; 4820Sstevel@tonic-gate spw = pwbuf->spwd; 4830Sstevel@tonic-gate 4840Sstevel@tonic-gate for (w = items; res == PWU_SUCCESS && w != NULL; w = w->next) { 4850Sstevel@tonic-gate switch (w->type) { 4860Sstevel@tonic-gate case ATTR_NAME: 4870Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_name)) == NULL) 4880Sstevel@tonic-gate res = PWU_NOMEM; 4890Sstevel@tonic-gate break; 4900Sstevel@tonic-gate case ATTR_COMMENT: 4910Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_comment)) == NULL) 4920Sstevel@tonic-gate res = PWU_NOMEM; 4930Sstevel@tonic-gate break; 4940Sstevel@tonic-gate case ATTR_GECOS: 4950Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL) 4960Sstevel@tonic-gate res = PWU_NOMEM; 4970Sstevel@tonic-gate break; 4980Sstevel@tonic-gate case ATTR_HOMEDIR: 4990Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_dir)) == NULL) 5000Sstevel@tonic-gate res = PWU_NOMEM; 5010Sstevel@tonic-gate break; 5020Sstevel@tonic-gate case ATTR_SHELL: 5030Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_shell)) == NULL) 5040Sstevel@tonic-gate res = PWU_NOMEM; 5050Sstevel@tonic-gate break; 5060Sstevel@tonic-gate /* 5070Sstevel@tonic-gate * Nothing special needs to be done for 5080Sstevel@tonic-gate * server policy 5090Sstevel@tonic-gate */ 5100Sstevel@tonic-gate case ATTR_PASSWD: 5110Sstevel@tonic-gate case ATTR_PASSWD_SERVER_POLICY: 5120Sstevel@tonic-gate if ((w->data.val_s = strdup(spw->sp_pwdp)) == NULL) 5130Sstevel@tonic-gate res = PWU_NOMEM; 5140Sstevel@tonic-gate break; 5150Sstevel@tonic-gate case ATTR_AGE: 5160Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_age)) == NULL) 5170Sstevel@tonic-gate res = PWU_NOMEM; 5180Sstevel@tonic-gate break; 5190Sstevel@tonic-gate case ATTR_REP_NAME: 5200Sstevel@tonic-gate if ((w->data.val_s = strdup("files")) == NULL) 5210Sstevel@tonic-gate res = PWU_NOMEM; 5220Sstevel@tonic-gate break; 5230Sstevel@tonic-gate case ATTR_HISTORY: { 5240Sstevel@tonic-gate FILE *history; 5250Sstevel@tonic-gate char buf[MAX_LOGNAME + MAXHISTORY + 5260Sstevel@tonic-gate (MAXHISTORY * CRYPT_MAXCIPHERTEXTLEN)+1]; 5270Sstevel@tonic-gate char *s, *s1; 5280Sstevel@tonic-gate 5290Sstevel@tonic-gate debug("files_getattr: Get password history for %s ", 5300Sstevel@tonic-gate name); 5310Sstevel@tonic-gate 5321914Scasper if ((history = fopen(HISTORY, "rF")) == NULL) { 5330Sstevel@tonic-gate debug("files_getattr: %s not found", HISTORY); 5340Sstevel@tonic-gate res = PWU_OPEN_FAILED; 5350Sstevel@tonic-gate goto getattr_exit; 5360Sstevel@tonic-gate } 5370Sstevel@tonic-gate res = PWU_NOT_FOUND; 5380Sstevel@tonic-gate while ((s = fgets(buf, sizeof (buf), history)) != 5390Sstevel@tonic-gate NULL) { 5400Sstevel@tonic-gate s1 = strchr(s, ':'); 5410Sstevel@tonic-gate if (s1 != NULL) { 5420Sstevel@tonic-gate *s1 = '\0'; 5430Sstevel@tonic-gate } else { 5440Sstevel@tonic-gate res = PWU_NOT_FOUND; 5450Sstevel@tonic-gate break; 5460Sstevel@tonic-gate } 5470Sstevel@tonic-gate #ifdef DEBUG 5480Sstevel@tonic-gate debug("got history line for %s", s); 5490Sstevel@tonic-gate #endif /* DEBUG */ 5500Sstevel@tonic-gate if (strcmp(s, name) == 0) { 5510Sstevel@tonic-gate /* found user */ 5520Sstevel@tonic-gate if ((items->data.val_s = 5530Sstevel@tonic-gate strdup(s1+1)) == NULL) 5540Sstevel@tonic-gate res = PWU_NOMEM; 5550Sstevel@tonic-gate else 5560Sstevel@tonic-gate res = PWU_SUCCESS; 5570Sstevel@tonic-gate break; 5580Sstevel@tonic-gate } 5590Sstevel@tonic-gate } 5600Sstevel@tonic-gate (void) fclose(history); 5610Sstevel@tonic-gate break; 5620Sstevel@tonic-gate } 5630Sstevel@tonic-gate 5640Sstevel@tonic-gate /* integer values */ 5650Sstevel@tonic-gate case ATTR_UID: 5660Sstevel@tonic-gate w->data.val_i = pw->pw_uid; 5670Sstevel@tonic-gate break; 5680Sstevel@tonic-gate case ATTR_GID: 5690Sstevel@tonic-gate w->data.val_i = pw->pw_gid; 5700Sstevel@tonic-gate break; 5710Sstevel@tonic-gate case ATTR_LSTCHG: 5720Sstevel@tonic-gate w->data.val_i = spw->sp_lstchg; 5730Sstevel@tonic-gate break; 5740Sstevel@tonic-gate case ATTR_MIN: 5750Sstevel@tonic-gate w->data.val_i = spw->sp_min; 5760Sstevel@tonic-gate break; 5770Sstevel@tonic-gate case ATTR_MAX: 5780Sstevel@tonic-gate w->data.val_i = spw->sp_max; 5790Sstevel@tonic-gate break; 5800Sstevel@tonic-gate case ATTR_WARN: 5810Sstevel@tonic-gate w->data.val_i = spw->sp_warn; 5820Sstevel@tonic-gate break; 5830Sstevel@tonic-gate case ATTR_INACT: 5840Sstevel@tonic-gate w->data.val_i = spw->sp_inact; 5850Sstevel@tonic-gate break; 5860Sstevel@tonic-gate case ATTR_EXPIRE: 5870Sstevel@tonic-gate w->data.val_i = spw->sp_expire; 5880Sstevel@tonic-gate break; 5890Sstevel@tonic-gate case ATTR_FLAG: 5900Sstevel@tonic-gate w->data.val_i = spw->sp_flag; 5910Sstevel@tonic-gate break; 5920Sstevel@tonic-gate case ATTR_FAILED_LOGINS: 5930Sstevel@tonic-gate w->data.val_i = spw->sp_flag & FAILCOUNT_MASK; 5940Sstevel@tonic-gate break; 5950Sstevel@tonic-gate default: 5960Sstevel@tonic-gate break; 5970Sstevel@tonic-gate } 5980Sstevel@tonic-gate } 5990Sstevel@tonic-gate 6000Sstevel@tonic-gate getattr_exit: 6010Sstevel@tonic-gate if (pwbuf->pwd) free(pwbuf->pwd); 6020Sstevel@tonic-gate if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch); 6030Sstevel@tonic-gate if (pwbuf->spwd) free(pwbuf->spwd); 604*2279Sps57422 if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch); 6050Sstevel@tonic-gate free(pwbuf); 6060Sstevel@tonic-gate 6070Sstevel@tonic-gate return (res); 6080Sstevel@tonic-gate } 6090Sstevel@tonic-gate 6100Sstevel@tonic-gate /* 6110Sstevel@tonic-gate * max_present(list) 6120Sstevel@tonic-gate * 6130Sstevel@tonic-gate * see if attribute ATTR_MAX, with value != -1, is present in 6140Sstevel@tonic-gate * attribute-list "list". 6150Sstevel@tonic-gate * 6160Sstevel@tonic-gate * returns 1 if present, 0 otherwise. 6170Sstevel@tonic-gate */ 6180Sstevel@tonic-gate static int 6190Sstevel@tonic-gate max_present(attrlist *list) 6200Sstevel@tonic-gate { 6210Sstevel@tonic-gate while (list != NULL) 6220Sstevel@tonic-gate if (list->type == ATTR_MAX && list->data.val_i != -1) 6230Sstevel@tonic-gate return (1); 6240Sstevel@tonic-gate else 6250Sstevel@tonic-gate list = list->next; 6260Sstevel@tonic-gate 6270Sstevel@tonic-gate return (0); 6280Sstevel@tonic-gate } 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate /* 6310Sstevel@tonic-gate * files_update(items, rep, buf) 6320Sstevel@tonic-gate * 6330Sstevel@tonic-gate * update the information in buf with the attributes specified in 6340Sstevel@tonic-gate * items. 6350Sstevel@tonic-gate */ 6360Sstevel@tonic-gate /*ARGSUSED*/ 6370Sstevel@tonic-gate int 6380Sstevel@tonic-gate files_update(attrlist *items, pwu_repository_t *rep, void *buf) 6390Sstevel@tonic-gate { 6400Sstevel@tonic-gate struct pwbuf *pwbuf = (struct pwbuf *)buf; 6410Sstevel@tonic-gate struct passwd *pw; 6420Sstevel@tonic-gate struct spwd *spw; 6430Sstevel@tonic-gate attrlist *p; 6440Sstevel@tonic-gate int aging_needed = 0; 6450Sstevel@tonic-gate int aging_set = 0; 6460Sstevel@tonic-gate int disable_aging; 6470Sstevel@tonic-gate char *pword; 6480Sstevel@tonic-gate int len; 6490Sstevel@tonic-gate 6500Sstevel@tonic-gate pw = pwbuf->pwd; 6510Sstevel@tonic-gate spw = pwbuf->spwd; 6520Sstevel@tonic-gate pwbuf->update_history = 0; 6530Sstevel@tonic-gate 6540Sstevel@tonic-gate /* 6550Sstevel@tonic-gate * if sp_max==0 : disable passwd aging after updating the password 6560Sstevel@tonic-gate */ 6570Sstevel@tonic-gate disable_aging = (spw != NULL && spw->sp_max == 0); 6580Sstevel@tonic-gate 6590Sstevel@tonic-gate for (p = items; p != NULL; p = p->next) { 6600Sstevel@tonic-gate switch (p->type) { 6610Sstevel@tonic-gate case ATTR_NAME: 6620Sstevel@tonic-gate break; /* We are able to handle this, but... */ 6630Sstevel@tonic-gate case ATTR_UID: 6640Sstevel@tonic-gate pw->pw_uid = (uid_t)p->data.val_i; 6650Sstevel@tonic-gate break; 6660Sstevel@tonic-gate case ATTR_GID: 6670Sstevel@tonic-gate pw->pw_gid = (gid_t)p->data.val_i; 6680Sstevel@tonic-gate break; 6690Sstevel@tonic-gate case ATTR_AGE: 6700Sstevel@tonic-gate pw->pw_age = p->data.val_s; 6710Sstevel@tonic-gate break; 6720Sstevel@tonic-gate case ATTR_COMMENT: 6730Sstevel@tonic-gate pw->pw_comment = p->data.val_s; 6740Sstevel@tonic-gate break; 6750Sstevel@tonic-gate case ATTR_GECOS: 6760Sstevel@tonic-gate pw->pw_gecos = p->data.val_s; 6770Sstevel@tonic-gate break; 6780Sstevel@tonic-gate case ATTR_HOMEDIR: 6790Sstevel@tonic-gate pw->pw_dir = p->data.val_s; 6800Sstevel@tonic-gate break; 6810Sstevel@tonic-gate case ATTR_SHELL: 6820Sstevel@tonic-gate pw->pw_shell = p->data.val_s; 6830Sstevel@tonic-gate break; 6840Sstevel@tonic-gate 6850Sstevel@tonic-gate /* 6860Sstevel@tonic-gate * Nothing special needs to be done for 6870Sstevel@tonic-gate * server policy 6880Sstevel@tonic-gate */ 6890Sstevel@tonic-gate case ATTR_PASSWD: 6900Sstevel@tonic-gate case ATTR_PASSWD_SERVER_POLICY: 6910Sstevel@tonic-gate /* 6920Sstevel@tonic-gate * There is a special case only for files: if the 6930Sstevel@tonic-gate * password is to be deleted (-d to passwd), 6940Sstevel@tonic-gate * p->data.val_s will be NULL. 6950Sstevel@tonic-gate */ 6960Sstevel@tonic-gate if (p->data.val_s == NULL) { 6970Sstevel@tonic-gate spw->sp_pwdp = ""; 6980Sstevel@tonic-gate } else { 6990Sstevel@tonic-gate char *salt = NULL; 7000Sstevel@tonic-gate char *hash = NULL; 7010Sstevel@tonic-gate 7020Sstevel@tonic-gate salt = crypt_gensalt(spw->sp_pwdp, pw); 7030Sstevel@tonic-gate 7040Sstevel@tonic-gate if (salt == NULL) { 7050Sstevel@tonic-gate if (errno == ENOMEM) 7060Sstevel@tonic-gate return (PWU_NOMEM); 7070Sstevel@tonic-gate /* algorithm problem? */ 7080Sstevel@tonic-gate syslog(LOG_AUTH | LOG_ALERT, 7090Sstevel@tonic-gate "passwdutil: crypt_gensalt %m"); 7100Sstevel@tonic-gate return (PWU_UPDATE_FAILED); 7110Sstevel@tonic-gate } 7120Sstevel@tonic-gate hash = crypt(p->data.val_s, salt); 7130Sstevel@tonic-gate free(salt); 7140Sstevel@tonic-gate if (hash == NULL) { 7150Sstevel@tonic-gate errno = ENOMEM; 7160Sstevel@tonic-gate return (PWU_NOMEM); 7170Sstevel@tonic-gate } 7180Sstevel@tonic-gate pword = strdup(hash); 7190Sstevel@tonic-gate free(hash); 7200Sstevel@tonic-gate if (pword == NULL) { 7210Sstevel@tonic-gate errno = ENOMEM; 7220Sstevel@tonic-gate return (PWU_NOMEM); 7230Sstevel@tonic-gate } 7240Sstevel@tonic-gate 7250Sstevel@tonic-gate if (pwbuf->new_sp_pwdp) 7260Sstevel@tonic-gate free(pwbuf->new_sp_pwdp); 7270Sstevel@tonic-gate pwbuf->new_sp_pwdp = pword; 7280Sstevel@tonic-gate spw->sp_pwdp = pword; 7290Sstevel@tonic-gate aging_needed = 1; 7300Sstevel@tonic-gate pwbuf->update_history = 1; 7310Sstevel@tonic-gate } 7320Sstevel@tonic-gate spw->sp_flag &= ~FAILCOUNT_MASK; /* reset count */ 7330Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 7340Sstevel@tonic-gate break; 7350Sstevel@tonic-gate case ATTR_LOCK_ACCOUNT: 7360Sstevel@tonic-gate if (spw->sp_pwdp == NULL) { 7370Sstevel@tonic-gate spw->sp_pwdp = LOCKSTRING; 7380Sstevel@tonic-gate } else if (strncmp(spw->sp_pwdp, LOCKSTRING, 7390Sstevel@tonic-gate sizeof (LOCKSTRING)-1) != 0) { 7400Sstevel@tonic-gate len = sizeof (LOCKSTRING)-1 + 7410Sstevel@tonic-gate strlen(spw->sp_pwdp) + 1; 7420Sstevel@tonic-gate pword = malloc(len); 7430Sstevel@tonic-gate if (pword == NULL) { 7440Sstevel@tonic-gate errno = ENOMEM; 7450Sstevel@tonic-gate return (PWU_NOMEM); 7460Sstevel@tonic-gate } 7470Sstevel@tonic-gate (void) strlcpy(pword, LOCKSTRING, len); 7480Sstevel@tonic-gate (void) strlcat(pword, spw->sp_pwdp, len); 7490Sstevel@tonic-gate if (pwbuf->new_sp_pwdp) 7500Sstevel@tonic-gate free(pwbuf->new_sp_pwdp); 7510Sstevel@tonic-gate pwbuf->new_sp_pwdp = pword; 7520Sstevel@tonic-gate spw->sp_pwdp = pword; 7530Sstevel@tonic-gate } 7540Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 7550Sstevel@tonic-gate break; 7560Sstevel@tonic-gate case ATTR_UNLOCK_ACCOUNT: 7570Sstevel@tonic-gate if (spw->sp_pwdp != NULL && 7580Sstevel@tonic-gate strncmp(spw->sp_pwdp, LOCKSTRING, 7590Sstevel@tonic-gate sizeof (LOCKSTRING)-1) == 0) { 7600Sstevel@tonic-gate (void) strcpy(spw->sp_pwdp, spw->sp_pwdp + 7610Sstevel@tonic-gate sizeof (LOCKSTRING)-1); 7620Sstevel@tonic-gate } 7630Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 7640Sstevel@tonic-gate break; 7650Sstevel@tonic-gate case ATTR_NOLOGIN_ACCOUNT: 7660Sstevel@tonic-gate spw->sp_pwdp = NOLOGINSTRING; 7670Sstevel@tonic-gate if (pwbuf->new_sp_pwdp) { 7680Sstevel@tonic-gate free(pwbuf->new_sp_pwdp); 7690Sstevel@tonic-gate pwbuf->new_sp_pwdp = NULL; 7700Sstevel@tonic-gate } 7710Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 7720Sstevel@tonic-gate break; 7730Sstevel@tonic-gate case ATTR_EXPIRE_PASSWORD: 7740Sstevel@tonic-gate spw->sp_lstchg = 0; 7750Sstevel@tonic-gate break; 7760Sstevel@tonic-gate case ATTR_LSTCHG: 7770Sstevel@tonic-gate spw->sp_lstchg = p->data.val_i; 7780Sstevel@tonic-gate break; 7790Sstevel@tonic-gate case ATTR_MIN: 7800Sstevel@tonic-gate if (spw->sp_max == -1 && 7810Sstevel@tonic-gate p->data.val_i != -1 && max_present(p->next) == 0) 7820Sstevel@tonic-gate return (PWU_AGING_DISABLED); 7830Sstevel@tonic-gate spw->sp_min = p->data.val_i; 7840Sstevel@tonic-gate aging_set = 1; 7850Sstevel@tonic-gate break; 7860Sstevel@tonic-gate case ATTR_MAX: 7870Sstevel@tonic-gate if (p->data.val_i == -1) { 7880Sstevel@tonic-gate /* Turn aging off -> Reset min and warn too */ 7890Sstevel@tonic-gate 7900Sstevel@tonic-gate spw->sp_min = -1; 7910Sstevel@tonic-gate spw->sp_warn = -1; 7920Sstevel@tonic-gate } else { 7930Sstevel@tonic-gate /* Turn aging on */ 7940Sstevel@tonic-gate 7950Sstevel@tonic-gate if (spw->sp_min == -1) { 7960Sstevel@tonic-gate /* 7970Sstevel@tonic-gate * If minage has not been set with 7980Sstevel@tonic-gate * a command-line option, we set it 7990Sstevel@tonic-gate * to zero. 8000Sstevel@tonic-gate */ 8010Sstevel@tonic-gate spw->sp_min = 0; 8020Sstevel@tonic-gate } 8030Sstevel@tonic-gate 8040Sstevel@tonic-gate /* 8050Sstevel@tonic-gate * If aging was turned off, we update lstchg. 8060Sstevel@tonic-gate * 8070Sstevel@tonic-gate * We take care not to update lstchg if the 8080Sstevel@tonic-gate * user has no password, otherwise the user 8090Sstevel@tonic-gate * might not be required to provide a password 8100Sstevel@tonic-gate * the next time [s]he logs-in. 8110Sstevel@tonic-gate * 8120Sstevel@tonic-gate * Also, if lstchg != -1 (i.e., not set in 8130Sstevel@tonic-gate * /etc/shadow), we keep the old value. 8140Sstevel@tonic-gate */ 8150Sstevel@tonic-gate if (spw->sp_max == -1 && 8160Sstevel@tonic-gate spw->sp_pwdp != NULL && *spw->sp_pwdp && 8170Sstevel@tonic-gate spw->sp_lstchg == -1) { 8180Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 8190Sstevel@tonic-gate } 8200Sstevel@tonic-gate } 8210Sstevel@tonic-gate 8220Sstevel@tonic-gate spw->sp_max = p->data.val_i; 8230Sstevel@tonic-gate 8240Sstevel@tonic-gate aging_set = 1; 8250Sstevel@tonic-gate 8260Sstevel@tonic-gate break; 8270Sstevel@tonic-gate case ATTR_WARN: 8280Sstevel@tonic-gate if (spw->sp_max == -1 && p->data.val_i != -1 && 8290Sstevel@tonic-gate max_present(p->next) == 0) 8300Sstevel@tonic-gate return (PWU_AGING_DISABLED); 8310Sstevel@tonic-gate spw->sp_warn = p->data.val_i; 8320Sstevel@tonic-gate break; 8330Sstevel@tonic-gate case ATTR_INACT: 8340Sstevel@tonic-gate spw->sp_inact = p->data.val_i; 8350Sstevel@tonic-gate break; 8360Sstevel@tonic-gate case ATTR_EXPIRE: 8370Sstevel@tonic-gate spw->sp_expire = p->data.val_i; 8380Sstevel@tonic-gate break; 8390Sstevel@tonic-gate case ATTR_FLAG: 8400Sstevel@tonic-gate spw->sp_flag = p->data.val_i; 8410Sstevel@tonic-gate break; 8420Sstevel@tonic-gate case ATTR_INCR_FAILED_LOGINS: 8430Sstevel@tonic-gate { 8440Sstevel@tonic-gate int count = (spw->sp_flag & FAILCOUNT_MASK) + 1; 8450Sstevel@tonic-gate spw->sp_flag &= ~FAILCOUNT_MASK; 8460Sstevel@tonic-gate spw->sp_flag |= min(FAILCOUNT_MASK, count); 8470Sstevel@tonic-gate p->data.val_i = count; 8480Sstevel@tonic-gate } 8490Sstevel@tonic-gate break; 8500Sstevel@tonic-gate case ATTR_RST_FAILED_LOGINS: 8510Sstevel@tonic-gate p->data.val_i = spw->sp_flag & FAILCOUNT_MASK; 8520Sstevel@tonic-gate spw->sp_flag &= ~FAILCOUNT_MASK; 8530Sstevel@tonic-gate break; 8540Sstevel@tonic-gate default: 8550Sstevel@tonic-gate break; 8560Sstevel@tonic-gate } 8570Sstevel@tonic-gate } 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate /* 8600Sstevel@tonic-gate * What should the new aging values look like? 8610Sstevel@tonic-gate * 8620Sstevel@tonic-gate * There are a number of different conditions 8630Sstevel@tonic-gate * 8640Sstevel@tonic-gate * a) aging is already configured: don't touch it 8650Sstevel@tonic-gate * 8660Sstevel@tonic-gate * b) disable_aging is set: disable aging 8670Sstevel@tonic-gate * 8680Sstevel@tonic-gate * c) aging is not configured: turn on default aging; 8690Sstevel@tonic-gate * 8700Sstevel@tonic-gate * b) and c) of course only if aging_needed and !aging_set. 8710Sstevel@tonic-gate * (i.e., password changed, and aging values not changed) 8720Sstevel@tonic-gate */ 8730Sstevel@tonic-gate 8740Sstevel@tonic-gate if (spw != NULL && spw->sp_max <= 0) { 8750Sstevel@tonic-gate /* a) aging not yet configured */ 8760Sstevel@tonic-gate if (aging_needed && !aging_set) { 8770Sstevel@tonic-gate if (disable_aging) { 8780Sstevel@tonic-gate /* b) turn off aging */ 8790Sstevel@tonic-gate spw->sp_min = spw->sp_max = spw->sp_warn = -1; 8800Sstevel@tonic-gate } else { 8810Sstevel@tonic-gate /* c) */ 8820Sstevel@tonic-gate turn_on_default_aging(spw); 8830Sstevel@tonic-gate } 8840Sstevel@tonic-gate } 8850Sstevel@tonic-gate } 8860Sstevel@tonic-gate 8870Sstevel@tonic-gate return (PWU_SUCCESS); 8880Sstevel@tonic-gate } 8890Sstevel@tonic-gate 8900Sstevel@tonic-gate /* 8910Sstevel@tonic-gate * files_update_shadow(char *name, struct spwd *spwd) 8920Sstevel@tonic-gate * 8930Sstevel@tonic-gate * update the shadow password file SHADOW to contain the spwd structure 8940Sstevel@tonic-gate * "spwd" for user "name" 8950Sstevel@tonic-gate */ 8960Sstevel@tonic-gate int 8970Sstevel@tonic-gate files_update_shadow(char *name, struct spwd *spwd) 8980Sstevel@tonic-gate { 8990Sstevel@tonic-gate struct stat64 stbuf; 9000Sstevel@tonic-gate FILE *dst; 9010Sstevel@tonic-gate FILE *src; 9020Sstevel@tonic-gate struct spwd cur; 9030Sstevel@tonic-gate char buf[SPW_SCRATCH_SIZE]; 9040Sstevel@tonic-gate int tempfd; 9050Sstevel@tonic-gate mode_t filemode; 9060Sstevel@tonic-gate int result = -1; 9070Sstevel@tonic-gate int err = PWU_SUCCESS; 9080Sstevel@tonic-gate 9090Sstevel@tonic-gate /* Mode of the shadow file should be 400 or 000 */ 9100Sstevel@tonic-gate if (stat64(SHADOW, &stbuf) < 0) { 9110Sstevel@tonic-gate err = PWU_STAT_FAILED; 9120Sstevel@tonic-gate goto shadow_exit; 9130Sstevel@tonic-gate } 9140Sstevel@tonic-gate 9150Sstevel@tonic-gate /* copy mode from current shadow file (0400 or 0000) */ 9160Sstevel@tonic-gate filemode = stbuf.st_mode & S_IRUSR; 9170Sstevel@tonic-gate 9180Sstevel@tonic-gate /* 9190Sstevel@tonic-gate * we can't specify filemodes to fopen(), and we SHOULD NOT 9200Sstevel@tonic-gate * set umask in multi-thread safe libraries, so we use 9210Sstevel@tonic-gate * a combination of open() and fdopen() 9220Sstevel@tonic-gate */ 9230Sstevel@tonic-gate tempfd = open(SHADTEMP, O_WRONLY|O_CREAT|O_TRUNC, filemode); 9240Sstevel@tonic-gate if (tempfd < 0) { 9250Sstevel@tonic-gate err = PWU_OPEN_FAILED; 9260Sstevel@tonic-gate goto shadow_exit; 9270Sstevel@tonic-gate } 9280Sstevel@tonic-gate (void) fchown(tempfd, (uid_t)0, stbuf.st_gid); 9290Sstevel@tonic-gate 9301914Scasper if ((dst = fdopen(tempfd, "wF")) == NULL) { 9310Sstevel@tonic-gate err = PWU_OPEN_FAILED; 9320Sstevel@tonic-gate goto shadow_exit; 9330Sstevel@tonic-gate } 9340Sstevel@tonic-gate 9351914Scasper if ((src = fopen(SHADOW, "rF")) == NULL) { 9360Sstevel@tonic-gate err = PWU_OPEN_FAILED; 9370Sstevel@tonic-gate (void) fclose(dst); 9380Sstevel@tonic-gate (void) unlink(SHADTEMP); 9390Sstevel@tonic-gate goto shadow_exit; 9400Sstevel@tonic-gate } 9410Sstevel@tonic-gate 9420Sstevel@tonic-gate /* 9430Sstevel@tonic-gate * copy old shadow to temporary file while replacing the entry 9440Sstevel@tonic-gate * that matches "name". 9450Sstevel@tonic-gate */ 9460Sstevel@tonic-gate while (fgetspent_r(src, &cur, buf, sizeof (buf)) != NULL) { 9470Sstevel@tonic-gate 9480Sstevel@tonic-gate if (strcmp(cur.sp_namp, name) == 0) 9490Sstevel@tonic-gate result = putspent(spwd, dst); 9500Sstevel@tonic-gate else 9510Sstevel@tonic-gate result = putspent(&cur, dst); 9520Sstevel@tonic-gate 9530Sstevel@tonic-gate if (result != 0) { 9540Sstevel@tonic-gate err = PWU_WRITE_FAILED; 9550Sstevel@tonic-gate (void) fclose(src); 9560Sstevel@tonic-gate (void) fclose(dst); 9570Sstevel@tonic-gate goto shadow_exit; 9580Sstevel@tonic-gate } 9590Sstevel@tonic-gate } 9600Sstevel@tonic-gate 9610Sstevel@tonic-gate (void) fclose(src); 9620Sstevel@tonic-gate 9630Sstevel@tonic-gate if (fclose(dst) != 0) { 9640Sstevel@tonic-gate /* 9650Sstevel@tonic-gate * Something went wrong (ENOSPC for example). Don't 9660Sstevel@tonic-gate * use the resulting temporary file! 9670Sstevel@tonic-gate */ 9680Sstevel@tonic-gate err = PWU_CLOSE_FAILED; 9690Sstevel@tonic-gate (void) unlink(SHADTEMP); 9700Sstevel@tonic-gate goto shadow_exit; 9710Sstevel@tonic-gate } 9720Sstevel@tonic-gate 9730Sstevel@tonic-gate /* 9740Sstevel@tonic-gate * Rename stmp to shadow: 9750Sstevel@tonic-gate * 1. make sure /etc/oshadow is gone 9760Sstevel@tonic-gate * 2. ln /etc/shadow /etc/oshadow 9770Sstevel@tonic-gate * 3. mv /etc/stmp /etc/shadow 9780Sstevel@tonic-gate */ 9790Sstevel@tonic-gate if (unlink(OSHADOW) && access(OSHADOW, 0) == 0) { 9800Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 9810Sstevel@tonic-gate (void) unlink(SHADTEMP); 9820Sstevel@tonic-gate goto shadow_exit; 9830Sstevel@tonic-gate } 9840Sstevel@tonic-gate 9850Sstevel@tonic-gate if (link(SHADOW, OSHADOW) == -1) { 9860Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 9870Sstevel@tonic-gate (void) unlink(SHADTEMP); 9880Sstevel@tonic-gate goto shadow_exit; 9890Sstevel@tonic-gate } 9900Sstevel@tonic-gate 9910Sstevel@tonic-gate if (rename(SHADTEMP, SHADOW) == -1) { 9920Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 9930Sstevel@tonic-gate (void) unlink(SHADTEMP); 9940Sstevel@tonic-gate goto shadow_exit; 9950Sstevel@tonic-gate } 9960Sstevel@tonic-gate (void) unlink(OSHADOW); 9970Sstevel@tonic-gate 9980Sstevel@tonic-gate shadow_exit: 9990Sstevel@tonic-gate return (err); 10000Sstevel@tonic-gate } 10010Sstevel@tonic-gate 10020Sstevel@tonic-gate int 10030Sstevel@tonic-gate files_update_passwd(char *name, struct passwd *pwd) 10040Sstevel@tonic-gate { 10050Sstevel@tonic-gate struct stat64 stbuf; 10060Sstevel@tonic-gate FILE *src, *dst; 10070Sstevel@tonic-gate int tempfd; 10080Sstevel@tonic-gate struct passwd cur; 10090Sstevel@tonic-gate char buf[PWD_SCRATCH_SIZE]; 10100Sstevel@tonic-gate int result; 10110Sstevel@tonic-gate int err = PWU_SUCCESS; 10120Sstevel@tonic-gate 10130Sstevel@tonic-gate if (stat64(PASSWD, &stbuf) < 0) { 10140Sstevel@tonic-gate err = PWU_STAT_FAILED; 10150Sstevel@tonic-gate goto passwd_exit; 10160Sstevel@tonic-gate } 10170Sstevel@tonic-gate 10180Sstevel@tonic-gate /* see files_update_shadow() for open()+fdopen() rationale */ 10190Sstevel@tonic-gate 10200Sstevel@tonic-gate if ((tempfd = open(PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { 10210Sstevel@tonic-gate err = PWU_OPEN_FAILED; 10220Sstevel@tonic-gate goto passwd_exit; 10230Sstevel@tonic-gate } 10241914Scasper if ((dst = fdopen(tempfd, "wF")) == NULL) { 10250Sstevel@tonic-gate err = PWU_OPEN_FAILED; 10260Sstevel@tonic-gate goto passwd_exit; 10270Sstevel@tonic-gate } 10281914Scasper if ((src = fopen(PASSWD, "rF")) == NULL) { 10290Sstevel@tonic-gate err = PWU_OPEN_FAILED; 10300Sstevel@tonic-gate (void) fclose(dst); 10310Sstevel@tonic-gate (void) unlink(PASSTEMP); 10320Sstevel@tonic-gate goto passwd_exit; 10330Sstevel@tonic-gate } 10340Sstevel@tonic-gate 10350Sstevel@tonic-gate /* 10360Sstevel@tonic-gate * copy old password entries to temporary file while replacing 10370Sstevel@tonic-gate * the entry that matches "name" 10380Sstevel@tonic-gate */ 10390Sstevel@tonic-gate while (fgetpwent_r(src, &cur, buf, sizeof (buf)) != NULL) { 10400Sstevel@tonic-gate if (strcmp(cur.pw_name, name) == 0) 10410Sstevel@tonic-gate result = putpwent(pwd, dst); 10420Sstevel@tonic-gate else 10430Sstevel@tonic-gate result = putpwent(&cur, dst); 10440Sstevel@tonic-gate if (result != 0) { 10450Sstevel@tonic-gate err = PWU_WRITE_FAILED; 10460Sstevel@tonic-gate (void) fclose(src); 10470Sstevel@tonic-gate (void) fclose(dst); 10480Sstevel@tonic-gate goto passwd_exit; 10490Sstevel@tonic-gate } 10500Sstevel@tonic-gate } 10510Sstevel@tonic-gate 10520Sstevel@tonic-gate (void) fclose(src); 10530Sstevel@tonic-gate if (fclose(dst) != 0) { 10540Sstevel@tonic-gate err = PWU_CLOSE_FAILED; 10550Sstevel@tonic-gate goto passwd_exit; /* Don't trust the temporary file */ 10560Sstevel@tonic-gate } 10570Sstevel@tonic-gate 10580Sstevel@tonic-gate /* Rename temp to passwd */ 10590Sstevel@tonic-gate if (unlink(OPASSWD) && access(OPASSWD, 0) == 0) { 10600Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 10610Sstevel@tonic-gate (void) unlink(PASSTEMP); 10620Sstevel@tonic-gate goto passwd_exit; 10630Sstevel@tonic-gate } 10640Sstevel@tonic-gate 10650Sstevel@tonic-gate if (link(PASSWD, OPASSWD) == -1) { 10660Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 10670Sstevel@tonic-gate (void) unlink(PASSTEMP); 10680Sstevel@tonic-gate goto passwd_exit; 10690Sstevel@tonic-gate } 10700Sstevel@tonic-gate 10710Sstevel@tonic-gate if (rename(PASSTEMP, PASSWD) == -1) { 10720Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 10730Sstevel@tonic-gate (void) unlink(PASSTEMP); 10740Sstevel@tonic-gate goto passwd_exit; 10750Sstevel@tonic-gate } 10760Sstevel@tonic-gate 10770Sstevel@tonic-gate (void) chmod(PASSWD, 0644); 10780Sstevel@tonic-gate 10790Sstevel@tonic-gate passwd_exit: 10800Sstevel@tonic-gate return (err); 10810Sstevel@tonic-gate 10820Sstevel@tonic-gate } 10830Sstevel@tonic-gate 10840Sstevel@tonic-gate /* 10850Sstevel@tonic-gate * files_putpwnam(name, oldpw, dummy, rep, buf) 10860Sstevel@tonic-gate * 10870Sstevel@tonic-gate * store the password attributes contained in "buf" in /etc/passwd and 10880Sstevel@tonic-gate * /etc/shadow. The dummy parameter is a placeholder for NIS+ 10890Sstevel@tonic-gate * updates where the "oldrpc" password is passed. 10900Sstevel@tonic-gate */ 10910Sstevel@tonic-gate /*ARGSUSED*/ 10920Sstevel@tonic-gate int 10930Sstevel@tonic-gate files_putpwnam(char *name, char *oldpw, char *dummy, 10940Sstevel@tonic-gate pwu_repository_t *rep, void *buf) 10950Sstevel@tonic-gate { 10960Sstevel@tonic-gate struct pwbuf *pwbuf = (struct pwbuf *)buf; 10970Sstevel@tonic-gate int result = PWU_SUCCESS; 10980Sstevel@tonic-gate 10990Sstevel@tonic-gate if (pwbuf->pwd) { 11000Sstevel@tonic-gate result = files_update_passwd(name, pwbuf->pwd); 11010Sstevel@tonic-gate } 11020Sstevel@tonic-gate 11030Sstevel@tonic-gate if (result == PWU_SUCCESS && pwbuf->spwd) { 11040Sstevel@tonic-gate if (pwbuf->update_history != 0) { 11050Sstevel@tonic-gate debug("update_history = %d", pwbuf->update_history); 11060Sstevel@tonic-gate result = files_update_history(name, pwbuf->spwd); 11070Sstevel@tonic-gate } else { 11080Sstevel@tonic-gate debug("no password change"); 11090Sstevel@tonic-gate } 11100Sstevel@tonic-gate if (result == PWU_SUCCESS) { 11110Sstevel@tonic-gate result = files_update_shadow(name, pwbuf->spwd); 11120Sstevel@tonic-gate } 11130Sstevel@tonic-gate } 11140Sstevel@tonic-gate 11150Sstevel@tonic-gate if (pwbuf->pwd) { 11160Sstevel@tonic-gate (void) memset(pwbuf->pwd, 0, sizeof (struct passwd)); 11170Sstevel@tonic-gate (void) memset(pwbuf->pwd_scratch, 0, PWD_SCRATCH_SIZE); 11180Sstevel@tonic-gate free(pwbuf->pwd); 11190Sstevel@tonic-gate free(pwbuf->pwd_scratch); 11200Sstevel@tonic-gate } 11210Sstevel@tonic-gate if (pwbuf->spwd) { 11220Sstevel@tonic-gate (void) memset(pwbuf->spwd, 0, sizeof (struct spwd)); 11230Sstevel@tonic-gate (void) memset(pwbuf->spwd_scratch, 0, SPW_SCRATCH_SIZE); 11240Sstevel@tonic-gate free(pwbuf->spwd); 11250Sstevel@tonic-gate free(pwbuf->spwd_scratch); 11260Sstevel@tonic-gate } 11270Sstevel@tonic-gate if (pwbuf->new_sp_pwdp) { 11280Sstevel@tonic-gate free(pwbuf->new_sp_pwdp); 11290Sstevel@tonic-gate } 11300Sstevel@tonic-gate 11310Sstevel@tonic-gate return (result); 11320Sstevel@tonic-gate } 11330Sstevel@tonic-gate 11340Sstevel@tonic-gate /* 11350Sstevel@tonic-gate * NOTE: This is all covered under the repository lock held for updating 11360Sstevel@tonic-gate * passwd(4) and shadow(4). 11370Sstevel@tonic-gate */ 11380Sstevel@tonic-gate int 11390Sstevel@tonic-gate files_update_history(char *name, struct spwd *spwd) 11400Sstevel@tonic-gate { 11410Sstevel@tonic-gate int histsize; 11420Sstevel@tonic-gate int tmpfd; 11430Sstevel@tonic-gate FILE *src; /* history database file */ 11440Sstevel@tonic-gate FILE *dst; /* temp history database being updated */ 11450Sstevel@tonic-gate struct stat64 statbuf; 11460Sstevel@tonic-gate char buf[MAX_LOGNAME + MAXHISTORY + 11470Sstevel@tonic-gate (MAXHISTORY * CRYPT_MAXCIPHERTEXTLEN)+1]; 11480Sstevel@tonic-gate int found; 11490Sstevel@tonic-gate 11500Sstevel@tonic-gate if ((histsize = def_getint("HISTORY=", DEFHISTORY)) == 0) { 11510Sstevel@tonic-gate debug("files_update_history(%s) no history, unlinking", name); 11520Sstevel@tonic-gate (void) unlink(HISTORY); 11530Sstevel@tonic-gate return (PWU_SUCCESS); /* no history update defined */ 11540Sstevel@tonic-gate } 11550Sstevel@tonic-gate debug("files_update_history(%s, %s) histsize = %d", name, spwd->sp_pwdp, 11560Sstevel@tonic-gate histsize); 11570Sstevel@tonic-gate 11580Sstevel@tonic-gate if (histsize > MAXHISTORY) 11590Sstevel@tonic-gate histsize = MAXHISTORY; 11600Sstevel@tonic-gate if ((tmpfd = open(HISTEMP, O_WRONLY|O_CREAT|O_TRUNC, HISTMODE)) < 0) { 11610Sstevel@tonic-gate return (PWU_OPEN_FAILED); 11620Sstevel@tonic-gate } 11630Sstevel@tonic-gate (void) fchown(tmpfd, (uid_t)0, (gid_t)0); 11640Sstevel@tonic-gate 11650Sstevel@tonic-gate /* get ready to copy */ 11661914Scasper if (((src = fopen(HISTORY, "rF")) == NULL) && 11670Sstevel@tonic-gate (errno != ENOENT)) { 11680Sstevel@tonic-gate (void) unlink(HISTEMP); 11690Sstevel@tonic-gate return (PWU_OPEN_FAILED); 11700Sstevel@tonic-gate } 11711914Scasper if ((dst = fdopen(tmpfd, "wF")) == NULL) { 11720Sstevel@tonic-gate (void) fclose(src); 11730Sstevel@tonic-gate (void) unlink(HISTEMP); 11740Sstevel@tonic-gate return (PWU_OPEN_FAILED); 11750Sstevel@tonic-gate } 11760Sstevel@tonic-gate 11770Sstevel@tonic-gate /* Copy and update if found. Add if not found. */ 11780Sstevel@tonic-gate 11790Sstevel@tonic-gate found = 0; 11800Sstevel@tonic-gate 11810Sstevel@tonic-gate while ((src != NULL) && 11820Sstevel@tonic-gate (fgets(buf, sizeof (buf), src) != NULL)) { 11830Sstevel@tonic-gate char *user; 11840Sstevel@tonic-gate char *last; 11850Sstevel@tonic-gate 11860Sstevel@tonic-gate /* get username field */ 11870Sstevel@tonic-gate user = strtok_r(buf, ":", &last); 11880Sstevel@tonic-gate 11890Sstevel@tonic-gate #ifdef DEBUG 11900Sstevel@tonic-gate debug("files_update_history: read=\"%s\"", user); 11910Sstevel@tonic-gate #endif /* DEBUG */ 11920Sstevel@tonic-gate 11930Sstevel@tonic-gate if (strcmp(user, name) == 0) { 11940Sstevel@tonic-gate char *crypt; 11950Sstevel@tonic-gate int i; 11960Sstevel@tonic-gate 11970Sstevel@tonic-gate /* found user, update */ 11980Sstevel@tonic-gate found++; 11990Sstevel@tonic-gate (void) fprintf(dst, "%s:%s:", name, spwd->sp_pwdp); 12000Sstevel@tonic-gate debug("files_update_history: update user\n" 12010Sstevel@tonic-gate "\t%s:%s:", name, spwd->sp_pwdp); 12020Sstevel@tonic-gate 12030Sstevel@tonic-gate /* get old crypted password history */ 12040Sstevel@tonic-gate for (i = 0; i < MAXHISTORY-1; i++) { 12050Sstevel@tonic-gate crypt = strtok_r(NULL, ":", &last); 12060Sstevel@tonic-gate if (crypt == NULL || 12070Sstevel@tonic-gate *crypt == '\n') { 12080Sstevel@tonic-gate break; 12090Sstevel@tonic-gate } 12100Sstevel@tonic-gate (void) fprintf(dst, "%s:", crypt); 12110Sstevel@tonic-gate debug("\t%d = %s:", i+1, crypt); 12120Sstevel@tonic-gate } 12130Sstevel@tonic-gate (void) fprintf(dst, "\n"); 12140Sstevel@tonic-gate } else { 12150Sstevel@tonic-gate 12160Sstevel@tonic-gate /* copy other users to updated file */ 12170Sstevel@tonic-gate (void) fprintf(dst, "%s:%s", user, last); 12180Sstevel@tonic-gate #ifdef DEBUG 12190Sstevel@tonic-gate debug("files_update_history: copy line %s", 12200Sstevel@tonic-gate user); 12210Sstevel@tonic-gate #endif /* DEBUG */ 12220Sstevel@tonic-gate } 12230Sstevel@tonic-gate } 12240Sstevel@tonic-gate 12250Sstevel@tonic-gate if (found == 0) { 12260Sstevel@tonic-gate 12270Sstevel@tonic-gate /* user not found, add to history file */ 12280Sstevel@tonic-gate (void) fprintf(dst, "%s:%s:\n", name, spwd->sp_pwdp); 12290Sstevel@tonic-gate debug("files_update_history: add line\n" 12300Sstevel@tonic-gate "\t%s:%s:", name, spwd->sp_pwdp); 12310Sstevel@tonic-gate } 12320Sstevel@tonic-gate 12330Sstevel@tonic-gate (void) fclose(src); 12340Sstevel@tonic-gate 12350Sstevel@tonic-gate /* If something messed up in file system, loose the update */ 12360Sstevel@tonic-gate if (fclose(dst) != 0) { 12370Sstevel@tonic-gate 12380Sstevel@tonic-gate debug("files_update_history: update file close failed %d", 12390Sstevel@tonic-gate errno); 12400Sstevel@tonic-gate (void) unlink(HISTEMP); 12410Sstevel@tonic-gate return (PWU_CLOSE_FAILED); 12420Sstevel@tonic-gate } 12430Sstevel@tonic-gate 12440Sstevel@tonic-gate /* 12450Sstevel@tonic-gate * rename history to ohistory, 12460Sstevel@tonic-gate * rename tmp to history, 12470Sstevel@tonic-gate * unlink ohistory. 12480Sstevel@tonic-gate */ 12490Sstevel@tonic-gate 12500Sstevel@tonic-gate (void) unlink(OHISTORY); 12510Sstevel@tonic-gate 12520Sstevel@tonic-gate if (stat64(OHISTORY, &statbuf) == 0 || 12530Sstevel@tonic-gate ((src != NULL) && (link(HISTORY, OHISTORY) != 0)) || 12540Sstevel@tonic-gate rename(HISTEMP, HISTORY) != 0) { 12550Sstevel@tonic-gate 12560Sstevel@tonic-gate /* old history won't go away, loose the update */ 12570Sstevel@tonic-gate debug("files_update_history: update file rename failed %d", 12580Sstevel@tonic-gate errno); 12590Sstevel@tonic-gate (void) unlink(HISTEMP); 12600Sstevel@tonic-gate return (PWU_UPDATE_FAILED); 12610Sstevel@tonic-gate } 12620Sstevel@tonic-gate 12630Sstevel@tonic-gate (void) unlink(OHISTORY); 12640Sstevel@tonic-gate return (PWU_SUCCESS); 12650Sstevel@tonic-gate } 1266