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 /* 225816Sgww * Copyright 2008 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; 2295816Sgww if (pwbuf == NULL) 2305816Sgww return (PWU_NOMEM); 2310Sstevel@tonic-gate 2320Sstevel@tonic-gate /* 2330Sstevel@tonic-gate * determine which password structure (/etc/passwd or /etc/shadow) 2340Sstevel@tonic-gate * we need for the items we need to update 2350Sstevel@tonic-gate */ 2360Sstevel@tonic-gate for (p = items; p != NULL; p = p->next) { 2370Sstevel@tonic-gate switch (p->type) { 2380Sstevel@tonic-gate case ATTR_NAME: 2390Sstevel@tonic-gate case ATTR_UID: 2400Sstevel@tonic-gate case ATTR_GID: 2410Sstevel@tonic-gate case ATTR_AGE: 2420Sstevel@tonic-gate case ATTR_COMMENT: 2430Sstevel@tonic-gate case ATTR_GECOS: 2440Sstevel@tonic-gate case ATTR_HOMEDIR: 2450Sstevel@tonic-gate case ATTR_SHELL: 2460Sstevel@tonic-gate if (pwbuf->pwd == NULL) { 2470Sstevel@tonic-gate pwbuf->pwd = malloc(sizeof (struct passwd)); 2480Sstevel@tonic-gate if (pwbuf->pwd == NULL) { 2490Sstevel@tonic-gate err = PWU_NOMEM; 2500Sstevel@tonic-gate goto error; 2510Sstevel@tonic-gate } 2520Sstevel@tonic-gate } 2530Sstevel@tonic-gate break; 2540Sstevel@tonic-gate case ATTR_PASSWD: 2550Sstevel@tonic-gate case ATTR_PASSWD_SERVER_POLICY: 2560Sstevel@tonic-gate case ATTR_LSTCHG: 2570Sstevel@tonic-gate case ATTR_MIN: 2580Sstevel@tonic-gate case ATTR_MAX: 2590Sstevel@tonic-gate case ATTR_WARN: 2600Sstevel@tonic-gate case ATTR_INACT: 2610Sstevel@tonic-gate case ATTR_EXPIRE: 2620Sstevel@tonic-gate case ATTR_FLAG: 2630Sstevel@tonic-gate case ATTR_LOCK_ACCOUNT: 2640Sstevel@tonic-gate case ATTR_EXPIRE_PASSWORD: 2650Sstevel@tonic-gate case ATTR_FAILED_LOGINS: 2660Sstevel@tonic-gate case ATTR_INCR_FAILED_LOGINS: 2670Sstevel@tonic-gate case ATTR_RST_FAILED_LOGINS: 2680Sstevel@tonic-gate case ATTR_NOLOGIN_ACCOUNT: 2690Sstevel@tonic-gate case ATTR_UNLOCK_ACCOUNT: 2700Sstevel@tonic-gate if (pwbuf->spwd == NULL) { 2710Sstevel@tonic-gate pwbuf->spwd = malloc(sizeof (struct spwd)); 2720Sstevel@tonic-gate if (pwbuf->spwd == NULL) { 2730Sstevel@tonic-gate err = PWU_NOMEM; 2740Sstevel@tonic-gate goto error; 2750Sstevel@tonic-gate } 2760Sstevel@tonic-gate } 2770Sstevel@tonic-gate break; 2780Sstevel@tonic-gate default: 2790Sstevel@tonic-gate /* 2800Sstevel@tonic-gate * Some other repository might have different values 2810Sstevel@tonic-gate * so we ignore those. 2820Sstevel@tonic-gate */ 2830Sstevel@tonic-gate break; 2840Sstevel@tonic-gate } 2850Sstevel@tonic-gate } 2860Sstevel@tonic-gate 2870Sstevel@tonic-gate if (pwbuf->pwd) { 2880Sstevel@tonic-gate if ((pwbuf->pwd_scratch = malloc(PWD_SCRATCH_SIZE)) == NULL) { 2890Sstevel@tonic-gate err = PWU_NOMEM; 2900Sstevel@tonic-gate goto error; 2910Sstevel@tonic-gate } 2920Sstevel@tonic-gate if (private_getpwnam_r(name, pwbuf->pwd, pwbuf->pwd_scratch, 2930Sstevel@tonic-gate PWD_SCRATCH_SIZE) == NULL) { 2940Sstevel@tonic-gate err = PWU_NOT_FOUND; 2950Sstevel@tonic-gate goto error; 2960Sstevel@tonic-gate } 2970Sstevel@tonic-gate } 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate if (pwbuf->spwd) { 3000Sstevel@tonic-gate if ((pwbuf->spwd_scratch = malloc(SPW_SCRATCH_SIZE)) == NULL) { 3010Sstevel@tonic-gate err = PWU_NOMEM; 3020Sstevel@tonic-gate goto error; 3030Sstevel@tonic-gate } 3040Sstevel@tonic-gate if (private_getspnam_r(name, pwbuf->spwd, pwbuf->spwd_scratch, 3050Sstevel@tonic-gate SPW_SCRATCH_SIZE) == NULL) { 3060Sstevel@tonic-gate err = PWU_NOT_FOUND; 3070Sstevel@tonic-gate goto error; 3080Sstevel@tonic-gate } 3090Sstevel@tonic-gate } 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate return (PWU_SUCCESS); 3120Sstevel@tonic-gate error: 3130Sstevel@tonic-gate if (pwbuf->pwd) free(pwbuf->pwd); 3140Sstevel@tonic-gate if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch); 3150Sstevel@tonic-gate if (pwbuf->spwd) free(pwbuf->spwd); 3160Sstevel@tonic-gate if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch); 3170Sstevel@tonic-gate free(pwbuf); 3180Sstevel@tonic-gate *buf = NULL; 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate return (err); 3210Sstevel@tonic-gate } 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate /* 3240Sstevel@tonic-gate * int files_user_to_authenticate(name, rep, auth_user, privileged) 3250Sstevel@tonic-gate * Determine which user needs to be authenticated. For files, the 3260Sstevel@tonic-gate * possible return values are: 3270Sstevel@tonic-gate * PWU_NOT_FOUND 3280Sstevel@tonic-gate * PWU_SUCCESS and (auth_user == NULL || auth_user = user) 3290Sstevel@tonic-gate * PWU_DENIED 330*5890Sjjj * PWU_NOMEM 3310Sstevel@tonic-gate */ 3320Sstevel@tonic-gate /*ARGSUSED*/ 3330Sstevel@tonic-gate int 3340Sstevel@tonic-gate files_user_to_authenticate(char *user, pwu_repository_t *rep, 3350Sstevel@tonic-gate char **auth_user, int *privileged) 3360Sstevel@tonic-gate { 3370Sstevel@tonic-gate struct pwbuf *pwbuf; 3380Sstevel@tonic-gate int res; 3390Sstevel@tonic-gate attrlist attr_tmp[1] = { { ATTR_UID, NULL, NULL } }; 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate /* check to see if target user is present in files */ 3420Sstevel@tonic-gate res = files_getpwnam(user, &attr_tmp[0], rep, (void **)&pwbuf); 3430Sstevel@tonic-gate if (res != PWU_SUCCESS) 3440Sstevel@tonic-gate return (res); 3450Sstevel@tonic-gate 3460Sstevel@tonic-gate if (files_privileged()) { 3470Sstevel@tonic-gate *auth_user = NULL; 3480Sstevel@tonic-gate *privileged = 1; 3490Sstevel@tonic-gate res = PWU_SUCCESS; 3500Sstevel@tonic-gate } else { 3510Sstevel@tonic-gate *privileged = 0; 3520Sstevel@tonic-gate if (getuid() == pwbuf->pwd->pw_uid) { 353*5890Sjjj if ((*auth_user = strdup(user)) == NULL) { 354*5890Sjjj res = PWU_NOMEM; 355*5890Sjjj } else { 356*5890Sjjj res = PWU_SUCCESS; 357*5890Sjjj } 3580Sstevel@tonic-gate } else { 3590Sstevel@tonic-gate res = PWU_DENIED; 3600Sstevel@tonic-gate } 3610Sstevel@tonic-gate } 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate if (pwbuf->pwd) free(pwbuf->pwd); 3640Sstevel@tonic-gate if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch); 3650Sstevel@tonic-gate if (pwbuf->spwd) free(pwbuf->spwd); 3660Sstevel@tonic-gate if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch); 3670Sstevel@tonic-gate free(pwbuf); 3680Sstevel@tonic-gate 3690Sstevel@tonic-gate return (res); 3700Sstevel@tonic-gate } 3710Sstevel@tonic-gate 3720Sstevel@tonic-gate /* 3730Sstevel@tonic-gate * Password history file format: 3740Sstevel@tonic-gate * user:crypw1: ... crypwn: such that n <= MAXHISTORY 3750Sstevel@tonic-gate */ 3760Sstevel@tonic-gate #define HISTORY "/etc/security/passhistory" 3770Sstevel@tonic-gate #define HISTEMP "/etc/security/pwhistemp" 3780Sstevel@tonic-gate #define OHISTORY "/etc/security/opwhistory" 3790Sstevel@tonic-gate #define HISTMODE S_IRUSR /* mode to create history file */ 3800Sstevel@tonic-gate /* 3810Sstevel@tonic-gate * XXX 3820Sstevel@tonic-gate * 3*LOGNAME_MAX just in case there are long user names. 3830Sstevel@tonic-gate * Traditionally Solaris LOGNAME_MAX (_POSIX_LOGIN_NAME_MAX) is 13, 3840Sstevel@tonic-gate * but some sites often user more. 3850Sstevel@tonic-gate * If LOGNAME_MAX ever becomes reasonable (128) and actually enforced, 3860Sstevel@tonic-gate * fix up here. 3870Sstevel@tonic-gate * XXX 3880Sstevel@tonic-gate */ 3890Sstevel@tonic-gate #define MAX_LOGNAME (3 * LOGNAME_MAX) 3900Sstevel@tonic-gate 3910Sstevel@tonic-gate /* 3920Sstevel@tonic-gate * files_checkhistory - check if a user's new password is in the user's 3930Sstevel@tonic-gate * old password history. 3940Sstevel@tonic-gate * 3950Sstevel@tonic-gate * Entry 3960Sstevel@tonic-gate * user = username. 3970Sstevel@tonic-gate * passwd = new clear text password. 3980Sstevel@tonic-gate * 3990Sstevel@tonic-gate * Exit 4000Sstevel@tonic-gate * PWU_SUCCESS, passwd found in user's old password history. 4010Sstevel@tonic-gate * The caller should only be interested and fail if 4020Sstevel@tonic-gate * PWU_SUCCESS is returned. 4030Sstevel@tonic-gate * PWU_NOT_FOUND, passwd not in user's old password history. 4040Sstevel@tonic-gate * PWU_errors, PWU_ errors from other routines. 4050Sstevel@tonic-gate * 4060Sstevel@tonic-gate */ 4070Sstevel@tonic-gate int 4080Sstevel@tonic-gate files_checkhistory(char *user, char *passwd, pwu_repository_t *rep) 4090Sstevel@tonic-gate { 4100Sstevel@tonic-gate attrlist attr; 4110Sstevel@tonic-gate int res; 4120Sstevel@tonic-gate 4130Sstevel@tonic-gate attr.type = ATTR_HISTORY; 4140Sstevel@tonic-gate attr.data.val_s = NULL; 4150Sstevel@tonic-gate attr.next = NULL; 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate debug("files_checkhistory(user=%s)", user); 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate /* 4200Sstevel@tonic-gate * XXX 4210Sstevel@tonic-gate * This depends on the underlying files_getattr implementation 4220Sstevel@tonic-gate * treating user not found in backing store or no history as 4230Sstevel@tonic-gate * an error. 4240Sstevel@tonic-gate * XXX 4250Sstevel@tonic-gate */ 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate if ((res = files_getattr(user, &attr, rep)) == PWU_SUCCESS) { 4280Sstevel@tonic-gate char *s; 4290Sstevel@tonic-gate char *crypt_passwd; 4300Sstevel@tonic-gate int histsize; 4310Sstevel@tonic-gate char *last = attr.data.val_s; 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate if ((histsize = def_getint("HISTORY=", DEFHISTORY)) == 0) { 4340Sstevel@tonic-gate debug("files_checkhistory: no history requested"); 4350Sstevel@tonic-gate res = PWU_NOT_FOUND; 4360Sstevel@tonic-gate goto out; 4370Sstevel@tonic-gate } 4380Sstevel@tonic-gate 4390Sstevel@tonic-gate debug("files_checkhistory: histsize = %d", histsize); 4400Sstevel@tonic-gate if (histsize > MAXHISTORY) 4410Sstevel@tonic-gate histsize = MAXHISTORY; 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate debug("line to test\n\t%s", last); 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate /* compare crypt_passwd to attr.data.val_s strings. */ 4460Sstevel@tonic-gate res = PWU_NOT_FOUND; 4470Sstevel@tonic-gate while ((histsize-- > 0) && 4480Sstevel@tonic-gate (((s = strtok_r(NULL, ":", &last)) != NULL) && 4490Sstevel@tonic-gate (*s != '\n'))) { 4500Sstevel@tonic-gate 4510Sstevel@tonic-gate crypt_passwd = crypt(passwd, s); 4520Sstevel@tonic-gate debug("files_checkhistory: user_pw=%s, history_pw=%s", 4530Sstevel@tonic-gate crypt_passwd, s); 4540Sstevel@tonic-gate if (strcmp(crypt_passwd, s) == 0) { 4550Sstevel@tonic-gate res = PWU_SUCCESS; 4560Sstevel@tonic-gate break; 4570Sstevel@tonic-gate } 4580Sstevel@tonic-gate } 4590Sstevel@tonic-gate debug("files_checkhistory(%s, %s) = %d", user, crypt_passwd, 4600Sstevel@tonic-gate res); 4610Sstevel@tonic-gate } 4620Sstevel@tonic-gate out: 4630Sstevel@tonic-gate if (attr.data.val_s != NULL) 4640Sstevel@tonic-gate free(attr.data.val_s); 4650Sstevel@tonic-gate 4660Sstevel@tonic-gate return (res); 4670Sstevel@tonic-gate } 4680Sstevel@tonic-gate 4690Sstevel@tonic-gate /* 4700Sstevel@tonic-gate * files_getattr(name, items, rep) 4710Sstevel@tonic-gate * 4720Sstevel@tonic-gate * Get attributes specified in list 'items' 4730Sstevel@tonic-gate */ 4740Sstevel@tonic-gate int 4750Sstevel@tonic-gate files_getattr(char *name, attrlist *items, pwu_repository_t *rep) 4760Sstevel@tonic-gate { 4770Sstevel@tonic-gate struct pwbuf *pwbuf; 4780Sstevel@tonic-gate struct passwd *pw; 4790Sstevel@tonic-gate struct spwd *spw; 4800Sstevel@tonic-gate attrlist *w; 4810Sstevel@tonic-gate int res; 4820Sstevel@tonic-gate 4830Sstevel@tonic-gate res = files_getpwnam(name, items, rep, (void **)&pwbuf); 4840Sstevel@tonic-gate if (res != PWU_SUCCESS) 4850Sstevel@tonic-gate return (res); 4860Sstevel@tonic-gate 4870Sstevel@tonic-gate pw = pwbuf->pwd; 4880Sstevel@tonic-gate spw = pwbuf->spwd; 4890Sstevel@tonic-gate 4900Sstevel@tonic-gate for (w = items; res == PWU_SUCCESS && w != NULL; w = w->next) { 4910Sstevel@tonic-gate switch (w->type) { 4920Sstevel@tonic-gate case ATTR_NAME: 4930Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_name)) == NULL) 4940Sstevel@tonic-gate res = PWU_NOMEM; 4950Sstevel@tonic-gate break; 4960Sstevel@tonic-gate case ATTR_COMMENT: 4970Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_comment)) == NULL) 4980Sstevel@tonic-gate res = PWU_NOMEM; 4990Sstevel@tonic-gate break; 5000Sstevel@tonic-gate case ATTR_GECOS: 5010Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL) 5020Sstevel@tonic-gate res = PWU_NOMEM; 5030Sstevel@tonic-gate break; 5040Sstevel@tonic-gate case ATTR_HOMEDIR: 5050Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_dir)) == NULL) 5060Sstevel@tonic-gate res = PWU_NOMEM; 5070Sstevel@tonic-gate break; 5080Sstevel@tonic-gate case ATTR_SHELL: 5090Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_shell)) == NULL) 5100Sstevel@tonic-gate res = PWU_NOMEM; 5110Sstevel@tonic-gate break; 5120Sstevel@tonic-gate /* 5130Sstevel@tonic-gate * Nothing special needs to be done for 5140Sstevel@tonic-gate * server policy 5150Sstevel@tonic-gate */ 5160Sstevel@tonic-gate case ATTR_PASSWD: 5170Sstevel@tonic-gate case ATTR_PASSWD_SERVER_POLICY: 5180Sstevel@tonic-gate if ((w->data.val_s = strdup(spw->sp_pwdp)) == NULL) 5190Sstevel@tonic-gate res = PWU_NOMEM; 5200Sstevel@tonic-gate break; 5210Sstevel@tonic-gate case ATTR_AGE: 5220Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_age)) == NULL) 5230Sstevel@tonic-gate res = PWU_NOMEM; 5240Sstevel@tonic-gate break; 5250Sstevel@tonic-gate case ATTR_REP_NAME: 5260Sstevel@tonic-gate if ((w->data.val_s = strdup("files")) == NULL) 5270Sstevel@tonic-gate res = PWU_NOMEM; 5280Sstevel@tonic-gate break; 5290Sstevel@tonic-gate case ATTR_HISTORY: { 5300Sstevel@tonic-gate FILE *history; 5310Sstevel@tonic-gate char buf[MAX_LOGNAME + MAXHISTORY + 5320Sstevel@tonic-gate (MAXHISTORY * CRYPT_MAXCIPHERTEXTLEN)+1]; 5330Sstevel@tonic-gate char *s, *s1; 5340Sstevel@tonic-gate 5350Sstevel@tonic-gate debug("files_getattr: Get password history for %s ", 5360Sstevel@tonic-gate name); 5370Sstevel@tonic-gate 5381914Scasper if ((history = fopen(HISTORY, "rF")) == NULL) { 5390Sstevel@tonic-gate debug("files_getattr: %s not found", HISTORY); 5400Sstevel@tonic-gate res = PWU_OPEN_FAILED; 5410Sstevel@tonic-gate goto getattr_exit; 5420Sstevel@tonic-gate } 5430Sstevel@tonic-gate res = PWU_NOT_FOUND; 5440Sstevel@tonic-gate while ((s = fgets(buf, sizeof (buf), history)) != 5450Sstevel@tonic-gate NULL) { 5460Sstevel@tonic-gate s1 = strchr(s, ':'); 5470Sstevel@tonic-gate if (s1 != NULL) { 5480Sstevel@tonic-gate *s1 = '\0'; 5490Sstevel@tonic-gate } else { 5500Sstevel@tonic-gate res = PWU_NOT_FOUND; 5510Sstevel@tonic-gate break; 5520Sstevel@tonic-gate } 5530Sstevel@tonic-gate #ifdef DEBUG 5540Sstevel@tonic-gate debug("got history line for %s", s); 5550Sstevel@tonic-gate #endif /* DEBUG */ 5560Sstevel@tonic-gate if (strcmp(s, name) == 0) { 5570Sstevel@tonic-gate /* found user */ 5580Sstevel@tonic-gate if ((items->data.val_s = 5590Sstevel@tonic-gate strdup(s1+1)) == NULL) 5600Sstevel@tonic-gate res = PWU_NOMEM; 5610Sstevel@tonic-gate else 5620Sstevel@tonic-gate res = PWU_SUCCESS; 5630Sstevel@tonic-gate break; 5640Sstevel@tonic-gate } 5650Sstevel@tonic-gate } 5660Sstevel@tonic-gate (void) fclose(history); 5670Sstevel@tonic-gate break; 5680Sstevel@tonic-gate } 5690Sstevel@tonic-gate 5700Sstevel@tonic-gate /* integer values */ 5710Sstevel@tonic-gate case ATTR_UID: 5720Sstevel@tonic-gate w->data.val_i = pw->pw_uid; 5730Sstevel@tonic-gate break; 5740Sstevel@tonic-gate case ATTR_GID: 5750Sstevel@tonic-gate w->data.val_i = pw->pw_gid; 5760Sstevel@tonic-gate break; 5770Sstevel@tonic-gate case ATTR_LSTCHG: 5780Sstevel@tonic-gate w->data.val_i = spw->sp_lstchg; 5790Sstevel@tonic-gate break; 5800Sstevel@tonic-gate case ATTR_MIN: 5810Sstevel@tonic-gate w->data.val_i = spw->sp_min; 5820Sstevel@tonic-gate break; 5830Sstevel@tonic-gate case ATTR_MAX: 5840Sstevel@tonic-gate w->data.val_i = spw->sp_max; 5850Sstevel@tonic-gate break; 5860Sstevel@tonic-gate case ATTR_WARN: 5870Sstevel@tonic-gate w->data.val_i = spw->sp_warn; 5880Sstevel@tonic-gate break; 5890Sstevel@tonic-gate case ATTR_INACT: 5900Sstevel@tonic-gate w->data.val_i = spw->sp_inact; 5910Sstevel@tonic-gate break; 5920Sstevel@tonic-gate case ATTR_EXPIRE: 5930Sstevel@tonic-gate w->data.val_i = spw->sp_expire; 5940Sstevel@tonic-gate break; 5950Sstevel@tonic-gate case ATTR_FLAG: 5960Sstevel@tonic-gate w->data.val_i = spw->sp_flag; 5970Sstevel@tonic-gate break; 5980Sstevel@tonic-gate case ATTR_FAILED_LOGINS: 5990Sstevel@tonic-gate w->data.val_i = spw->sp_flag & FAILCOUNT_MASK; 6000Sstevel@tonic-gate break; 6010Sstevel@tonic-gate default: 6020Sstevel@tonic-gate break; 6030Sstevel@tonic-gate } 6040Sstevel@tonic-gate } 6050Sstevel@tonic-gate 6060Sstevel@tonic-gate getattr_exit: 6070Sstevel@tonic-gate if (pwbuf->pwd) free(pwbuf->pwd); 6080Sstevel@tonic-gate if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch); 6090Sstevel@tonic-gate if (pwbuf->spwd) free(pwbuf->spwd); 6102279Sps57422 if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch); 6110Sstevel@tonic-gate free(pwbuf); 6120Sstevel@tonic-gate 6130Sstevel@tonic-gate return (res); 6140Sstevel@tonic-gate } 6150Sstevel@tonic-gate 6160Sstevel@tonic-gate /* 6170Sstevel@tonic-gate * max_present(list) 6180Sstevel@tonic-gate * 6190Sstevel@tonic-gate * see if attribute ATTR_MAX, with value != -1, is present in 6200Sstevel@tonic-gate * attribute-list "list". 6210Sstevel@tonic-gate * 6220Sstevel@tonic-gate * returns 1 if present, 0 otherwise. 6230Sstevel@tonic-gate */ 6240Sstevel@tonic-gate static int 6250Sstevel@tonic-gate max_present(attrlist *list) 6260Sstevel@tonic-gate { 6270Sstevel@tonic-gate while (list != NULL) 6280Sstevel@tonic-gate if (list->type == ATTR_MAX && list->data.val_i != -1) 6290Sstevel@tonic-gate return (1); 6300Sstevel@tonic-gate else 6310Sstevel@tonic-gate list = list->next; 6320Sstevel@tonic-gate 6330Sstevel@tonic-gate return (0); 6340Sstevel@tonic-gate } 6350Sstevel@tonic-gate 6360Sstevel@tonic-gate /* 6370Sstevel@tonic-gate * files_update(items, rep, buf) 6380Sstevel@tonic-gate * 6390Sstevel@tonic-gate * update the information in buf with the attributes specified in 6400Sstevel@tonic-gate * items. 6410Sstevel@tonic-gate */ 6420Sstevel@tonic-gate /*ARGSUSED*/ 6430Sstevel@tonic-gate int 6440Sstevel@tonic-gate files_update(attrlist *items, pwu_repository_t *rep, void *buf) 6450Sstevel@tonic-gate { 6460Sstevel@tonic-gate struct pwbuf *pwbuf = (struct pwbuf *)buf; 6470Sstevel@tonic-gate struct passwd *pw; 6480Sstevel@tonic-gate struct spwd *spw; 6490Sstevel@tonic-gate attrlist *p; 6500Sstevel@tonic-gate int aging_needed = 0; 6510Sstevel@tonic-gate int aging_set = 0; 6520Sstevel@tonic-gate int disable_aging; 6530Sstevel@tonic-gate char *pword; 6540Sstevel@tonic-gate int len; 6550Sstevel@tonic-gate 6560Sstevel@tonic-gate pw = pwbuf->pwd; 6570Sstevel@tonic-gate spw = pwbuf->spwd; 6580Sstevel@tonic-gate pwbuf->update_history = 0; 6590Sstevel@tonic-gate 6600Sstevel@tonic-gate /* 6610Sstevel@tonic-gate * if sp_max==0 : disable passwd aging after updating the password 6620Sstevel@tonic-gate */ 6630Sstevel@tonic-gate disable_aging = (spw != NULL && spw->sp_max == 0); 6640Sstevel@tonic-gate 6650Sstevel@tonic-gate for (p = items; p != NULL; p = p->next) { 6660Sstevel@tonic-gate switch (p->type) { 6670Sstevel@tonic-gate case ATTR_NAME: 6680Sstevel@tonic-gate break; /* We are able to handle this, but... */ 6690Sstevel@tonic-gate case ATTR_UID: 6700Sstevel@tonic-gate pw->pw_uid = (uid_t)p->data.val_i; 6710Sstevel@tonic-gate break; 6720Sstevel@tonic-gate case ATTR_GID: 6730Sstevel@tonic-gate pw->pw_gid = (gid_t)p->data.val_i; 6740Sstevel@tonic-gate break; 6750Sstevel@tonic-gate case ATTR_AGE: 6760Sstevel@tonic-gate pw->pw_age = p->data.val_s; 6770Sstevel@tonic-gate break; 6780Sstevel@tonic-gate case ATTR_COMMENT: 6790Sstevel@tonic-gate pw->pw_comment = p->data.val_s; 6800Sstevel@tonic-gate break; 6810Sstevel@tonic-gate case ATTR_GECOS: 6820Sstevel@tonic-gate pw->pw_gecos = p->data.val_s; 6830Sstevel@tonic-gate break; 6840Sstevel@tonic-gate case ATTR_HOMEDIR: 6850Sstevel@tonic-gate pw->pw_dir = p->data.val_s; 6860Sstevel@tonic-gate break; 6870Sstevel@tonic-gate case ATTR_SHELL: 6880Sstevel@tonic-gate pw->pw_shell = p->data.val_s; 6890Sstevel@tonic-gate break; 6900Sstevel@tonic-gate 6910Sstevel@tonic-gate /* 6920Sstevel@tonic-gate * Nothing special needs to be done for 6930Sstevel@tonic-gate * server policy 6940Sstevel@tonic-gate */ 6950Sstevel@tonic-gate case ATTR_PASSWD: 6960Sstevel@tonic-gate case ATTR_PASSWD_SERVER_POLICY: 6970Sstevel@tonic-gate /* 6980Sstevel@tonic-gate * There is a special case only for files: if the 6990Sstevel@tonic-gate * password is to be deleted (-d to passwd), 7000Sstevel@tonic-gate * p->data.val_s will be NULL. 7010Sstevel@tonic-gate */ 7020Sstevel@tonic-gate if (p->data.val_s == NULL) { 7030Sstevel@tonic-gate spw->sp_pwdp = ""; 7040Sstevel@tonic-gate } else { 7050Sstevel@tonic-gate char *salt = NULL; 7060Sstevel@tonic-gate char *hash = NULL; 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate salt = crypt_gensalt(spw->sp_pwdp, pw); 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate if (salt == NULL) { 7110Sstevel@tonic-gate if (errno == ENOMEM) 7120Sstevel@tonic-gate return (PWU_NOMEM); 7130Sstevel@tonic-gate /* algorithm problem? */ 7140Sstevel@tonic-gate syslog(LOG_AUTH | LOG_ALERT, 7155187Sjjj "passwdutil: crypt_gensalt %m"); 7160Sstevel@tonic-gate return (PWU_UPDATE_FAILED); 7170Sstevel@tonic-gate } 7180Sstevel@tonic-gate hash = crypt(p->data.val_s, salt); 7190Sstevel@tonic-gate free(salt); 7200Sstevel@tonic-gate if (hash == NULL) { 7210Sstevel@tonic-gate errno = ENOMEM; 7220Sstevel@tonic-gate return (PWU_NOMEM); 7230Sstevel@tonic-gate } 7240Sstevel@tonic-gate pword = strdup(hash); 7250Sstevel@tonic-gate if (pword == NULL) { 7260Sstevel@tonic-gate errno = ENOMEM; 7270Sstevel@tonic-gate return (PWU_NOMEM); 7280Sstevel@tonic-gate } 7290Sstevel@tonic-gate 7300Sstevel@tonic-gate if (pwbuf->new_sp_pwdp) 7310Sstevel@tonic-gate free(pwbuf->new_sp_pwdp); 7320Sstevel@tonic-gate pwbuf->new_sp_pwdp = pword; 7330Sstevel@tonic-gate spw->sp_pwdp = pword; 7340Sstevel@tonic-gate aging_needed = 1; 7350Sstevel@tonic-gate pwbuf->update_history = 1; 7360Sstevel@tonic-gate } 7370Sstevel@tonic-gate spw->sp_flag &= ~FAILCOUNT_MASK; /* reset count */ 7380Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 7390Sstevel@tonic-gate break; 7400Sstevel@tonic-gate case ATTR_LOCK_ACCOUNT: 7410Sstevel@tonic-gate if (spw->sp_pwdp == NULL) { 7420Sstevel@tonic-gate spw->sp_pwdp = LOCKSTRING; 7430Sstevel@tonic-gate } else if (strncmp(spw->sp_pwdp, LOCKSTRING, 7440Sstevel@tonic-gate sizeof (LOCKSTRING)-1) != 0) { 7450Sstevel@tonic-gate len = sizeof (LOCKSTRING)-1 + 7460Sstevel@tonic-gate strlen(spw->sp_pwdp) + 1; 7470Sstevel@tonic-gate pword = malloc(len); 7480Sstevel@tonic-gate if (pword == NULL) { 7490Sstevel@tonic-gate errno = ENOMEM; 7500Sstevel@tonic-gate return (PWU_NOMEM); 7510Sstevel@tonic-gate } 7520Sstevel@tonic-gate (void) strlcpy(pword, LOCKSTRING, len); 7530Sstevel@tonic-gate (void) strlcat(pword, spw->sp_pwdp, len); 7540Sstevel@tonic-gate if (pwbuf->new_sp_pwdp) 7550Sstevel@tonic-gate free(pwbuf->new_sp_pwdp); 7560Sstevel@tonic-gate pwbuf->new_sp_pwdp = pword; 7570Sstevel@tonic-gate spw->sp_pwdp = pword; 7580Sstevel@tonic-gate } 7590Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 7600Sstevel@tonic-gate break; 7610Sstevel@tonic-gate case ATTR_UNLOCK_ACCOUNT: 7620Sstevel@tonic-gate if (spw->sp_pwdp != NULL && 7630Sstevel@tonic-gate strncmp(spw->sp_pwdp, LOCKSTRING, 7640Sstevel@tonic-gate sizeof (LOCKSTRING)-1) == 0) { 7650Sstevel@tonic-gate (void) strcpy(spw->sp_pwdp, spw->sp_pwdp + 7665187Sjjj sizeof (LOCKSTRING)-1); 7670Sstevel@tonic-gate } 7680Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 7690Sstevel@tonic-gate break; 7700Sstevel@tonic-gate case ATTR_NOLOGIN_ACCOUNT: 7710Sstevel@tonic-gate spw->sp_pwdp = NOLOGINSTRING; 7720Sstevel@tonic-gate if (pwbuf->new_sp_pwdp) { 7730Sstevel@tonic-gate free(pwbuf->new_sp_pwdp); 7740Sstevel@tonic-gate pwbuf->new_sp_pwdp = NULL; 7750Sstevel@tonic-gate } 7760Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 7770Sstevel@tonic-gate break; 7780Sstevel@tonic-gate case ATTR_EXPIRE_PASSWORD: 7790Sstevel@tonic-gate spw->sp_lstchg = 0; 7800Sstevel@tonic-gate break; 7810Sstevel@tonic-gate case ATTR_LSTCHG: 7820Sstevel@tonic-gate spw->sp_lstchg = p->data.val_i; 7830Sstevel@tonic-gate break; 7840Sstevel@tonic-gate case ATTR_MIN: 7850Sstevel@tonic-gate if (spw->sp_max == -1 && 7860Sstevel@tonic-gate p->data.val_i != -1 && max_present(p->next) == 0) 7870Sstevel@tonic-gate return (PWU_AGING_DISABLED); 7880Sstevel@tonic-gate spw->sp_min = p->data.val_i; 7890Sstevel@tonic-gate aging_set = 1; 7900Sstevel@tonic-gate break; 7910Sstevel@tonic-gate case ATTR_MAX: 7920Sstevel@tonic-gate if (p->data.val_i == -1) { 7930Sstevel@tonic-gate /* Turn aging off -> Reset min and warn too */ 7940Sstevel@tonic-gate 7950Sstevel@tonic-gate spw->sp_min = -1; 7960Sstevel@tonic-gate spw->sp_warn = -1; 7970Sstevel@tonic-gate } else { 7980Sstevel@tonic-gate /* Turn aging on */ 7990Sstevel@tonic-gate 8000Sstevel@tonic-gate if (spw->sp_min == -1) { 8010Sstevel@tonic-gate /* 8020Sstevel@tonic-gate * If minage has not been set with 8030Sstevel@tonic-gate * a command-line option, we set it 8040Sstevel@tonic-gate * to zero. 8050Sstevel@tonic-gate */ 8060Sstevel@tonic-gate spw->sp_min = 0; 8070Sstevel@tonic-gate } 8080Sstevel@tonic-gate 8090Sstevel@tonic-gate /* 8100Sstevel@tonic-gate * If aging was turned off, we update lstchg. 8110Sstevel@tonic-gate * 8120Sstevel@tonic-gate * We take care not to update lstchg if the 8130Sstevel@tonic-gate * user has no password, otherwise the user 8140Sstevel@tonic-gate * might not be required to provide a password 8150Sstevel@tonic-gate * the next time [s]he logs-in. 8160Sstevel@tonic-gate * 8170Sstevel@tonic-gate * Also, if lstchg != -1 (i.e., not set in 8180Sstevel@tonic-gate * /etc/shadow), we keep the old value. 8190Sstevel@tonic-gate */ 8200Sstevel@tonic-gate if (spw->sp_max == -1 && 8210Sstevel@tonic-gate spw->sp_pwdp != NULL && *spw->sp_pwdp && 8220Sstevel@tonic-gate spw->sp_lstchg == -1) { 8230Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 8240Sstevel@tonic-gate } 8250Sstevel@tonic-gate } 8260Sstevel@tonic-gate 8270Sstevel@tonic-gate spw->sp_max = p->data.val_i; 8280Sstevel@tonic-gate 8290Sstevel@tonic-gate aging_set = 1; 8300Sstevel@tonic-gate 8310Sstevel@tonic-gate break; 8320Sstevel@tonic-gate case ATTR_WARN: 8330Sstevel@tonic-gate if (spw->sp_max == -1 && p->data.val_i != -1 && 8340Sstevel@tonic-gate max_present(p->next) == 0) 8350Sstevel@tonic-gate return (PWU_AGING_DISABLED); 8360Sstevel@tonic-gate spw->sp_warn = p->data.val_i; 8370Sstevel@tonic-gate break; 8380Sstevel@tonic-gate case ATTR_INACT: 8390Sstevel@tonic-gate spw->sp_inact = p->data.val_i; 8400Sstevel@tonic-gate break; 8410Sstevel@tonic-gate case ATTR_EXPIRE: 8420Sstevel@tonic-gate spw->sp_expire = p->data.val_i; 8430Sstevel@tonic-gate break; 8440Sstevel@tonic-gate case ATTR_FLAG: 8450Sstevel@tonic-gate spw->sp_flag = p->data.val_i; 8460Sstevel@tonic-gate break; 8470Sstevel@tonic-gate case ATTR_INCR_FAILED_LOGINS: 8480Sstevel@tonic-gate { 8490Sstevel@tonic-gate int count = (spw->sp_flag & FAILCOUNT_MASK) + 1; 8500Sstevel@tonic-gate spw->sp_flag &= ~FAILCOUNT_MASK; 8510Sstevel@tonic-gate spw->sp_flag |= min(FAILCOUNT_MASK, count); 8520Sstevel@tonic-gate p->data.val_i = count; 8530Sstevel@tonic-gate } 8540Sstevel@tonic-gate break; 8550Sstevel@tonic-gate case ATTR_RST_FAILED_LOGINS: 8560Sstevel@tonic-gate p->data.val_i = spw->sp_flag & FAILCOUNT_MASK; 8570Sstevel@tonic-gate spw->sp_flag &= ~FAILCOUNT_MASK; 8580Sstevel@tonic-gate break; 8590Sstevel@tonic-gate default: 8600Sstevel@tonic-gate break; 8610Sstevel@tonic-gate } 8620Sstevel@tonic-gate } 8630Sstevel@tonic-gate 8640Sstevel@tonic-gate /* 8650Sstevel@tonic-gate * What should the new aging values look like? 8660Sstevel@tonic-gate * 8670Sstevel@tonic-gate * There are a number of different conditions 8680Sstevel@tonic-gate * 8690Sstevel@tonic-gate * a) aging is already configured: don't touch it 8700Sstevel@tonic-gate * 8710Sstevel@tonic-gate * b) disable_aging is set: disable aging 8720Sstevel@tonic-gate * 8730Sstevel@tonic-gate * c) aging is not configured: turn on default aging; 8740Sstevel@tonic-gate * 8750Sstevel@tonic-gate * b) and c) of course only if aging_needed and !aging_set. 8760Sstevel@tonic-gate * (i.e., password changed, and aging values not changed) 8770Sstevel@tonic-gate */ 8780Sstevel@tonic-gate 8790Sstevel@tonic-gate if (spw != NULL && spw->sp_max <= 0) { 8800Sstevel@tonic-gate /* a) aging not yet configured */ 8810Sstevel@tonic-gate if (aging_needed && !aging_set) { 8820Sstevel@tonic-gate if (disable_aging) { 8830Sstevel@tonic-gate /* b) turn off aging */ 8840Sstevel@tonic-gate spw->sp_min = spw->sp_max = spw->sp_warn = -1; 8850Sstevel@tonic-gate } else { 8860Sstevel@tonic-gate /* c) */ 8870Sstevel@tonic-gate turn_on_default_aging(spw); 8880Sstevel@tonic-gate } 8890Sstevel@tonic-gate } 8900Sstevel@tonic-gate } 8910Sstevel@tonic-gate 8920Sstevel@tonic-gate return (PWU_SUCCESS); 8930Sstevel@tonic-gate } 8940Sstevel@tonic-gate 8950Sstevel@tonic-gate /* 8960Sstevel@tonic-gate * files_update_shadow(char *name, struct spwd *spwd) 8970Sstevel@tonic-gate * 8980Sstevel@tonic-gate * update the shadow password file SHADOW to contain the spwd structure 8990Sstevel@tonic-gate * "spwd" for user "name" 9000Sstevel@tonic-gate */ 9010Sstevel@tonic-gate int 9020Sstevel@tonic-gate files_update_shadow(char *name, struct spwd *spwd) 9030Sstevel@tonic-gate { 9040Sstevel@tonic-gate struct stat64 stbuf; 9050Sstevel@tonic-gate FILE *dst; 9060Sstevel@tonic-gate FILE *src; 9070Sstevel@tonic-gate struct spwd cur; 9080Sstevel@tonic-gate char buf[SPW_SCRATCH_SIZE]; 9090Sstevel@tonic-gate int tempfd; 9100Sstevel@tonic-gate mode_t filemode; 9110Sstevel@tonic-gate int result = -1; 9120Sstevel@tonic-gate int err = PWU_SUCCESS; 9130Sstevel@tonic-gate 9140Sstevel@tonic-gate /* Mode of the shadow file should be 400 or 000 */ 9150Sstevel@tonic-gate if (stat64(SHADOW, &stbuf) < 0) { 9160Sstevel@tonic-gate err = PWU_STAT_FAILED; 9170Sstevel@tonic-gate goto shadow_exit; 9180Sstevel@tonic-gate } 9190Sstevel@tonic-gate 9200Sstevel@tonic-gate /* copy mode from current shadow file (0400 or 0000) */ 9210Sstevel@tonic-gate filemode = stbuf.st_mode & S_IRUSR; 9220Sstevel@tonic-gate 9230Sstevel@tonic-gate /* 9240Sstevel@tonic-gate * we can't specify filemodes to fopen(), and we SHOULD NOT 9250Sstevel@tonic-gate * set umask in multi-thread safe libraries, so we use 9260Sstevel@tonic-gate * a combination of open() and fdopen() 9270Sstevel@tonic-gate */ 9280Sstevel@tonic-gate tempfd = open(SHADTEMP, O_WRONLY|O_CREAT|O_TRUNC, filemode); 9290Sstevel@tonic-gate if (tempfd < 0) { 9300Sstevel@tonic-gate err = PWU_OPEN_FAILED; 9310Sstevel@tonic-gate goto shadow_exit; 9320Sstevel@tonic-gate } 9330Sstevel@tonic-gate (void) fchown(tempfd, (uid_t)0, stbuf.st_gid); 9340Sstevel@tonic-gate 9351914Scasper if ((dst = fdopen(tempfd, "wF")) == NULL) { 9360Sstevel@tonic-gate err = PWU_OPEN_FAILED; 9370Sstevel@tonic-gate goto shadow_exit; 9380Sstevel@tonic-gate } 9390Sstevel@tonic-gate 9401914Scasper if ((src = fopen(SHADOW, "rF")) == NULL) { 9410Sstevel@tonic-gate err = PWU_OPEN_FAILED; 9420Sstevel@tonic-gate (void) fclose(dst); 9430Sstevel@tonic-gate (void) unlink(SHADTEMP); 9440Sstevel@tonic-gate goto shadow_exit; 9450Sstevel@tonic-gate } 9460Sstevel@tonic-gate 9470Sstevel@tonic-gate /* 9480Sstevel@tonic-gate * copy old shadow to temporary file while replacing the entry 9490Sstevel@tonic-gate * that matches "name". 9500Sstevel@tonic-gate */ 9510Sstevel@tonic-gate while (fgetspent_r(src, &cur, buf, sizeof (buf)) != NULL) { 9520Sstevel@tonic-gate 9530Sstevel@tonic-gate if (strcmp(cur.sp_namp, name) == 0) 9540Sstevel@tonic-gate result = putspent(spwd, dst); 9550Sstevel@tonic-gate else 9560Sstevel@tonic-gate result = putspent(&cur, dst); 9570Sstevel@tonic-gate 9580Sstevel@tonic-gate if (result != 0) { 9590Sstevel@tonic-gate err = PWU_WRITE_FAILED; 9600Sstevel@tonic-gate (void) fclose(src); 9610Sstevel@tonic-gate (void) fclose(dst); 9620Sstevel@tonic-gate goto shadow_exit; 9630Sstevel@tonic-gate } 9640Sstevel@tonic-gate } 9650Sstevel@tonic-gate 9660Sstevel@tonic-gate (void) fclose(src); 9670Sstevel@tonic-gate 9680Sstevel@tonic-gate if (fclose(dst) != 0) { 9690Sstevel@tonic-gate /* 9700Sstevel@tonic-gate * Something went wrong (ENOSPC for example). Don't 9710Sstevel@tonic-gate * use the resulting temporary file! 9720Sstevel@tonic-gate */ 9730Sstevel@tonic-gate err = PWU_CLOSE_FAILED; 9740Sstevel@tonic-gate (void) unlink(SHADTEMP); 9750Sstevel@tonic-gate goto shadow_exit; 9760Sstevel@tonic-gate } 9770Sstevel@tonic-gate 9780Sstevel@tonic-gate /* 9790Sstevel@tonic-gate * Rename stmp to shadow: 9800Sstevel@tonic-gate * 1. make sure /etc/oshadow is gone 9810Sstevel@tonic-gate * 2. ln /etc/shadow /etc/oshadow 9820Sstevel@tonic-gate * 3. mv /etc/stmp /etc/shadow 9830Sstevel@tonic-gate */ 9840Sstevel@tonic-gate if (unlink(OSHADOW) && access(OSHADOW, 0) == 0) { 9850Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 9860Sstevel@tonic-gate (void) unlink(SHADTEMP); 9870Sstevel@tonic-gate goto shadow_exit; 9880Sstevel@tonic-gate } 9890Sstevel@tonic-gate 9900Sstevel@tonic-gate if (link(SHADOW, OSHADOW) == -1) { 9910Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 9920Sstevel@tonic-gate (void) unlink(SHADTEMP); 9930Sstevel@tonic-gate goto shadow_exit; 9940Sstevel@tonic-gate } 9950Sstevel@tonic-gate 9960Sstevel@tonic-gate if (rename(SHADTEMP, SHADOW) == -1) { 9970Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 9980Sstevel@tonic-gate (void) unlink(SHADTEMP); 9990Sstevel@tonic-gate goto shadow_exit; 10000Sstevel@tonic-gate } 10010Sstevel@tonic-gate (void) unlink(OSHADOW); 10020Sstevel@tonic-gate 10030Sstevel@tonic-gate shadow_exit: 10040Sstevel@tonic-gate return (err); 10050Sstevel@tonic-gate } 10060Sstevel@tonic-gate 10070Sstevel@tonic-gate int 10080Sstevel@tonic-gate files_update_passwd(char *name, struct passwd *pwd) 10090Sstevel@tonic-gate { 10100Sstevel@tonic-gate struct stat64 stbuf; 10110Sstevel@tonic-gate FILE *src, *dst; 10120Sstevel@tonic-gate int tempfd; 10130Sstevel@tonic-gate struct passwd cur; 10140Sstevel@tonic-gate char buf[PWD_SCRATCH_SIZE]; 10150Sstevel@tonic-gate int result; 10160Sstevel@tonic-gate int err = PWU_SUCCESS; 10170Sstevel@tonic-gate 10180Sstevel@tonic-gate if (stat64(PASSWD, &stbuf) < 0) { 10190Sstevel@tonic-gate err = PWU_STAT_FAILED; 10200Sstevel@tonic-gate goto passwd_exit; 10210Sstevel@tonic-gate } 10220Sstevel@tonic-gate 10230Sstevel@tonic-gate /* see files_update_shadow() for open()+fdopen() rationale */ 10240Sstevel@tonic-gate 10250Sstevel@tonic-gate if ((tempfd = open(PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { 10260Sstevel@tonic-gate err = PWU_OPEN_FAILED; 10270Sstevel@tonic-gate goto passwd_exit; 10280Sstevel@tonic-gate } 10291914Scasper if ((dst = fdopen(tempfd, "wF")) == NULL) { 10300Sstevel@tonic-gate err = PWU_OPEN_FAILED; 10310Sstevel@tonic-gate goto passwd_exit; 10320Sstevel@tonic-gate } 10331914Scasper if ((src = fopen(PASSWD, "rF")) == NULL) { 10340Sstevel@tonic-gate err = PWU_OPEN_FAILED; 10350Sstevel@tonic-gate (void) fclose(dst); 10360Sstevel@tonic-gate (void) unlink(PASSTEMP); 10370Sstevel@tonic-gate goto passwd_exit; 10380Sstevel@tonic-gate } 10390Sstevel@tonic-gate 10400Sstevel@tonic-gate /* 10410Sstevel@tonic-gate * copy old password entries to temporary file while replacing 10420Sstevel@tonic-gate * the entry that matches "name" 10430Sstevel@tonic-gate */ 10440Sstevel@tonic-gate while (fgetpwent_r(src, &cur, buf, sizeof (buf)) != NULL) { 10450Sstevel@tonic-gate if (strcmp(cur.pw_name, name) == 0) 10460Sstevel@tonic-gate result = putpwent(pwd, dst); 10470Sstevel@tonic-gate else 10480Sstevel@tonic-gate result = putpwent(&cur, dst); 10490Sstevel@tonic-gate if (result != 0) { 10500Sstevel@tonic-gate err = PWU_WRITE_FAILED; 10510Sstevel@tonic-gate (void) fclose(src); 10520Sstevel@tonic-gate (void) fclose(dst); 10530Sstevel@tonic-gate goto passwd_exit; 10540Sstevel@tonic-gate } 10550Sstevel@tonic-gate } 10560Sstevel@tonic-gate 10570Sstevel@tonic-gate (void) fclose(src); 10580Sstevel@tonic-gate if (fclose(dst) != 0) { 10590Sstevel@tonic-gate err = PWU_CLOSE_FAILED; 10600Sstevel@tonic-gate goto passwd_exit; /* Don't trust the temporary file */ 10610Sstevel@tonic-gate } 10620Sstevel@tonic-gate 10630Sstevel@tonic-gate /* Rename temp to passwd */ 10640Sstevel@tonic-gate if (unlink(OPASSWD) && access(OPASSWD, 0) == 0) { 10650Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 10660Sstevel@tonic-gate (void) unlink(PASSTEMP); 10670Sstevel@tonic-gate goto passwd_exit; 10680Sstevel@tonic-gate } 10690Sstevel@tonic-gate 10700Sstevel@tonic-gate if (link(PASSWD, OPASSWD) == -1) { 10710Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 10720Sstevel@tonic-gate (void) unlink(PASSTEMP); 10730Sstevel@tonic-gate goto passwd_exit; 10740Sstevel@tonic-gate } 10750Sstevel@tonic-gate 10760Sstevel@tonic-gate if (rename(PASSTEMP, PASSWD) == -1) { 10770Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 10780Sstevel@tonic-gate (void) unlink(PASSTEMP); 10790Sstevel@tonic-gate goto passwd_exit; 10800Sstevel@tonic-gate } 10810Sstevel@tonic-gate 10820Sstevel@tonic-gate (void) chmod(PASSWD, 0644); 10830Sstevel@tonic-gate 10840Sstevel@tonic-gate passwd_exit: 10850Sstevel@tonic-gate return (err); 10860Sstevel@tonic-gate 10870Sstevel@tonic-gate } 10880Sstevel@tonic-gate 10890Sstevel@tonic-gate /* 10900Sstevel@tonic-gate * files_putpwnam(name, oldpw, dummy, rep, buf) 10910Sstevel@tonic-gate * 10920Sstevel@tonic-gate * store the password attributes contained in "buf" in /etc/passwd and 10930Sstevel@tonic-gate * /etc/shadow. The dummy parameter is a placeholder for NIS+ 10940Sstevel@tonic-gate * updates where the "oldrpc" password is passed. 10950Sstevel@tonic-gate */ 10960Sstevel@tonic-gate /*ARGSUSED*/ 10970Sstevel@tonic-gate int 10980Sstevel@tonic-gate files_putpwnam(char *name, char *oldpw, char *dummy, 10990Sstevel@tonic-gate pwu_repository_t *rep, void *buf) 11000Sstevel@tonic-gate { 11010Sstevel@tonic-gate struct pwbuf *pwbuf = (struct pwbuf *)buf; 11020Sstevel@tonic-gate int result = PWU_SUCCESS; 11030Sstevel@tonic-gate 11040Sstevel@tonic-gate if (pwbuf->pwd) { 11050Sstevel@tonic-gate result = files_update_passwd(name, pwbuf->pwd); 11060Sstevel@tonic-gate } 11070Sstevel@tonic-gate 11080Sstevel@tonic-gate if (result == PWU_SUCCESS && pwbuf->spwd) { 11090Sstevel@tonic-gate if (pwbuf->update_history != 0) { 11100Sstevel@tonic-gate debug("update_history = %d", pwbuf->update_history); 11110Sstevel@tonic-gate result = files_update_history(name, pwbuf->spwd); 11120Sstevel@tonic-gate } else { 11130Sstevel@tonic-gate debug("no password change"); 11140Sstevel@tonic-gate } 11150Sstevel@tonic-gate if (result == PWU_SUCCESS) { 11160Sstevel@tonic-gate result = files_update_shadow(name, pwbuf->spwd); 11170Sstevel@tonic-gate } 11180Sstevel@tonic-gate } 11190Sstevel@tonic-gate 11200Sstevel@tonic-gate if (pwbuf->pwd) { 11210Sstevel@tonic-gate (void) memset(pwbuf->pwd, 0, sizeof (struct passwd)); 11220Sstevel@tonic-gate (void) memset(pwbuf->pwd_scratch, 0, PWD_SCRATCH_SIZE); 11230Sstevel@tonic-gate free(pwbuf->pwd); 11240Sstevel@tonic-gate free(pwbuf->pwd_scratch); 11250Sstevel@tonic-gate } 11260Sstevel@tonic-gate if (pwbuf->spwd) { 11270Sstevel@tonic-gate (void) memset(pwbuf->spwd, 0, sizeof (struct spwd)); 11280Sstevel@tonic-gate (void) memset(pwbuf->spwd_scratch, 0, SPW_SCRATCH_SIZE); 11290Sstevel@tonic-gate free(pwbuf->spwd); 11300Sstevel@tonic-gate free(pwbuf->spwd_scratch); 11310Sstevel@tonic-gate } 11320Sstevel@tonic-gate if (pwbuf->new_sp_pwdp) { 11330Sstevel@tonic-gate free(pwbuf->new_sp_pwdp); 11340Sstevel@tonic-gate } 11350Sstevel@tonic-gate 11360Sstevel@tonic-gate return (result); 11370Sstevel@tonic-gate } 11380Sstevel@tonic-gate 11390Sstevel@tonic-gate /* 11400Sstevel@tonic-gate * NOTE: This is all covered under the repository lock held for updating 11410Sstevel@tonic-gate * passwd(4) and shadow(4). 11420Sstevel@tonic-gate */ 11430Sstevel@tonic-gate int 11440Sstevel@tonic-gate files_update_history(char *name, struct spwd *spwd) 11450Sstevel@tonic-gate { 11460Sstevel@tonic-gate int histsize; 11470Sstevel@tonic-gate int tmpfd; 11480Sstevel@tonic-gate FILE *src; /* history database file */ 11490Sstevel@tonic-gate FILE *dst; /* temp history database being updated */ 11500Sstevel@tonic-gate struct stat64 statbuf; 11510Sstevel@tonic-gate char buf[MAX_LOGNAME + MAXHISTORY + 11525187Sjjj (MAXHISTORY * CRYPT_MAXCIPHERTEXTLEN)+1]; 11530Sstevel@tonic-gate int found; 11540Sstevel@tonic-gate 11550Sstevel@tonic-gate if ((histsize = def_getint("HISTORY=", DEFHISTORY)) == 0) { 11560Sstevel@tonic-gate debug("files_update_history(%s) no history, unlinking", name); 11570Sstevel@tonic-gate (void) unlink(HISTORY); 11580Sstevel@tonic-gate return (PWU_SUCCESS); /* no history update defined */ 11590Sstevel@tonic-gate } 11600Sstevel@tonic-gate debug("files_update_history(%s, %s) histsize = %d", name, spwd->sp_pwdp, 11610Sstevel@tonic-gate histsize); 11620Sstevel@tonic-gate 11630Sstevel@tonic-gate if (histsize > MAXHISTORY) 11640Sstevel@tonic-gate histsize = MAXHISTORY; 11650Sstevel@tonic-gate if ((tmpfd = open(HISTEMP, O_WRONLY|O_CREAT|O_TRUNC, HISTMODE)) < 0) { 11660Sstevel@tonic-gate return (PWU_OPEN_FAILED); 11670Sstevel@tonic-gate } 11680Sstevel@tonic-gate (void) fchown(tmpfd, (uid_t)0, (gid_t)0); 11690Sstevel@tonic-gate 11700Sstevel@tonic-gate /* get ready to copy */ 11711914Scasper if (((src = fopen(HISTORY, "rF")) == NULL) && 11720Sstevel@tonic-gate (errno != ENOENT)) { 11730Sstevel@tonic-gate (void) unlink(HISTEMP); 11740Sstevel@tonic-gate return (PWU_OPEN_FAILED); 11750Sstevel@tonic-gate } 11761914Scasper if ((dst = fdopen(tmpfd, "wF")) == NULL) { 11770Sstevel@tonic-gate (void) fclose(src); 11780Sstevel@tonic-gate (void) unlink(HISTEMP); 11790Sstevel@tonic-gate return (PWU_OPEN_FAILED); 11800Sstevel@tonic-gate } 11810Sstevel@tonic-gate 11820Sstevel@tonic-gate /* Copy and update if found. Add if not found. */ 11830Sstevel@tonic-gate 11840Sstevel@tonic-gate found = 0; 11850Sstevel@tonic-gate 11860Sstevel@tonic-gate while ((src != NULL) && 11870Sstevel@tonic-gate (fgets(buf, sizeof (buf), src) != NULL)) { 11880Sstevel@tonic-gate char *user; 11890Sstevel@tonic-gate char *last; 11900Sstevel@tonic-gate 11910Sstevel@tonic-gate /* get username field */ 11920Sstevel@tonic-gate user = strtok_r(buf, ":", &last); 11930Sstevel@tonic-gate 11940Sstevel@tonic-gate #ifdef DEBUG 11950Sstevel@tonic-gate debug("files_update_history: read=\"%s\"", user); 11960Sstevel@tonic-gate #endif /* DEBUG */ 11970Sstevel@tonic-gate 11980Sstevel@tonic-gate if (strcmp(user, name) == 0) { 11990Sstevel@tonic-gate char *crypt; 12000Sstevel@tonic-gate int i; 12010Sstevel@tonic-gate 12020Sstevel@tonic-gate /* found user, update */ 12030Sstevel@tonic-gate found++; 12040Sstevel@tonic-gate (void) fprintf(dst, "%s:%s:", name, spwd->sp_pwdp); 12050Sstevel@tonic-gate debug("files_update_history: update user\n" 12060Sstevel@tonic-gate "\t%s:%s:", name, spwd->sp_pwdp); 12070Sstevel@tonic-gate 12080Sstevel@tonic-gate /* get old crypted password history */ 12090Sstevel@tonic-gate for (i = 0; i < MAXHISTORY-1; i++) { 12100Sstevel@tonic-gate crypt = strtok_r(NULL, ":", &last); 12110Sstevel@tonic-gate if (crypt == NULL || 12120Sstevel@tonic-gate *crypt == '\n') { 12130Sstevel@tonic-gate break; 12140Sstevel@tonic-gate } 12150Sstevel@tonic-gate (void) fprintf(dst, "%s:", crypt); 12160Sstevel@tonic-gate debug("\t%d = %s:", i+1, crypt); 12170Sstevel@tonic-gate } 12180Sstevel@tonic-gate (void) fprintf(dst, "\n"); 12190Sstevel@tonic-gate } else { 12200Sstevel@tonic-gate 12210Sstevel@tonic-gate /* copy other users to updated file */ 12220Sstevel@tonic-gate (void) fprintf(dst, "%s:%s", user, last); 12230Sstevel@tonic-gate #ifdef DEBUG 12240Sstevel@tonic-gate debug("files_update_history: copy line %s", 12250Sstevel@tonic-gate user); 12260Sstevel@tonic-gate #endif /* DEBUG */ 12270Sstevel@tonic-gate } 12280Sstevel@tonic-gate } 12290Sstevel@tonic-gate 12300Sstevel@tonic-gate if (found == 0) { 12310Sstevel@tonic-gate 12320Sstevel@tonic-gate /* user not found, add to history file */ 12330Sstevel@tonic-gate (void) fprintf(dst, "%s:%s:\n", name, spwd->sp_pwdp); 12340Sstevel@tonic-gate debug("files_update_history: add line\n" 12350Sstevel@tonic-gate "\t%s:%s:", name, spwd->sp_pwdp); 12360Sstevel@tonic-gate } 12370Sstevel@tonic-gate 12380Sstevel@tonic-gate (void) fclose(src); 12390Sstevel@tonic-gate 12400Sstevel@tonic-gate /* If something messed up in file system, loose the update */ 12410Sstevel@tonic-gate if (fclose(dst) != 0) { 12420Sstevel@tonic-gate 12430Sstevel@tonic-gate debug("files_update_history: update file close failed %d", 12440Sstevel@tonic-gate errno); 12450Sstevel@tonic-gate (void) unlink(HISTEMP); 12460Sstevel@tonic-gate return (PWU_CLOSE_FAILED); 12470Sstevel@tonic-gate } 12480Sstevel@tonic-gate 12490Sstevel@tonic-gate /* 12500Sstevel@tonic-gate * rename history to ohistory, 12510Sstevel@tonic-gate * rename tmp to history, 12520Sstevel@tonic-gate * unlink ohistory. 12530Sstevel@tonic-gate */ 12540Sstevel@tonic-gate 12550Sstevel@tonic-gate (void) unlink(OHISTORY); 12560Sstevel@tonic-gate 12570Sstevel@tonic-gate if (stat64(OHISTORY, &statbuf) == 0 || 12580Sstevel@tonic-gate ((src != NULL) && (link(HISTORY, OHISTORY) != 0)) || 12590Sstevel@tonic-gate rename(HISTEMP, HISTORY) != 0) { 12600Sstevel@tonic-gate 12610Sstevel@tonic-gate /* old history won't go away, loose the update */ 12620Sstevel@tonic-gate debug("files_update_history: update file rename failed %d", 12630Sstevel@tonic-gate errno); 12640Sstevel@tonic-gate (void) unlink(HISTEMP); 12650Sstevel@tonic-gate return (PWU_UPDATE_FAILED); 12660Sstevel@tonic-gate } 12670Sstevel@tonic-gate 12680Sstevel@tonic-gate (void) unlink(OHISTORY); 12690Sstevel@tonic-gate return (PWU_SUCCESS); 12700Sstevel@tonic-gate } 1271