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