1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/types.h> 30*0Sstevel@tonic-gate #include <fcntl.h> 31*0Sstevel@tonic-gate #include <errno.h> 32*0Sstevel@tonic-gate #include <stdlib.h> 33*0Sstevel@tonic-gate #include <sys/stat.h> 34*0Sstevel@tonic-gate #include <pwd.h> 35*0Sstevel@tonic-gate #include <shadow.h> 36*0Sstevel@tonic-gate #include <string.h> 37*0Sstevel@tonic-gate #include <strings.h> 38*0Sstevel@tonic-gate #include <stdlib.h> 39*0Sstevel@tonic-gate #include <unistd.h> 40*0Sstevel@tonic-gate #include <nss_dbdefs.h> 41*0Sstevel@tonic-gate #include <macros.h> 42*0Sstevel@tonic-gate #include <syslog.h> 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate #include <limits.h> /* LOGNAME_MAX -- max Solaris user name */ 45*0Sstevel@tonic-gate 46*0Sstevel@tonic-gate #include "passwdutil.h" 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate int files_lock(void); 49*0Sstevel@tonic-gate int files_unlock(void); 50*0Sstevel@tonic-gate int files_checkhistory(char *user, char *passwd, pwu_repository_t *rep); 51*0Sstevel@tonic-gate int files_getattr(char *name, attrlist *item, pwu_repository_t *rep); 52*0Sstevel@tonic-gate int files_getpwnam(char *name, attrlist *items, pwu_repository_t *rep, 53*0Sstevel@tonic-gate void **buf); 54*0Sstevel@tonic-gate int files_update(attrlist *items, pwu_repository_t *rep, void *buf); 55*0Sstevel@tonic-gate int files_putpwnam(char *name, char *oldpw, char *dummy, 56*0Sstevel@tonic-gate pwu_repository_t *rep, void *buf); 57*0Sstevel@tonic-gate int files_user_to_authenticate(char *name, pwu_repository_t *rep, 58*0Sstevel@tonic-gate char **auth_user, int *privileged); 59*0Sstevel@tonic-gate 60*0Sstevel@tonic-gate static int files_update_history(char *name, struct spwd *spwd); 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate /* 63*0Sstevel@tonic-gate * files function pointer table, used by passwdutil_init to initialize 64*0Sstevel@tonic-gate * the global Repository-OPerations table "rops" 65*0Sstevel@tonic-gate */ 66*0Sstevel@tonic-gate struct repops files_repops = { 67*0Sstevel@tonic-gate files_checkhistory, 68*0Sstevel@tonic-gate files_getattr, 69*0Sstevel@tonic-gate files_getpwnam, 70*0Sstevel@tonic-gate files_update, 71*0Sstevel@tonic-gate files_putpwnam, 72*0Sstevel@tonic-gate files_user_to_authenticate, 73*0Sstevel@tonic-gate files_lock, 74*0Sstevel@tonic-gate files_unlock 75*0Sstevel@tonic-gate }; 76*0Sstevel@tonic-gate 77*0Sstevel@tonic-gate /* 78*0Sstevel@tonic-gate * this structure defines the buffer used to keep state between 79*0Sstevel@tonic-gate * get/update/put calls 80*0Sstevel@tonic-gate */ 81*0Sstevel@tonic-gate struct pwbuf { 82*0Sstevel@tonic-gate int update_history; 83*0Sstevel@tonic-gate struct passwd *pwd; 84*0Sstevel@tonic-gate char *pwd_scratch; 85*0Sstevel@tonic-gate struct spwd *spwd; 86*0Sstevel@tonic-gate char *spwd_scratch; 87*0Sstevel@tonic-gate char *new_sp_pwdp; 88*0Sstevel@tonic-gate }; 89*0Sstevel@tonic-gate 90*0Sstevel@tonic-gate /* 91*0Sstevel@tonic-gate * We should use sysconf, but there is no sysconf name for SHADOW 92*0Sstevel@tonic-gate * so we use these from nss_dbdefs 93*0Sstevel@tonic-gate */ 94*0Sstevel@tonic-gate #define PWD_SCRATCH_SIZE NSS_LINELEN_PASSWD 95*0Sstevel@tonic-gate #define SPW_SCRATCH_SIZE NSS_LINELEN_SHADOW 96*0Sstevel@tonic-gate 97*0Sstevel@tonic-gate /* 98*0Sstevel@tonic-gate * lock functions for files repository 99*0Sstevel@tonic-gate */ 100*0Sstevel@tonic-gate int 101*0Sstevel@tonic-gate files_lock(void) 102*0Sstevel@tonic-gate { 103*0Sstevel@tonic-gate int res; 104*0Sstevel@tonic-gate 105*0Sstevel@tonic-gate if (lckpwdf()) { 106*0Sstevel@tonic-gate switch (errno) { 107*0Sstevel@tonic-gate case EINTR: 108*0Sstevel@tonic-gate res = PWU_BUSY; 109*0Sstevel@tonic-gate break; 110*0Sstevel@tonic-gate case EACCES: 111*0Sstevel@tonic-gate res = PWU_DENIED; 112*0Sstevel@tonic-gate break; 113*0Sstevel@tonic-gate case 0: 114*0Sstevel@tonic-gate res = PWU_SUCCESS; 115*0Sstevel@tonic-gate break; 116*0Sstevel@tonic-gate } 117*0Sstevel@tonic-gate } else 118*0Sstevel@tonic-gate res = PWU_SUCCESS; 119*0Sstevel@tonic-gate 120*0Sstevel@tonic-gate return (res); 121*0Sstevel@tonic-gate } 122*0Sstevel@tonic-gate 123*0Sstevel@tonic-gate int 124*0Sstevel@tonic-gate files_unlock(void) 125*0Sstevel@tonic-gate { 126*0Sstevel@tonic-gate if (ulckpwdf()) 127*0Sstevel@tonic-gate return (PWU_SYSTEM_ERROR); 128*0Sstevel@tonic-gate 129*0Sstevel@tonic-gate return (PWU_SUCCESS); 130*0Sstevel@tonic-gate } 131*0Sstevel@tonic-gate 132*0Sstevel@tonic-gate /* 133*0Sstevel@tonic-gate * files_privileged 134*0Sstevel@tonic-gate * 135*0Sstevel@tonic-gate * Are we a privileged user with regard to the files repository? 136*0Sstevel@tonic-gate */ 137*0Sstevel@tonic-gate int 138*0Sstevel@tonic-gate files_privileged(void) 139*0Sstevel@tonic-gate { 140*0Sstevel@tonic-gate return (getuid() == 0); 141*0Sstevel@tonic-gate } 142*0Sstevel@tonic-gate 143*0Sstevel@tonic-gate /* 144*0Sstevel@tonic-gate * 145*0Sstevel@tonic-gate * private_getpwnam_r() 146*0Sstevel@tonic-gate * 147*0Sstevel@tonic-gate * A private implementation of getpwnam_r which does *not* fall back to 148*0Sstevel@tonic-gate * other services possibly defined in nsswitch.conf 149*0Sstevel@tonic-gate * 150*0Sstevel@tonic-gate * behaves like getpwnam_r(). 151*0Sstevel@tonic-gate */ 152*0Sstevel@tonic-gate struct passwd * 153*0Sstevel@tonic-gate private_getpwnam_r(const char *name, struct passwd *result, char *buffer, 154*0Sstevel@tonic-gate int buflen) 155*0Sstevel@tonic-gate { 156*0Sstevel@tonic-gate FILE *fp; 157*0Sstevel@tonic-gate int found; 158*0Sstevel@tonic-gate 159*0Sstevel@tonic-gate if ((fp = fopen(PASSWD, "r")) == NULL) 160*0Sstevel@tonic-gate return (NULL); 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate found = 0; 163*0Sstevel@tonic-gate while (!found && fgetpwent_r(fp, result, buffer, buflen) != NULL) { 164*0Sstevel@tonic-gate if (strcmp(name, result->pw_name) == 0) 165*0Sstevel@tonic-gate found = 1; 166*0Sstevel@tonic-gate } 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate (void) fclose(fp); 169*0Sstevel@tonic-gate 170*0Sstevel@tonic-gate if (!found) { 171*0Sstevel@tonic-gate (void) memset(buffer, 0, buflen); 172*0Sstevel@tonic-gate (void) memset(result, 0, sizeof (*result)); 173*0Sstevel@tonic-gate return (NULL); 174*0Sstevel@tonic-gate } 175*0Sstevel@tonic-gate 176*0Sstevel@tonic-gate return (result); 177*0Sstevel@tonic-gate } 178*0Sstevel@tonic-gate 179*0Sstevel@tonic-gate /* 180*0Sstevel@tonic-gate * private_getspnam_r() 181*0Sstevel@tonic-gate * 182*0Sstevel@tonic-gate * A private implementation of getspnam_r which does *not* fall back to 183*0Sstevel@tonic-gate * other services possibly defined in nsswitch.conf. 184*0Sstevel@tonic-gate * 185*0Sstevel@tonic-gate * Behaves like getspnam_r(). Since we use fgetspent_t(), all numeric 186*0Sstevel@tonic-gate * fields that are undefined in /etc/shadow will be set to -1. 187*0Sstevel@tonic-gate * 188*0Sstevel@tonic-gate */ 189*0Sstevel@tonic-gate struct spwd * 190*0Sstevel@tonic-gate private_getspnam_r(const char *name, struct spwd *result, char *buffer, 191*0Sstevel@tonic-gate int buflen) 192*0Sstevel@tonic-gate { 193*0Sstevel@tonic-gate FILE *fp; 194*0Sstevel@tonic-gate int found; 195*0Sstevel@tonic-gate 196*0Sstevel@tonic-gate fp = fopen(SHADOW, "r"); 197*0Sstevel@tonic-gate if (fp == NULL) 198*0Sstevel@tonic-gate return (NULL); 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate found = 0; 201*0Sstevel@tonic-gate while (!found && fgetspent_r(fp, result, buffer, buflen) != NULL) { 202*0Sstevel@tonic-gate if (strcmp(name, result->sp_namp) == 0) 203*0Sstevel@tonic-gate found = 1; 204*0Sstevel@tonic-gate } 205*0Sstevel@tonic-gate 206*0Sstevel@tonic-gate (void) fclose(fp); 207*0Sstevel@tonic-gate 208*0Sstevel@tonic-gate if (!found) { 209*0Sstevel@tonic-gate (void) memset(buffer, 0, buflen); 210*0Sstevel@tonic-gate (void) memset(result, 0, sizeof (*result)); 211*0Sstevel@tonic-gate return (NULL); 212*0Sstevel@tonic-gate } 213*0Sstevel@tonic-gate return (result); 214*0Sstevel@tonic-gate } 215*0Sstevel@tonic-gate 216*0Sstevel@tonic-gate /* 217*0Sstevel@tonic-gate * files_getpwnam(name, items, rep, buf) 218*0Sstevel@tonic-gate * 219*0Sstevel@tonic-gate */ 220*0Sstevel@tonic-gate /*ARGSUSED*/ 221*0Sstevel@tonic-gate int 222*0Sstevel@tonic-gate files_getpwnam(char *name, attrlist *items, pwu_repository_t *rep, void **buf) 223*0Sstevel@tonic-gate { 224*0Sstevel@tonic-gate attrlist *p; 225*0Sstevel@tonic-gate struct pwbuf *pwbuf; 226*0Sstevel@tonic-gate int err = PWU_SUCCESS; 227*0Sstevel@tonic-gate 228*0Sstevel@tonic-gate *buf = calloc(1, sizeof (struct pwbuf)); 229*0Sstevel@tonic-gate pwbuf = (struct pwbuf *)*buf; 230*0Sstevel@tonic-gate 231*0Sstevel@tonic-gate /* 232*0Sstevel@tonic-gate * determine which password structure (/etc/passwd or /etc/shadow) 233*0Sstevel@tonic-gate * we need for the items we need to update 234*0Sstevel@tonic-gate */ 235*0Sstevel@tonic-gate for (p = items; p != NULL; p = p->next) { 236*0Sstevel@tonic-gate switch (p->type) { 237*0Sstevel@tonic-gate case ATTR_NAME: 238*0Sstevel@tonic-gate case ATTR_UID: 239*0Sstevel@tonic-gate case ATTR_GID: 240*0Sstevel@tonic-gate case ATTR_AGE: 241*0Sstevel@tonic-gate case ATTR_COMMENT: 242*0Sstevel@tonic-gate case ATTR_GECOS: 243*0Sstevel@tonic-gate case ATTR_HOMEDIR: 244*0Sstevel@tonic-gate case ATTR_SHELL: 245*0Sstevel@tonic-gate if (pwbuf->pwd == NULL) { 246*0Sstevel@tonic-gate pwbuf->pwd = malloc(sizeof (struct passwd)); 247*0Sstevel@tonic-gate if (pwbuf->pwd == NULL) { 248*0Sstevel@tonic-gate err = PWU_NOMEM; 249*0Sstevel@tonic-gate goto error; 250*0Sstevel@tonic-gate } 251*0Sstevel@tonic-gate } 252*0Sstevel@tonic-gate break; 253*0Sstevel@tonic-gate case ATTR_PASSWD: 254*0Sstevel@tonic-gate case ATTR_PASSWD_SERVER_POLICY: 255*0Sstevel@tonic-gate case ATTR_LSTCHG: 256*0Sstevel@tonic-gate case ATTR_MIN: 257*0Sstevel@tonic-gate case ATTR_MAX: 258*0Sstevel@tonic-gate case ATTR_WARN: 259*0Sstevel@tonic-gate case ATTR_INACT: 260*0Sstevel@tonic-gate case ATTR_EXPIRE: 261*0Sstevel@tonic-gate case ATTR_FLAG: 262*0Sstevel@tonic-gate case ATTR_LOCK_ACCOUNT: 263*0Sstevel@tonic-gate case ATTR_EXPIRE_PASSWORD: 264*0Sstevel@tonic-gate case ATTR_FAILED_LOGINS: 265*0Sstevel@tonic-gate case ATTR_INCR_FAILED_LOGINS: 266*0Sstevel@tonic-gate case ATTR_RST_FAILED_LOGINS: 267*0Sstevel@tonic-gate case ATTR_NOLOGIN_ACCOUNT: 268*0Sstevel@tonic-gate case ATTR_UNLOCK_ACCOUNT: 269*0Sstevel@tonic-gate if (pwbuf->spwd == NULL) { 270*0Sstevel@tonic-gate pwbuf->spwd = malloc(sizeof (struct spwd)); 271*0Sstevel@tonic-gate if (pwbuf->spwd == NULL) { 272*0Sstevel@tonic-gate err = PWU_NOMEM; 273*0Sstevel@tonic-gate goto error; 274*0Sstevel@tonic-gate } 275*0Sstevel@tonic-gate } 276*0Sstevel@tonic-gate break; 277*0Sstevel@tonic-gate default: 278*0Sstevel@tonic-gate /* 279*0Sstevel@tonic-gate * Some other repository might have different values 280*0Sstevel@tonic-gate * so we ignore those. 281*0Sstevel@tonic-gate */ 282*0Sstevel@tonic-gate break; 283*0Sstevel@tonic-gate } 284*0Sstevel@tonic-gate } 285*0Sstevel@tonic-gate 286*0Sstevel@tonic-gate if (pwbuf->pwd) { 287*0Sstevel@tonic-gate if ((pwbuf->pwd_scratch = malloc(PWD_SCRATCH_SIZE)) == NULL) { 288*0Sstevel@tonic-gate err = PWU_NOMEM; 289*0Sstevel@tonic-gate goto error; 290*0Sstevel@tonic-gate } 291*0Sstevel@tonic-gate if (private_getpwnam_r(name, pwbuf->pwd, pwbuf->pwd_scratch, 292*0Sstevel@tonic-gate PWD_SCRATCH_SIZE) == NULL) { 293*0Sstevel@tonic-gate err = PWU_NOT_FOUND; 294*0Sstevel@tonic-gate goto error; 295*0Sstevel@tonic-gate } 296*0Sstevel@tonic-gate } 297*0Sstevel@tonic-gate 298*0Sstevel@tonic-gate if (pwbuf->spwd) { 299*0Sstevel@tonic-gate if ((pwbuf->spwd_scratch = malloc(SPW_SCRATCH_SIZE)) == NULL) { 300*0Sstevel@tonic-gate err = PWU_NOMEM; 301*0Sstevel@tonic-gate goto error; 302*0Sstevel@tonic-gate } 303*0Sstevel@tonic-gate if (private_getspnam_r(name, pwbuf->spwd, pwbuf->spwd_scratch, 304*0Sstevel@tonic-gate SPW_SCRATCH_SIZE) == NULL) { 305*0Sstevel@tonic-gate err = PWU_NOT_FOUND; 306*0Sstevel@tonic-gate goto error; 307*0Sstevel@tonic-gate } 308*0Sstevel@tonic-gate } 309*0Sstevel@tonic-gate 310*0Sstevel@tonic-gate return (PWU_SUCCESS); 311*0Sstevel@tonic-gate error: 312*0Sstevel@tonic-gate if (pwbuf->pwd) free(pwbuf->pwd); 313*0Sstevel@tonic-gate if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch); 314*0Sstevel@tonic-gate if (pwbuf->spwd) free(pwbuf->spwd); 315*0Sstevel@tonic-gate if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch); 316*0Sstevel@tonic-gate free(pwbuf); 317*0Sstevel@tonic-gate *buf = NULL; 318*0Sstevel@tonic-gate 319*0Sstevel@tonic-gate return (err); 320*0Sstevel@tonic-gate } 321*0Sstevel@tonic-gate 322*0Sstevel@tonic-gate /* 323*0Sstevel@tonic-gate * int files_user_to_authenticate(name, rep, auth_user, privileged) 324*0Sstevel@tonic-gate * Determine which user needs to be authenticated. For files, the 325*0Sstevel@tonic-gate * possible return values are: 326*0Sstevel@tonic-gate * PWU_NOT_FOUND 327*0Sstevel@tonic-gate * PWU_SUCCESS and (auth_user == NULL || auth_user = user) 328*0Sstevel@tonic-gate * PWU_DENIED 329*0Sstevel@tonic-gate */ 330*0Sstevel@tonic-gate /*ARGSUSED*/ 331*0Sstevel@tonic-gate int 332*0Sstevel@tonic-gate files_user_to_authenticate(char *user, pwu_repository_t *rep, 333*0Sstevel@tonic-gate char **auth_user, int *privileged) 334*0Sstevel@tonic-gate { 335*0Sstevel@tonic-gate struct pwbuf *pwbuf; 336*0Sstevel@tonic-gate int res; 337*0Sstevel@tonic-gate attrlist attr_tmp[1] = { { ATTR_UID, NULL, NULL } }; 338*0Sstevel@tonic-gate 339*0Sstevel@tonic-gate /* check to see if target user is present in files */ 340*0Sstevel@tonic-gate res = files_getpwnam(user, &attr_tmp[0], rep, (void **)&pwbuf); 341*0Sstevel@tonic-gate if (res != PWU_SUCCESS) 342*0Sstevel@tonic-gate return (res); 343*0Sstevel@tonic-gate 344*0Sstevel@tonic-gate if (files_privileged()) { 345*0Sstevel@tonic-gate *auth_user = NULL; 346*0Sstevel@tonic-gate *privileged = 1; 347*0Sstevel@tonic-gate res = PWU_SUCCESS; 348*0Sstevel@tonic-gate } else { 349*0Sstevel@tonic-gate *privileged = 0; 350*0Sstevel@tonic-gate if (getuid() == pwbuf->pwd->pw_uid) { 351*0Sstevel@tonic-gate *auth_user = strdup(user); 352*0Sstevel@tonic-gate res = PWU_SUCCESS; 353*0Sstevel@tonic-gate } else { 354*0Sstevel@tonic-gate res = PWU_DENIED; 355*0Sstevel@tonic-gate } 356*0Sstevel@tonic-gate } 357*0Sstevel@tonic-gate 358*0Sstevel@tonic-gate if (pwbuf->pwd) free(pwbuf->pwd); 359*0Sstevel@tonic-gate if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch); 360*0Sstevel@tonic-gate if (pwbuf->spwd) free(pwbuf->spwd); 361*0Sstevel@tonic-gate if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch); 362*0Sstevel@tonic-gate free(pwbuf); 363*0Sstevel@tonic-gate 364*0Sstevel@tonic-gate return (res); 365*0Sstevel@tonic-gate } 366*0Sstevel@tonic-gate 367*0Sstevel@tonic-gate /* 368*0Sstevel@tonic-gate * Password history file format: 369*0Sstevel@tonic-gate * user:crypw1: ... crypwn: such that n <= MAXHISTORY 370*0Sstevel@tonic-gate */ 371*0Sstevel@tonic-gate #define HISTORY "/etc/security/passhistory" 372*0Sstevel@tonic-gate #define HISTEMP "/etc/security/pwhistemp" 373*0Sstevel@tonic-gate #define OHISTORY "/etc/security/opwhistory" 374*0Sstevel@tonic-gate #define HISTMODE S_IRUSR /* mode to create history file */ 375*0Sstevel@tonic-gate /* 376*0Sstevel@tonic-gate * XXX 377*0Sstevel@tonic-gate * 3*LOGNAME_MAX just in case there are long user names. 378*0Sstevel@tonic-gate * Traditionally Solaris LOGNAME_MAX (_POSIX_LOGIN_NAME_MAX) is 13, 379*0Sstevel@tonic-gate * but some sites often user more. 380*0Sstevel@tonic-gate * If LOGNAME_MAX ever becomes reasonable (128) and actually enforced, 381*0Sstevel@tonic-gate * fix up here. 382*0Sstevel@tonic-gate * XXX 383*0Sstevel@tonic-gate */ 384*0Sstevel@tonic-gate #define MAX_LOGNAME (3 * LOGNAME_MAX) 385*0Sstevel@tonic-gate 386*0Sstevel@tonic-gate /* 387*0Sstevel@tonic-gate * files_checkhistory - check if a user's new password is in the user's 388*0Sstevel@tonic-gate * old password history. 389*0Sstevel@tonic-gate * 390*0Sstevel@tonic-gate * Entry 391*0Sstevel@tonic-gate * user = username. 392*0Sstevel@tonic-gate * passwd = new clear text password. 393*0Sstevel@tonic-gate * 394*0Sstevel@tonic-gate * Exit 395*0Sstevel@tonic-gate * PWU_SUCCESS, passwd found in user's old password history. 396*0Sstevel@tonic-gate * The caller should only be interested and fail if 397*0Sstevel@tonic-gate * PWU_SUCCESS is returned. 398*0Sstevel@tonic-gate * PWU_NOT_FOUND, passwd not in user's old password history. 399*0Sstevel@tonic-gate * PWU_errors, PWU_ errors from other routines. 400*0Sstevel@tonic-gate * 401*0Sstevel@tonic-gate */ 402*0Sstevel@tonic-gate int 403*0Sstevel@tonic-gate files_checkhistory(char *user, char *passwd, pwu_repository_t *rep) 404*0Sstevel@tonic-gate { 405*0Sstevel@tonic-gate attrlist attr; 406*0Sstevel@tonic-gate int res; 407*0Sstevel@tonic-gate 408*0Sstevel@tonic-gate attr.type = ATTR_HISTORY; 409*0Sstevel@tonic-gate attr.data.val_s = NULL; 410*0Sstevel@tonic-gate attr.next = NULL; 411*0Sstevel@tonic-gate 412*0Sstevel@tonic-gate debug("files_checkhistory(user=%s)", user); 413*0Sstevel@tonic-gate 414*0Sstevel@tonic-gate /* 415*0Sstevel@tonic-gate * XXX 416*0Sstevel@tonic-gate * This depends on the underlying files_getattr implementation 417*0Sstevel@tonic-gate * treating user not found in backing store or no history as 418*0Sstevel@tonic-gate * an error. 419*0Sstevel@tonic-gate * XXX 420*0Sstevel@tonic-gate */ 421*0Sstevel@tonic-gate 422*0Sstevel@tonic-gate if ((res = files_getattr(user, &attr, rep)) == PWU_SUCCESS) { 423*0Sstevel@tonic-gate char *s; 424*0Sstevel@tonic-gate char *crypt_passwd; 425*0Sstevel@tonic-gate int histsize; 426*0Sstevel@tonic-gate char *last = attr.data.val_s; 427*0Sstevel@tonic-gate 428*0Sstevel@tonic-gate if ((histsize = def_getint("HISTORY=", DEFHISTORY)) == 0) { 429*0Sstevel@tonic-gate debug("files_checkhistory: no history requested"); 430*0Sstevel@tonic-gate res = PWU_NOT_FOUND; 431*0Sstevel@tonic-gate goto out; 432*0Sstevel@tonic-gate } 433*0Sstevel@tonic-gate 434*0Sstevel@tonic-gate debug("files_checkhistory: histsize = %d", histsize); 435*0Sstevel@tonic-gate if (histsize > MAXHISTORY) 436*0Sstevel@tonic-gate histsize = MAXHISTORY; 437*0Sstevel@tonic-gate 438*0Sstevel@tonic-gate debug("line to test\n\t%s", last); 439*0Sstevel@tonic-gate 440*0Sstevel@tonic-gate /* compare crypt_passwd to attr.data.val_s strings. */ 441*0Sstevel@tonic-gate res = PWU_NOT_FOUND; 442*0Sstevel@tonic-gate while ((histsize-- > 0) && 443*0Sstevel@tonic-gate (((s = strtok_r(NULL, ":", &last)) != NULL) && 444*0Sstevel@tonic-gate (*s != '\n'))) { 445*0Sstevel@tonic-gate 446*0Sstevel@tonic-gate crypt_passwd = crypt(passwd, s); 447*0Sstevel@tonic-gate debug("files_checkhistory: user_pw=%s, history_pw=%s", 448*0Sstevel@tonic-gate crypt_passwd, s); 449*0Sstevel@tonic-gate if (strcmp(crypt_passwd, s) == 0) { 450*0Sstevel@tonic-gate res = PWU_SUCCESS; 451*0Sstevel@tonic-gate break; 452*0Sstevel@tonic-gate } 453*0Sstevel@tonic-gate } 454*0Sstevel@tonic-gate debug("files_checkhistory(%s, %s) = %d", user, crypt_passwd, 455*0Sstevel@tonic-gate res); 456*0Sstevel@tonic-gate } 457*0Sstevel@tonic-gate out: 458*0Sstevel@tonic-gate if (attr.data.val_s != NULL) 459*0Sstevel@tonic-gate free(attr.data.val_s); 460*0Sstevel@tonic-gate 461*0Sstevel@tonic-gate return (res); 462*0Sstevel@tonic-gate } 463*0Sstevel@tonic-gate 464*0Sstevel@tonic-gate /* 465*0Sstevel@tonic-gate * files_getattr(name, items, rep) 466*0Sstevel@tonic-gate * 467*0Sstevel@tonic-gate * Get attributes specified in list 'items' 468*0Sstevel@tonic-gate */ 469*0Sstevel@tonic-gate int 470*0Sstevel@tonic-gate files_getattr(char *name, attrlist *items, pwu_repository_t *rep) 471*0Sstevel@tonic-gate { 472*0Sstevel@tonic-gate struct pwbuf *pwbuf; 473*0Sstevel@tonic-gate struct passwd *pw; 474*0Sstevel@tonic-gate struct spwd *spw; 475*0Sstevel@tonic-gate attrlist *w; 476*0Sstevel@tonic-gate int res; 477*0Sstevel@tonic-gate 478*0Sstevel@tonic-gate res = files_getpwnam(name, items, rep, (void **)&pwbuf); 479*0Sstevel@tonic-gate if (res != PWU_SUCCESS) 480*0Sstevel@tonic-gate return (res); 481*0Sstevel@tonic-gate 482*0Sstevel@tonic-gate pw = pwbuf->pwd; 483*0Sstevel@tonic-gate spw = pwbuf->spwd; 484*0Sstevel@tonic-gate 485*0Sstevel@tonic-gate for (w = items; res == PWU_SUCCESS && w != NULL; w = w->next) { 486*0Sstevel@tonic-gate switch (w->type) { 487*0Sstevel@tonic-gate case ATTR_NAME: 488*0Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_name)) == NULL) 489*0Sstevel@tonic-gate res = PWU_NOMEM; 490*0Sstevel@tonic-gate break; 491*0Sstevel@tonic-gate case ATTR_COMMENT: 492*0Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_comment)) == NULL) 493*0Sstevel@tonic-gate res = PWU_NOMEM; 494*0Sstevel@tonic-gate break; 495*0Sstevel@tonic-gate case ATTR_GECOS: 496*0Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL) 497*0Sstevel@tonic-gate res = PWU_NOMEM; 498*0Sstevel@tonic-gate break; 499*0Sstevel@tonic-gate case ATTR_HOMEDIR: 500*0Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_dir)) == NULL) 501*0Sstevel@tonic-gate res = PWU_NOMEM; 502*0Sstevel@tonic-gate break; 503*0Sstevel@tonic-gate case ATTR_SHELL: 504*0Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_shell)) == NULL) 505*0Sstevel@tonic-gate res = PWU_NOMEM; 506*0Sstevel@tonic-gate break; 507*0Sstevel@tonic-gate /* 508*0Sstevel@tonic-gate * Nothing special needs to be done for 509*0Sstevel@tonic-gate * server policy 510*0Sstevel@tonic-gate */ 511*0Sstevel@tonic-gate case ATTR_PASSWD: 512*0Sstevel@tonic-gate case ATTR_PASSWD_SERVER_POLICY: 513*0Sstevel@tonic-gate if ((w->data.val_s = strdup(spw->sp_pwdp)) == NULL) 514*0Sstevel@tonic-gate res = PWU_NOMEM; 515*0Sstevel@tonic-gate break; 516*0Sstevel@tonic-gate case ATTR_AGE: 517*0Sstevel@tonic-gate if ((w->data.val_s = strdup(pw->pw_age)) == NULL) 518*0Sstevel@tonic-gate res = PWU_NOMEM; 519*0Sstevel@tonic-gate break; 520*0Sstevel@tonic-gate case ATTR_REP_NAME: 521*0Sstevel@tonic-gate if ((w->data.val_s = strdup("files")) == NULL) 522*0Sstevel@tonic-gate res = PWU_NOMEM; 523*0Sstevel@tonic-gate break; 524*0Sstevel@tonic-gate case ATTR_HISTORY: { 525*0Sstevel@tonic-gate FILE *history; 526*0Sstevel@tonic-gate char buf[MAX_LOGNAME + MAXHISTORY + 527*0Sstevel@tonic-gate (MAXHISTORY * CRYPT_MAXCIPHERTEXTLEN)+1]; 528*0Sstevel@tonic-gate char *s, *s1; 529*0Sstevel@tonic-gate 530*0Sstevel@tonic-gate debug("files_getattr: Get password history for %s ", 531*0Sstevel@tonic-gate name); 532*0Sstevel@tonic-gate 533*0Sstevel@tonic-gate if ((history = fopen(HISTORY, "r")) == NULL) { 534*0Sstevel@tonic-gate debug("files_getattr: %s not found", HISTORY); 535*0Sstevel@tonic-gate res = PWU_OPEN_FAILED; 536*0Sstevel@tonic-gate goto getattr_exit; 537*0Sstevel@tonic-gate } 538*0Sstevel@tonic-gate res = PWU_NOT_FOUND; 539*0Sstevel@tonic-gate while ((s = fgets(buf, sizeof (buf), history)) != 540*0Sstevel@tonic-gate NULL) { 541*0Sstevel@tonic-gate s1 = strchr(s, ':'); 542*0Sstevel@tonic-gate if (s1 != NULL) { 543*0Sstevel@tonic-gate *s1 = '\0'; 544*0Sstevel@tonic-gate } else { 545*0Sstevel@tonic-gate res = PWU_NOT_FOUND; 546*0Sstevel@tonic-gate break; 547*0Sstevel@tonic-gate } 548*0Sstevel@tonic-gate #ifdef DEBUG 549*0Sstevel@tonic-gate debug("got history line for %s", s); 550*0Sstevel@tonic-gate #endif /* DEBUG */ 551*0Sstevel@tonic-gate if (strcmp(s, name) == 0) { 552*0Sstevel@tonic-gate /* found user */ 553*0Sstevel@tonic-gate if ((items->data.val_s = 554*0Sstevel@tonic-gate strdup(s1+1)) == NULL) 555*0Sstevel@tonic-gate res = PWU_NOMEM; 556*0Sstevel@tonic-gate else 557*0Sstevel@tonic-gate res = PWU_SUCCESS; 558*0Sstevel@tonic-gate break; 559*0Sstevel@tonic-gate } 560*0Sstevel@tonic-gate } 561*0Sstevel@tonic-gate (void) fclose(history); 562*0Sstevel@tonic-gate break; 563*0Sstevel@tonic-gate } 564*0Sstevel@tonic-gate 565*0Sstevel@tonic-gate /* integer values */ 566*0Sstevel@tonic-gate case ATTR_UID: 567*0Sstevel@tonic-gate w->data.val_i = pw->pw_uid; 568*0Sstevel@tonic-gate break; 569*0Sstevel@tonic-gate case ATTR_GID: 570*0Sstevel@tonic-gate w->data.val_i = pw->pw_gid; 571*0Sstevel@tonic-gate break; 572*0Sstevel@tonic-gate case ATTR_LSTCHG: 573*0Sstevel@tonic-gate w->data.val_i = spw->sp_lstchg; 574*0Sstevel@tonic-gate break; 575*0Sstevel@tonic-gate case ATTR_MIN: 576*0Sstevel@tonic-gate w->data.val_i = spw->sp_min; 577*0Sstevel@tonic-gate break; 578*0Sstevel@tonic-gate case ATTR_MAX: 579*0Sstevel@tonic-gate w->data.val_i = spw->sp_max; 580*0Sstevel@tonic-gate break; 581*0Sstevel@tonic-gate case ATTR_WARN: 582*0Sstevel@tonic-gate w->data.val_i = spw->sp_warn; 583*0Sstevel@tonic-gate break; 584*0Sstevel@tonic-gate case ATTR_INACT: 585*0Sstevel@tonic-gate w->data.val_i = spw->sp_inact; 586*0Sstevel@tonic-gate break; 587*0Sstevel@tonic-gate case ATTR_EXPIRE: 588*0Sstevel@tonic-gate w->data.val_i = spw->sp_expire; 589*0Sstevel@tonic-gate break; 590*0Sstevel@tonic-gate case ATTR_FLAG: 591*0Sstevel@tonic-gate w->data.val_i = spw->sp_flag; 592*0Sstevel@tonic-gate break; 593*0Sstevel@tonic-gate case ATTR_FAILED_LOGINS: 594*0Sstevel@tonic-gate w->data.val_i = spw->sp_flag & FAILCOUNT_MASK; 595*0Sstevel@tonic-gate break; 596*0Sstevel@tonic-gate default: 597*0Sstevel@tonic-gate break; 598*0Sstevel@tonic-gate } 599*0Sstevel@tonic-gate } 600*0Sstevel@tonic-gate 601*0Sstevel@tonic-gate getattr_exit: 602*0Sstevel@tonic-gate if (pwbuf->pwd) free(pwbuf->pwd); 603*0Sstevel@tonic-gate if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch); 604*0Sstevel@tonic-gate if (pwbuf->spwd) free(pwbuf->spwd); 605*0Sstevel@tonic-gate free(pwbuf); 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate return (res); 608*0Sstevel@tonic-gate } 609*0Sstevel@tonic-gate 610*0Sstevel@tonic-gate /* 611*0Sstevel@tonic-gate * max_present(list) 612*0Sstevel@tonic-gate * 613*0Sstevel@tonic-gate * see if attribute ATTR_MAX, with value != -1, is present in 614*0Sstevel@tonic-gate * attribute-list "list". 615*0Sstevel@tonic-gate * 616*0Sstevel@tonic-gate * returns 1 if present, 0 otherwise. 617*0Sstevel@tonic-gate */ 618*0Sstevel@tonic-gate static int 619*0Sstevel@tonic-gate max_present(attrlist *list) 620*0Sstevel@tonic-gate { 621*0Sstevel@tonic-gate while (list != NULL) 622*0Sstevel@tonic-gate if (list->type == ATTR_MAX && list->data.val_i != -1) 623*0Sstevel@tonic-gate return (1); 624*0Sstevel@tonic-gate else 625*0Sstevel@tonic-gate list = list->next; 626*0Sstevel@tonic-gate 627*0Sstevel@tonic-gate return (0); 628*0Sstevel@tonic-gate } 629*0Sstevel@tonic-gate 630*0Sstevel@tonic-gate /* 631*0Sstevel@tonic-gate * files_update(items, rep, buf) 632*0Sstevel@tonic-gate * 633*0Sstevel@tonic-gate * update the information in buf with the attributes specified in 634*0Sstevel@tonic-gate * items. 635*0Sstevel@tonic-gate */ 636*0Sstevel@tonic-gate /*ARGSUSED*/ 637*0Sstevel@tonic-gate int 638*0Sstevel@tonic-gate files_update(attrlist *items, pwu_repository_t *rep, void *buf) 639*0Sstevel@tonic-gate { 640*0Sstevel@tonic-gate struct pwbuf *pwbuf = (struct pwbuf *)buf; 641*0Sstevel@tonic-gate struct passwd *pw; 642*0Sstevel@tonic-gate struct spwd *spw; 643*0Sstevel@tonic-gate attrlist *p; 644*0Sstevel@tonic-gate int aging_needed = 0; 645*0Sstevel@tonic-gate int aging_set = 0; 646*0Sstevel@tonic-gate int disable_aging; 647*0Sstevel@tonic-gate char *pword; 648*0Sstevel@tonic-gate int len; 649*0Sstevel@tonic-gate 650*0Sstevel@tonic-gate pw = pwbuf->pwd; 651*0Sstevel@tonic-gate spw = pwbuf->spwd; 652*0Sstevel@tonic-gate pwbuf->update_history = 0; 653*0Sstevel@tonic-gate 654*0Sstevel@tonic-gate /* 655*0Sstevel@tonic-gate * if sp_max==0 : disable passwd aging after updating the password 656*0Sstevel@tonic-gate */ 657*0Sstevel@tonic-gate disable_aging = (spw != NULL && spw->sp_max == 0); 658*0Sstevel@tonic-gate 659*0Sstevel@tonic-gate for (p = items; p != NULL; p = p->next) { 660*0Sstevel@tonic-gate switch (p->type) { 661*0Sstevel@tonic-gate case ATTR_NAME: 662*0Sstevel@tonic-gate break; /* We are able to handle this, but... */ 663*0Sstevel@tonic-gate case ATTR_UID: 664*0Sstevel@tonic-gate pw->pw_uid = (uid_t)p->data.val_i; 665*0Sstevel@tonic-gate break; 666*0Sstevel@tonic-gate case ATTR_GID: 667*0Sstevel@tonic-gate pw->pw_gid = (gid_t)p->data.val_i; 668*0Sstevel@tonic-gate break; 669*0Sstevel@tonic-gate case ATTR_AGE: 670*0Sstevel@tonic-gate pw->pw_age = p->data.val_s; 671*0Sstevel@tonic-gate break; 672*0Sstevel@tonic-gate case ATTR_COMMENT: 673*0Sstevel@tonic-gate pw->pw_comment = p->data.val_s; 674*0Sstevel@tonic-gate break; 675*0Sstevel@tonic-gate case ATTR_GECOS: 676*0Sstevel@tonic-gate pw->pw_gecos = p->data.val_s; 677*0Sstevel@tonic-gate break; 678*0Sstevel@tonic-gate case ATTR_HOMEDIR: 679*0Sstevel@tonic-gate pw->pw_dir = p->data.val_s; 680*0Sstevel@tonic-gate break; 681*0Sstevel@tonic-gate case ATTR_SHELL: 682*0Sstevel@tonic-gate pw->pw_shell = p->data.val_s; 683*0Sstevel@tonic-gate break; 684*0Sstevel@tonic-gate 685*0Sstevel@tonic-gate /* 686*0Sstevel@tonic-gate * Nothing special needs to be done for 687*0Sstevel@tonic-gate * server policy 688*0Sstevel@tonic-gate */ 689*0Sstevel@tonic-gate case ATTR_PASSWD: 690*0Sstevel@tonic-gate case ATTR_PASSWD_SERVER_POLICY: 691*0Sstevel@tonic-gate /* 692*0Sstevel@tonic-gate * There is a special case only for files: if the 693*0Sstevel@tonic-gate * password is to be deleted (-d to passwd), 694*0Sstevel@tonic-gate * p->data.val_s will be NULL. 695*0Sstevel@tonic-gate */ 696*0Sstevel@tonic-gate if (p->data.val_s == NULL) { 697*0Sstevel@tonic-gate spw->sp_pwdp = ""; 698*0Sstevel@tonic-gate } else { 699*0Sstevel@tonic-gate char *salt = NULL; 700*0Sstevel@tonic-gate char *hash = NULL; 701*0Sstevel@tonic-gate 702*0Sstevel@tonic-gate salt = crypt_gensalt(spw->sp_pwdp, pw); 703*0Sstevel@tonic-gate 704*0Sstevel@tonic-gate if (salt == NULL) { 705*0Sstevel@tonic-gate if (errno == ENOMEM) 706*0Sstevel@tonic-gate return (PWU_NOMEM); 707*0Sstevel@tonic-gate /* algorithm problem? */ 708*0Sstevel@tonic-gate syslog(LOG_AUTH | LOG_ALERT, 709*0Sstevel@tonic-gate "passwdutil: crypt_gensalt %m"); 710*0Sstevel@tonic-gate return (PWU_UPDATE_FAILED); 711*0Sstevel@tonic-gate } 712*0Sstevel@tonic-gate hash = crypt(p->data.val_s, salt); 713*0Sstevel@tonic-gate free(salt); 714*0Sstevel@tonic-gate if (hash == NULL) { 715*0Sstevel@tonic-gate errno = ENOMEM; 716*0Sstevel@tonic-gate return (PWU_NOMEM); 717*0Sstevel@tonic-gate } 718*0Sstevel@tonic-gate pword = strdup(hash); 719*0Sstevel@tonic-gate free(hash); 720*0Sstevel@tonic-gate if (pword == NULL) { 721*0Sstevel@tonic-gate errno = ENOMEM; 722*0Sstevel@tonic-gate return (PWU_NOMEM); 723*0Sstevel@tonic-gate } 724*0Sstevel@tonic-gate 725*0Sstevel@tonic-gate if (pwbuf->new_sp_pwdp) 726*0Sstevel@tonic-gate free(pwbuf->new_sp_pwdp); 727*0Sstevel@tonic-gate pwbuf->new_sp_pwdp = pword; 728*0Sstevel@tonic-gate spw->sp_pwdp = pword; 729*0Sstevel@tonic-gate aging_needed = 1; 730*0Sstevel@tonic-gate pwbuf->update_history = 1; 731*0Sstevel@tonic-gate } 732*0Sstevel@tonic-gate spw->sp_flag &= ~FAILCOUNT_MASK; /* reset count */ 733*0Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 734*0Sstevel@tonic-gate break; 735*0Sstevel@tonic-gate case ATTR_LOCK_ACCOUNT: 736*0Sstevel@tonic-gate if (spw->sp_pwdp == NULL) { 737*0Sstevel@tonic-gate spw->sp_pwdp = LOCKSTRING; 738*0Sstevel@tonic-gate } else if (strncmp(spw->sp_pwdp, LOCKSTRING, 739*0Sstevel@tonic-gate sizeof (LOCKSTRING)-1) != 0) { 740*0Sstevel@tonic-gate len = sizeof (LOCKSTRING)-1 + 741*0Sstevel@tonic-gate strlen(spw->sp_pwdp) + 1; 742*0Sstevel@tonic-gate pword = malloc(len); 743*0Sstevel@tonic-gate if (pword == NULL) { 744*0Sstevel@tonic-gate errno = ENOMEM; 745*0Sstevel@tonic-gate return (PWU_NOMEM); 746*0Sstevel@tonic-gate } 747*0Sstevel@tonic-gate (void) strlcpy(pword, LOCKSTRING, len); 748*0Sstevel@tonic-gate (void) strlcat(pword, spw->sp_pwdp, len); 749*0Sstevel@tonic-gate if (pwbuf->new_sp_pwdp) 750*0Sstevel@tonic-gate free(pwbuf->new_sp_pwdp); 751*0Sstevel@tonic-gate pwbuf->new_sp_pwdp = pword; 752*0Sstevel@tonic-gate spw->sp_pwdp = pword; 753*0Sstevel@tonic-gate } 754*0Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 755*0Sstevel@tonic-gate break; 756*0Sstevel@tonic-gate case ATTR_UNLOCK_ACCOUNT: 757*0Sstevel@tonic-gate if (spw->sp_pwdp != NULL && 758*0Sstevel@tonic-gate strncmp(spw->sp_pwdp, LOCKSTRING, 759*0Sstevel@tonic-gate sizeof (LOCKSTRING)-1) == 0) { 760*0Sstevel@tonic-gate (void) strcpy(spw->sp_pwdp, spw->sp_pwdp + 761*0Sstevel@tonic-gate sizeof (LOCKSTRING)-1); 762*0Sstevel@tonic-gate } 763*0Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 764*0Sstevel@tonic-gate break; 765*0Sstevel@tonic-gate case ATTR_NOLOGIN_ACCOUNT: 766*0Sstevel@tonic-gate spw->sp_pwdp = NOLOGINSTRING; 767*0Sstevel@tonic-gate if (pwbuf->new_sp_pwdp) { 768*0Sstevel@tonic-gate free(pwbuf->new_sp_pwdp); 769*0Sstevel@tonic-gate pwbuf->new_sp_pwdp = NULL; 770*0Sstevel@tonic-gate } 771*0Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 772*0Sstevel@tonic-gate break; 773*0Sstevel@tonic-gate case ATTR_EXPIRE_PASSWORD: 774*0Sstevel@tonic-gate spw->sp_lstchg = 0; 775*0Sstevel@tonic-gate break; 776*0Sstevel@tonic-gate case ATTR_LSTCHG: 777*0Sstevel@tonic-gate spw->sp_lstchg = p->data.val_i; 778*0Sstevel@tonic-gate break; 779*0Sstevel@tonic-gate case ATTR_MIN: 780*0Sstevel@tonic-gate if (spw->sp_max == -1 && 781*0Sstevel@tonic-gate p->data.val_i != -1 && max_present(p->next) == 0) 782*0Sstevel@tonic-gate return (PWU_AGING_DISABLED); 783*0Sstevel@tonic-gate spw->sp_min = p->data.val_i; 784*0Sstevel@tonic-gate aging_set = 1; 785*0Sstevel@tonic-gate break; 786*0Sstevel@tonic-gate case ATTR_MAX: 787*0Sstevel@tonic-gate if (p->data.val_i == -1) { 788*0Sstevel@tonic-gate /* Turn aging off -> Reset min and warn too */ 789*0Sstevel@tonic-gate 790*0Sstevel@tonic-gate spw->sp_min = -1; 791*0Sstevel@tonic-gate spw->sp_warn = -1; 792*0Sstevel@tonic-gate } else { 793*0Sstevel@tonic-gate /* Turn aging on */ 794*0Sstevel@tonic-gate 795*0Sstevel@tonic-gate if (spw->sp_min == -1) { 796*0Sstevel@tonic-gate /* 797*0Sstevel@tonic-gate * If minage has not been set with 798*0Sstevel@tonic-gate * a command-line option, we set it 799*0Sstevel@tonic-gate * to zero. 800*0Sstevel@tonic-gate */ 801*0Sstevel@tonic-gate spw->sp_min = 0; 802*0Sstevel@tonic-gate } 803*0Sstevel@tonic-gate 804*0Sstevel@tonic-gate /* 805*0Sstevel@tonic-gate * If aging was turned off, we update lstchg. 806*0Sstevel@tonic-gate * 807*0Sstevel@tonic-gate * We take care not to update lstchg if the 808*0Sstevel@tonic-gate * user has no password, otherwise the user 809*0Sstevel@tonic-gate * might not be required to provide a password 810*0Sstevel@tonic-gate * the next time [s]he logs-in. 811*0Sstevel@tonic-gate * 812*0Sstevel@tonic-gate * Also, if lstchg != -1 (i.e., not set in 813*0Sstevel@tonic-gate * /etc/shadow), we keep the old value. 814*0Sstevel@tonic-gate */ 815*0Sstevel@tonic-gate if (spw->sp_max == -1 && 816*0Sstevel@tonic-gate spw->sp_pwdp != NULL && *spw->sp_pwdp && 817*0Sstevel@tonic-gate spw->sp_lstchg == -1) { 818*0Sstevel@tonic-gate spw->sp_lstchg = DAY_NOW_32; 819*0Sstevel@tonic-gate } 820*0Sstevel@tonic-gate } 821*0Sstevel@tonic-gate 822*0Sstevel@tonic-gate spw->sp_max = p->data.val_i; 823*0Sstevel@tonic-gate 824*0Sstevel@tonic-gate aging_set = 1; 825*0Sstevel@tonic-gate 826*0Sstevel@tonic-gate break; 827*0Sstevel@tonic-gate case ATTR_WARN: 828*0Sstevel@tonic-gate if (spw->sp_max == -1 && p->data.val_i != -1 && 829*0Sstevel@tonic-gate max_present(p->next) == 0) 830*0Sstevel@tonic-gate return (PWU_AGING_DISABLED); 831*0Sstevel@tonic-gate spw->sp_warn = p->data.val_i; 832*0Sstevel@tonic-gate break; 833*0Sstevel@tonic-gate case ATTR_INACT: 834*0Sstevel@tonic-gate spw->sp_inact = p->data.val_i; 835*0Sstevel@tonic-gate break; 836*0Sstevel@tonic-gate case ATTR_EXPIRE: 837*0Sstevel@tonic-gate spw->sp_expire = p->data.val_i; 838*0Sstevel@tonic-gate break; 839*0Sstevel@tonic-gate case ATTR_FLAG: 840*0Sstevel@tonic-gate spw->sp_flag = p->data.val_i; 841*0Sstevel@tonic-gate break; 842*0Sstevel@tonic-gate case ATTR_INCR_FAILED_LOGINS: 843*0Sstevel@tonic-gate { 844*0Sstevel@tonic-gate int count = (spw->sp_flag & FAILCOUNT_MASK) + 1; 845*0Sstevel@tonic-gate spw->sp_flag &= ~FAILCOUNT_MASK; 846*0Sstevel@tonic-gate spw->sp_flag |= min(FAILCOUNT_MASK, count); 847*0Sstevel@tonic-gate p->data.val_i = count; 848*0Sstevel@tonic-gate } 849*0Sstevel@tonic-gate break; 850*0Sstevel@tonic-gate case ATTR_RST_FAILED_LOGINS: 851*0Sstevel@tonic-gate p->data.val_i = spw->sp_flag & FAILCOUNT_MASK; 852*0Sstevel@tonic-gate spw->sp_flag &= ~FAILCOUNT_MASK; 853*0Sstevel@tonic-gate break; 854*0Sstevel@tonic-gate default: 855*0Sstevel@tonic-gate break; 856*0Sstevel@tonic-gate } 857*0Sstevel@tonic-gate } 858*0Sstevel@tonic-gate 859*0Sstevel@tonic-gate /* 860*0Sstevel@tonic-gate * What should the new aging values look like? 861*0Sstevel@tonic-gate * 862*0Sstevel@tonic-gate * There are a number of different conditions 863*0Sstevel@tonic-gate * 864*0Sstevel@tonic-gate * a) aging is already configured: don't touch it 865*0Sstevel@tonic-gate * 866*0Sstevel@tonic-gate * b) disable_aging is set: disable aging 867*0Sstevel@tonic-gate * 868*0Sstevel@tonic-gate * c) aging is not configured: turn on default aging; 869*0Sstevel@tonic-gate * 870*0Sstevel@tonic-gate * b) and c) of course only if aging_needed and !aging_set. 871*0Sstevel@tonic-gate * (i.e., password changed, and aging values not changed) 872*0Sstevel@tonic-gate */ 873*0Sstevel@tonic-gate 874*0Sstevel@tonic-gate if (spw != NULL && spw->sp_max <= 0) { 875*0Sstevel@tonic-gate /* a) aging not yet configured */ 876*0Sstevel@tonic-gate if (aging_needed && !aging_set) { 877*0Sstevel@tonic-gate if (disable_aging) { 878*0Sstevel@tonic-gate /* b) turn off aging */ 879*0Sstevel@tonic-gate spw->sp_min = spw->sp_max = spw->sp_warn = -1; 880*0Sstevel@tonic-gate } else { 881*0Sstevel@tonic-gate /* c) */ 882*0Sstevel@tonic-gate turn_on_default_aging(spw); 883*0Sstevel@tonic-gate } 884*0Sstevel@tonic-gate } 885*0Sstevel@tonic-gate } 886*0Sstevel@tonic-gate 887*0Sstevel@tonic-gate return (PWU_SUCCESS); 888*0Sstevel@tonic-gate } 889*0Sstevel@tonic-gate 890*0Sstevel@tonic-gate /* 891*0Sstevel@tonic-gate * files_update_shadow(char *name, struct spwd *spwd) 892*0Sstevel@tonic-gate * 893*0Sstevel@tonic-gate * update the shadow password file SHADOW to contain the spwd structure 894*0Sstevel@tonic-gate * "spwd" for user "name" 895*0Sstevel@tonic-gate */ 896*0Sstevel@tonic-gate int 897*0Sstevel@tonic-gate files_update_shadow(char *name, struct spwd *spwd) 898*0Sstevel@tonic-gate { 899*0Sstevel@tonic-gate struct stat64 stbuf; 900*0Sstevel@tonic-gate FILE *dst; 901*0Sstevel@tonic-gate FILE *src; 902*0Sstevel@tonic-gate struct spwd cur; 903*0Sstevel@tonic-gate char buf[SPW_SCRATCH_SIZE]; 904*0Sstevel@tonic-gate int tempfd; 905*0Sstevel@tonic-gate mode_t filemode; 906*0Sstevel@tonic-gate int result = -1; 907*0Sstevel@tonic-gate int err = PWU_SUCCESS; 908*0Sstevel@tonic-gate 909*0Sstevel@tonic-gate /* Mode of the shadow file should be 400 or 000 */ 910*0Sstevel@tonic-gate if (stat64(SHADOW, &stbuf) < 0) { 911*0Sstevel@tonic-gate err = PWU_STAT_FAILED; 912*0Sstevel@tonic-gate goto shadow_exit; 913*0Sstevel@tonic-gate } 914*0Sstevel@tonic-gate 915*0Sstevel@tonic-gate /* copy mode from current shadow file (0400 or 0000) */ 916*0Sstevel@tonic-gate filemode = stbuf.st_mode & S_IRUSR; 917*0Sstevel@tonic-gate 918*0Sstevel@tonic-gate /* 919*0Sstevel@tonic-gate * we can't specify filemodes to fopen(), and we SHOULD NOT 920*0Sstevel@tonic-gate * set umask in multi-thread safe libraries, so we use 921*0Sstevel@tonic-gate * a combination of open() and fdopen() 922*0Sstevel@tonic-gate */ 923*0Sstevel@tonic-gate tempfd = open(SHADTEMP, O_WRONLY|O_CREAT|O_TRUNC, filemode); 924*0Sstevel@tonic-gate if (tempfd < 0) { 925*0Sstevel@tonic-gate err = PWU_OPEN_FAILED; 926*0Sstevel@tonic-gate goto shadow_exit; 927*0Sstevel@tonic-gate } 928*0Sstevel@tonic-gate (void) fchown(tempfd, (uid_t)0, stbuf.st_gid); 929*0Sstevel@tonic-gate 930*0Sstevel@tonic-gate if ((dst = fdopen(tempfd, "w")) == NULL) { 931*0Sstevel@tonic-gate err = PWU_OPEN_FAILED; 932*0Sstevel@tonic-gate goto shadow_exit; 933*0Sstevel@tonic-gate } 934*0Sstevel@tonic-gate 935*0Sstevel@tonic-gate if ((src = fopen(SHADOW, "r")) == NULL) { 936*0Sstevel@tonic-gate err = PWU_OPEN_FAILED; 937*0Sstevel@tonic-gate (void) fclose(dst); 938*0Sstevel@tonic-gate (void) unlink(SHADTEMP); 939*0Sstevel@tonic-gate goto shadow_exit; 940*0Sstevel@tonic-gate } 941*0Sstevel@tonic-gate 942*0Sstevel@tonic-gate /* 943*0Sstevel@tonic-gate * copy old shadow to temporary file while replacing the entry 944*0Sstevel@tonic-gate * that matches "name". 945*0Sstevel@tonic-gate */ 946*0Sstevel@tonic-gate while (fgetspent_r(src, &cur, buf, sizeof (buf)) != NULL) { 947*0Sstevel@tonic-gate 948*0Sstevel@tonic-gate if (strcmp(cur.sp_namp, name) == 0) 949*0Sstevel@tonic-gate result = putspent(spwd, dst); 950*0Sstevel@tonic-gate else 951*0Sstevel@tonic-gate result = putspent(&cur, dst); 952*0Sstevel@tonic-gate 953*0Sstevel@tonic-gate if (result != 0) { 954*0Sstevel@tonic-gate err = PWU_WRITE_FAILED; 955*0Sstevel@tonic-gate (void) fclose(src); 956*0Sstevel@tonic-gate (void) fclose(dst); 957*0Sstevel@tonic-gate goto shadow_exit; 958*0Sstevel@tonic-gate } 959*0Sstevel@tonic-gate } 960*0Sstevel@tonic-gate 961*0Sstevel@tonic-gate (void) fclose(src); 962*0Sstevel@tonic-gate 963*0Sstevel@tonic-gate if (fclose(dst) != 0) { 964*0Sstevel@tonic-gate /* 965*0Sstevel@tonic-gate * Something went wrong (ENOSPC for example). Don't 966*0Sstevel@tonic-gate * use the resulting temporary file! 967*0Sstevel@tonic-gate */ 968*0Sstevel@tonic-gate err = PWU_CLOSE_FAILED; 969*0Sstevel@tonic-gate (void) unlink(SHADTEMP); 970*0Sstevel@tonic-gate goto shadow_exit; 971*0Sstevel@tonic-gate } 972*0Sstevel@tonic-gate 973*0Sstevel@tonic-gate /* 974*0Sstevel@tonic-gate * Rename stmp to shadow: 975*0Sstevel@tonic-gate * 1. make sure /etc/oshadow is gone 976*0Sstevel@tonic-gate * 2. ln /etc/shadow /etc/oshadow 977*0Sstevel@tonic-gate * 3. mv /etc/stmp /etc/shadow 978*0Sstevel@tonic-gate */ 979*0Sstevel@tonic-gate if (unlink(OSHADOW) && access(OSHADOW, 0) == 0) { 980*0Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 981*0Sstevel@tonic-gate (void) unlink(SHADTEMP); 982*0Sstevel@tonic-gate goto shadow_exit; 983*0Sstevel@tonic-gate } 984*0Sstevel@tonic-gate 985*0Sstevel@tonic-gate if (link(SHADOW, OSHADOW) == -1) { 986*0Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 987*0Sstevel@tonic-gate (void) unlink(SHADTEMP); 988*0Sstevel@tonic-gate goto shadow_exit; 989*0Sstevel@tonic-gate } 990*0Sstevel@tonic-gate 991*0Sstevel@tonic-gate if (rename(SHADTEMP, SHADOW) == -1) { 992*0Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 993*0Sstevel@tonic-gate (void) unlink(SHADTEMP); 994*0Sstevel@tonic-gate goto shadow_exit; 995*0Sstevel@tonic-gate } 996*0Sstevel@tonic-gate (void) unlink(OSHADOW); 997*0Sstevel@tonic-gate 998*0Sstevel@tonic-gate shadow_exit: 999*0Sstevel@tonic-gate return (err); 1000*0Sstevel@tonic-gate } 1001*0Sstevel@tonic-gate 1002*0Sstevel@tonic-gate int 1003*0Sstevel@tonic-gate files_update_passwd(char *name, struct passwd *pwd) 1004*0Sstevel@tonic-gate { 1005*0Sstevel@tonic-gate struct stat64 stbuf; 1006*0Sstevel@tonic-gate FILE *src, *dst; 1007*0Sstevel@tonic-gate int tempfd; 1008*0Sstevel@tonic-gate struct passwd cur; 1009*0Sstevel@tonic-gate char buf[PWD_SCRATCH_SIZE]; 1010*0Sstevel@tonic-gate int result; 1011*0Sstevel@tonic-gate int err = PWU_SUCCESS; 1012*0Sstevel@tonic-gate 1013*0Sstevel@tonic-gate if (stat64(PASSWD, &stbuf) < 0) { 1014*0Sstevel@tonic-gate err = PWU_STAT_FAILED; 1015*0Sstevel@tonic-gate goto passwd_exit; 1016*0Sstevel@tonic-gate } 1017*0Sstevel@tonic-gate 1018*0Sstevel@tonic-gate /* see files_update_shadow() for open()+fdopen() rationale */ 1019*0Sstevel@tonic-gate 1020*0Sstevel@tonic-gate if ((tempfd = open(PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { 1021*0Sstevel@tonic-gate err = PWU_OPEN_FAILED; 1022*0Sstevel@tonic-gate goto passwd_exit; 1023*0Sstevel@tonic-gate } 1024*0Sstevel@tonic-gate if ((dst = fdopen(tempfd, "w")) == NULL) { 1025*0Sstevel@tonic-gate err = PWU_OPEN_FAILED; 1026*0Sstevel@tonic-gate goto passwd_exit; 1027*0Sstevel@tonic-gate } 1028*0Sstevel@tonic-gate if ((src = fopen(PASSWD, "r")) == NULL) { 1029*0Sstevel@tonic-gate err = PWU_OPEN_FAILED; 1030*0Sstevel@tonic-gate (void) fclose(dst); 1031*0Sstevel@tonic-gate (void) unlink(PASSTEMP); 1032*0Sstevel@tonic-gate goto passwd_exit; 1033*0Sstevel@tonic-gate } 1034*0Sstevel@tonic-gate 1035*0Sstevel@tonic-gate /* 1036*0Sstevel@tonic-gate * copy old password entries to temporary file while replacing 1037*0Sstevel@tonic-gate * the entry that matches "name" 1038*0Sstevel@tonic-gate */ 1039*0Sstevel@tonic-gate while (fgetpwent_r(src, &cur, buf, sizeof (buf)) != NULL) { 1040*0Sstevel@tonic-gate if (strcmp(cur.pw_name, name) == 0) 1041*0Sstevel@tonic-gate result = putpwent(pwd, dst); 1042*0Sstevel@tonic-gate else 1043*0Sstevel@tonic-gate result = putpwent(&cur, dst); 1044*0Sstevel@tonic-gate if (result != 0) { 1045*0Sstevel@tonic-gate err = PWU_WRITE_FAILED; 1046*0Sstevel@tonic-gate (void) fclose(src); 1047*0Sstevel@tonic-gate (void) fclose(dst); 1048*0Sstevel@tonic-gate goto passwd_exit; 1049*0Sstevel@tonic-gate } 1050*0Sstevel@tonic-gate } 1051*0Sstevel@tonic-gate 1052*0Sstevel@tonic-gate (void) fclose(src); 1053*0Sstevel@tonic-gate if (fclose(dst) != 0) { 1054*0Sstevel@tonic-gate err = PWU_CLOSE_FAILED; 1055*0Sstevel@tonic-gate goto passwd_exit; /* Don't trust the temporary file */ 1056*0Sstevel@tonic-gate } 1057*0Sstevel@tonic-gate 1058*0Sstevel@tonic-gate /* Rename temp to passwd */ 1059*0Sstevel@tonic-gate if (unlink(OPASSWD) && access(OPASSWD, 0) == 0) { 1060*0Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 1061*0Sstevel@tonic-gate (void) unlink(PASSTEMP); 1062*0Sstevel@tonic-gate goto passwd_exit; 1063*0Sstevel@tonic-gate } 1064*0Sstevel@tonic-gate 1065*0Sstevel@tonic-gate if (link(PASSWD, OPASSWD) == -1) { 1066*0Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 1067*0Sstevel@tonic-gate (void) unlink(PASSTEMP); 1068*0Sstevel@tonic-gate goto passwd_exit; 1069*0Sstevel@tonic-gate } 1070*0Sstevel@tonic-gate 1071*0Sstevel@tonic-gate if (rename(PASSTEMP, PASSWD) == -1) { 1072*0Sstevel@tonic-gate err = PWU_UPDATE_FAILED; 1073*0Sstevel@tonic-gate (void) unlink(PASSTEMP); 1074*0Sstevel@tonic-gate goto passwd_exit; 1075*0Sstevel@tonic-gate } 1076*0Sstevel@tonic-gate 1077*0Sstevel@tonic-gate (void) chmod(PASSWD, 0644); 1078*0Sstevel@tonic-gate 1079*0Sstevel@tonic-gate passwd_exit: 1080*0Sstevel@tonic-gate return (err); 1081*0Sstevel@tonic-gate 1082*0Sstevel@tonic-gate } 1083*0Sstevel@tonic-gate 1084*0Sstevel@tonic-gate /* 1085*0Sstevel@tonic-gate * files_putpwnam(name, oldpw, dummy, rep, buf) 1086*0Sstevel@tonic-gate * 1087*0Sstevel@tonic-gate * store the password attributes contained in "buf" in /etc/passwd and 1088*0Sstevel@tonic-gate * /etc/shadow. The dummy parameter is a placeholder for NIS+ 1089*0Sstevel@tonic-gate * updates where the "oldrpc" password is passed. 1090*0Sstevel@tonic-gate */ 1091*0Sstevel@tonic-gate /*ARGSUSED*/ 1092*0Sstevel@tonic-gate int 1093*0Sstevel@tonic-gate files_putpwnam(char *name, char *oldpw, char *dummy, 1094*0Sstevel@tonic-gate pwu_repository_t *rep, void *buf) 1095*0Sstevel@tonic-gate { 1096*0Sstevel@tonic-gate struct pwbuf *pwbuf = (struct pwbuf *)buf; 1097*0Sstevel@tonic-gate int result = PWU_SUCCESS; 1098*0Sstevel@tonic-gate 1099*0Sstevel@tonic-gate if (pwbuf->pwd) { 1100*0Sstevel@tonic-gate result = files_update_passwd(name, pwbuf->pwd); 1101*0Sstevel@tonic-gate } 1102*0Sstevel@tonic-gate 1103*0Sstevel@tonic-gate if (result == PWU_SUCCESS && pwbuf->spwd) { 1104*0Sstevel@tonic-gate if (pwbuf->update_history != 0) { 1105*0Sstevel@tonic-gate debug("update_history = %d", pwbuf->update_history); 1106*0Sstevel@tonic-gate result = files_update_history(name, pwbuf->spwd); 1107*0Sstevel@tonic-gate } else { 1108*0Sstevel@tonic-gate debug("no password change"); 1109*0Sstevel@tonic-gate } 1110*0Sstevel@tonic-gate if (result == PWU_SUCCESS) { 1111*0Sstevel@tonic-gate result = files_update_shadow(name, pwbuf->spwd); 1112*0Sstevel@tonic-gate } 1113*0Sstevel@tonic-gate } 1114*0Sstevel@tonic-gate 1115*0Sstevel@tonic-gate if (pwbuf->pwd) { 1116*0Sstevel@tonic-gate (void) memset(pwbuf->pwd, 0, sizeof (struct passwd)); 1117*0Sstevel@tonic-gate (void) memset(pwbuf->pwd_scratch, 0, PWD_SCRATCH_SIZE); 1118*0Sstevel@tonic-gate free(pwbuf->pwd); 1119*0Sstevel@tonic-gate free(pwbuf->pwd_scratch); 1120*0Sstevel@tonic-gate } 1121*0Sstevel@tonic-gate if (pwbuf->spwd) { 1122*0Sstevel@tonic-gate (void) memset(pwbuf->spwd, 0, sizeof (struct spwd)); 1123*0Sstevel@tonic-gate (void) memset(pwbuf->spwd_scratch, 0, SPW_SCRATCH_SIZE); 1124*0Sstevel@tonic-gate free(pwbuf->spwd); 1125*0Sstevel@tonic-gate free(pwbuf->spwd_scratch); 1126*0Sstevel@tonic-gate } 1127*0Sstevel@tonic-gate if (pwbuf->new_sp_pwdp) { 1128*0Sstevel@tonic-gate free(pwbuf->new_sp_pwdp); 1129*0Sstevel@tonic-gate } 1130*0Sstevel@tonic-gate 1131*0Sstevel@tonic-gate return (result); 1132*0Sstevel@tonic-gate } 1133*0Sstevel@tonic-gate 1134*0Sstevel@tonic-gate /* 1135*0Sstevel@tonic-gate * NOTE: This is all covered under the repository lock held for updating 1136*0Sstevel@tonic-gate * passwd(4) and shadow(4). 1137*0Sstevel@tonic-gate */ 1138*0Sstevel@tonic-gate int 1139*0Sstevel@tonic-gate files_update_history(char *name, struct spwd *spwd) 1140*0Sstevel@tonic-gate { 1141*0Sstevel@tonic-gate int histsize; 1142*0Sstevel@tonic-gate int tmpfd; 1143*0Sstevel@tonic-gate FILE *src; /* history database file */ 1144*0Sstevel@tonic-gate FILE *dst; /* temp history database being updated */ 1145*0Sstevel@tonic-gate struct stat64 statbuf; 1146*0Sstevel@tonic-gate char buf[MAX_LOGNAME + MAXHISTORY + 1147*0Sstevel@tonic-gate (MAXHISTORY * CRYPT_MAXCIPHERTEXTLEN)+1]; 1148*0Sstevel@tonic-gate int found; 1149*0Sstevel@tonic-gate 1150*0Sstevel@tonic-gate if ((histsize = def_getint("HISTORY=", DEFHISTORY)) == 0) { 1151*0Sstevel@tonic-gate debug("files_update_history(%s) no history, unlinking", name); 1152*0Sstevel@tonic-gate (void) unlink(HISTORY); 1153*0Sstevel@tonic-gate return (PWU_SUCCESS); /* no history update defined */ 1154*0Sstevel@tonic-gate } 1155*0Sstevel@tonic-gate debug("files_update_history(%s, %s) histsize = %d", name, spwd->sp_pwdp, 1156*0Sstevel@tonic-gate histsize); 1157*0Sstevel@tonic-gate 1158*0Sstevel@tonic-gate if (histsize > MAXHISTORY) 1159*0Sstevel@tonic-gate histsize = MAXHISTORY; 1160*0Sstevel@tonic-gate if ((tmpfd = open(HISTEMP, O_WRONLY|O_CREAT|O_TRUNC, HISTMODE)) < 0) { 1161*0Sstevel@tonic-gate return (PWU_OPEN_FAILED); 1162*0Sstevel@tonic-gate } 1163*0Sstevel@tonic-gate (void) fchown(tmpfd, (uid_t)0, (gid_t)0); 1164*0Sstevel@tonic-gate 1165*0Sstevel@tonic-gate /* get ready to copy */ 1166*0Sstevel@tonic-gate if (((src = fopen(HISTORY, "r")) == NULL) && 1167*0Sstevel@tonic-gate (errno != ENOENT)) { 1168*0Sstevel@tonic-gate (void) unlink(HISTEMP); 1169*0Sstevel@tonic-gate return (PWU_OPEN_FAILED); 1170*0Sstevel@tonic-gate } 1171*0Sstevel@tonic-gate if ((dst = fdopen(tmpfd, "w")) == NULL) { 1172*0Sstevel@tonic-gate (void) fclose(src); 1173*0Sstevel@tonic-gate (void) unlink(HISTEMP); 1174*0Sstevel@tonic-gate return (PWU_OPEN_FAILED); 1175*0Sstevel@tonic-gate } 1176*0Sstevel@tonic-gate 1177*0Sstevel@tonic-gate /* Copy and update if found. Add if not found. */ 1178*0Sstevel@tonic-gate 1179*0Sstevel@tonic-gate found = 0; 1180*0Sstevel@tonic-gate 1181*0Sstevel@tonic-gate while ((src != NULL) && 1182*0Sstevel@tonic-gate (fgets(buf, sizeof (buf), src) != NULL)) { 1183*0Sstevel@tonic-gate char *user; 1184*0Sstevel@tonic-gate char *last; 1185*0Sstevel@tonic-gate 1186*0Sstevel@tonic-gate /* get username field */ 1187*0Sstevel@tonic-gate user = strtok_r(buf, ":", &last); 1188*0Sstevel@tonic-gate 1189*0Sstevel@tonic-gate #ifdef DEBUG 1190*0Sstevel@tonic-gate debug("files_update_history: read=\"%s\"", user); 1191*0Sstevel@tonic-gate #endif /* DEBUG */ 1192*0Sstevel@tonic-gate 1193*0Sstevel@tonic-gate if (strcmp(user, name) == 0) { 1194*0Sstevel@tonic-gate char *crypt; 1195*0Sstevel@tonic-gate int i; 1196*0Sstevel@tonic-gate 1197*0Sstevel@tonic-gate /* found user, update */ 1198*0Sstevel@tonic-gate found++; 1199*0Sstevel@tonic-gate (void) fprintf(dst, "%s:%s:", name, spwd->sp_pwdp); 1200*0Sstevel@tonic-gate debug("files_update_history: update user\n" 1201*0Sstevel@tonic-gate "\t%s:%s:", name, spwd->sp_pwdp); 1202*0Sstevel@tonic-gate 1203*0Sstevel@tonic-gate /* get old crypted password history */ 1204*0Sstevel@tonic-gate for (i = 0; i < MAXHISTORY-1; i++) { 1205*0Sstevel@tonic-gate crypt = strtok_r(NULL, ":", &last); 1206*0Sstevel@tonic-gate if (crypt == NULL || 1207*0Sstevel@tonic-gate *crypt == '\n') { 1208*0Sstevel@tonic-gate break; 1209*0Sstevel@tonic-gate } 1210*0Sstevel@tonic-gate (void) fprintf(dst, "%s:", crypt); 1211*0Sstevel@tonic-gate debug("\t%d = %s:", i+1, crypt); 1212*0Sstevel@tonic-gate } 1213*0Sstevel@tonic-gate (void) fprintf(dst, "\n"); 1214*0Sstevel@tonic-gate } else { 1215*0Sstevel@tonic-gate 1216*0Sstevel@tonic-gate /* copy other users to updated file */ 1217*0Sstevel@tonic-gate (void) fprintf(dst, "%s:%s", user, last); 1218*0Sstevel@tonic-gate #ifdef DEBUG 1219*0Sstevel@tonic-gate debug("files_update_history: copy line %s", 1220*0Sstevel@tonic-gate user); 1221*0Sstevel@tonic-gate #endif /* DEBUG */ 1222*0Sstevel@tonic-gate } 1223*0Sstevel@tonic-gate } 1224*0Sstevel@tonic-gate 1225*0Sstevel@tonic-gate if (found == 0) { 1226*0Sstevel@tonic-gate 1227*0Sstevel@tonic-gate /* user not found, add to history file */ 1228*0Sstevel@tonic-gate (void) fprintf(dst, "%s:%s:\n", name, spwd->sp_pwdp); 1229*0Sstevel@tonic-gate debug("files_update_history: add line\n" 1230*0Sstevel@tonic-gate "\t%s:%s:", name, spwd->sp_pwdp); 1231*0Sstevel@tonic-gate } 1232*0Sstevel@tonic-gate 1233*0Sstevel@tonic-gate (void) fclose(src); 1234*0Sstevel@tonic-gate 1235*0Sstevel@tonic-gate /* If something messed up in file system, loose the update */ 1236*0Sstevel@tonic-gate if (fclose(dst) != 0) { 1237*0Sstevel@tonic-gate 1238*0Sstevel@tonic-gate debug("files_update_history: update file close failed %d", 1239*0Sstevel@tonic-gate errno); 1240*0Sstevel@tonic-gate (void) unlink(HISTEMP); 1241*0Sstevel@tonic-gate return (PWU_CLOSE_FAILED); 1242*0Sstevel@tonic-gate } 1243*0Sstevel@tonic-gate 1244*0Sstevel@tonic-gate /* 1245*0Sstevel@tonic-gate * rename history to ohistory, 1246*0Sstevel@tonic-gate * rename tmp to history, 1247*0Sstevel@tonic-gate * unlink ohistory. 1248*0Sstevel@tonic-gate */ 1249*0Sstevel@tonic-gate 1250*0Sstevel@tonic-gate (void) unlink(OHISTORY); 1251*0Sstevel@tonic-gate 1252*0Sstevel@tonic-gate if (stat64(OHISTORY, &statbuf) == 0 || 1253*0Sstevel@tonic-gate ((src != NULL) && (link(HISTORY, OHISTORY) != 0)) || 1254*0Sstevel@tonic-gate rename(HISTEMP, HISTORY) != 0) { 1255*0Sstevel@tonic-gate 1256*0Sstevel@tonic-gate /* old history won't go away, loose the update */ 1257*0Sstevel@tonic-gate debug("files_update_history: update file rename failed %d", 1258*0Sstevel@tonic-gate errno); 1259*0Sstevel@tonic-gate (void) unlink(HISTEMP); 1260*0Sstevel@tonic-gate return (PWU_UPDATE_FAILED); 1261*0Sstevel@tonic-gate } 1262*0Sstevel@tonic-gate 1263*0Sstevel@tonic-gate (void) unlink(OHISTORY); 1264*0Sstevel@tonic-gate return (PWU_SUCCESS); 1265*0Sstevel@tonic-gate } 1266