chpass.c revision 225736
1145132Sanholt/*- 2145132Sanholt * Copyright (c) 1988, 1993, 1994 3152909Sanholt * The Regents of the University of California. All rights reserved. 4145132Sanholt * Copyright (c) 2002 Networks Associates Technology, Inc. 5145132Sanholt * All rights reserved. 6182080Srnoland * 7152909Sanholt * Portions of this software were developed for the FreeBSD Project by 8152909Sanholt * ThinkSec AS and NAI Labs, the Security Research Division of Network 9152909Sanholt * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10152909Sanholt * ("CBOSS"), as part of the DARPA CHATS research program. 11152909Sanholt * 12152909Sanholt * Redistribution and use in source and binary forms, with or without 13152909Sanholt * modification, are permitted provided that the following conditions 14182080Srnoland * are met: 15152909Sanholt * 1. Redistributions of source code must retain the above copyright 16152909Sanholt * notice, this list of conditions and the following disclaimer. 17152909Sanholt * 2. Redistributions in binary form must reproduce the above copyright 18182080Srnoland * notice, this list of conditions and the following disclaimer in the 19152909Sanholt * documentation and/or other materials provided with the distribution. 20152909Sanholt * 3. All advertising materials mentioning features or use of this software 21152909Sanholt * must display the following acknowledgement: 22152909Sanholt * This product includes software developed by the University of 23152909Sanholt * California, Berkeley and its contributors. 24152909Sanholt * 4. Neither the name of the University nor the names of its contributors 25152909Sanholt * may be used to endorse or promote products derived from this software 26182080Srnoland * without specific prior written permission. 27152909Sanholt * 28145132Sanholt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29152909Sanholt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30152909Sanholt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31145132Sanholt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32152909Sanholt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33152909Sanholt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34152909Sanholt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35152909Sanholt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36152909Sanholt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37145132Sanholt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38145132Sanholt * SUCH DAMAGE. 39145132Sanholt */ 40145132Sanholt 41145132Sanholt#if 0 42182080Srnoland#ifndef lint 43145132Sanholtstatic const char copyright[] = 44145132Sanholt"@(#) Copyright (c) 1988, 1993, 1994\n\ 45145132Sanholt The Regents of the University of California. All rights reserved.\n"; 46182080Srnoland#endif /* not lint */ 47183573Srnoland 48183573Srnoland#ifndef lint 49183573Srnolandstatic char sccsid[] = "@(#)chpass.c 8.4 (Berkeley) 4/2/94"; 50145132Sanholt#endif /* not lint */ 51145132Sanholt#endif 52183573Srnoland#include <sys/cdefs.h> 53182080Srnoland__FBSDID("$FreeBSD: stable/9/usr.bin/chpass/chpass.c 162633 2006-09-25 15:06:24Z marck $"); 54183573Srnoland 55145132Sanholt#include <sys/param.h> 56145132Sanholt 57145132Sanholt#include <err.h> 58145132Sanholt#include <errno.h> 59145132Sanholt#include <pwd.h> 60145132Sanholt#include <stdio.h> 61189049Srnoland#include <stdlib.h> 62189049Srnoland#include <string.h> 63189049Srnoland#include <unistd.h> 64145132Sanholt#ifdef YP 65145132Sanholt#include <ypclnt.h> 66145132Sanholt#endif 67183573Srnoland 68183573Srnoland#include <pw_scan.h> 69183573Srnoland#include <libutil.h> 70145132Sanholt 71183573Srnoland#include "chpass.h" 72183573Srnoland 73145132Sanholtint master_mode; 74145132Sanholt 75182080Srnolandstatic void baduser(void); 76145132Sanholtstatic void usage(void); 77145132Sanholt 78189049Srnolandint 79189049Srnolandmain(int argc, char *argv[]) 80189049Srnoland{ 81189049Srnoland enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op; 82189049Srnoland struct passwd lpw, *old_pw, *pw; 83183573Srnoland int ch, pfd, tfd; 84183573Srnoland const char *password; 85183573Srnoland char *arg = NULL; 86183573Srnoland uid_t uid; 87183573Srnoland#ifdef YP 88189049Srnoland struct ypclnt *ypclnt; 89189049Srnoland const char *yp_domain = NULL, *yp_host = NULL; 90183573Srnoland#endif 91189049Srnoland 92183573Srnoland pw = old_pw = NULL; 93183573Srnoland op = EDITENTRY; 94183573Srnoland#ifdef YP 95189049Srnoland while ((ch = getopt(argc, argv, "a:p:s:e:d:h:loy")) != -1) 96189049Srnoland#else 97183573Srnoland while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1) 98183573Srnoland#endif 99183573Srnoland switch (ch) { 100189049Srnoland case 'a': 101183573Srnoland op = LOADENTRY; 102183573Srnoland arg = optarg; 103183573Srnoland break; 104183573Srnoland case 's': 105189049Srnoland op = NEWSH; 106189049Srnoland arg = optarg; 107189049Srnoland break; 108189049Srnoland case 'p': 109189049Srnoland op = NEWPW; 110183573Srnoland arg = optarg; 111183573Srnoland break; 112183573Srnoland case 'e': 113183573Srnoland op = NEWEXP; 114183573Srnoland arg = optarg; 115183573Srnoland break; 116183573Srnoland#ifdef YP 117183573Srnoland case 'd': 118183573Srnoland yp_domain = optarg; 119183573Srnoland break; 120183573Srnoland case 'h': 121183573Srnoland yp_host = optarg; 122189049Srnoland break; 123189049Srnoland case 'l': 124183573Srnoland case 'o': 125183573Srnoland case 'y': 126182080Srnoland /* compatibility */ 127145132Sanholt break; 128145132Sanholt#endif 129145132Sanholt case '?': 130145132Sanholt default: 131182080Srnoland usage(); 132182080Srnoland } 133145132Sanholt 134145132Sanholt argc -= optind; 135145132Sanholt argv += optind; 136189049Srnoland 137189049Srnoland if (argc > 1) 138189049Srnoland usage(); 139145132Sanholt 140145132Sanholt uid = getuid(); 141182080Srnoland 142145132Sanholt if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) { 143182080Srnoland if (argc == 0) { 144145132Sanholt if ((pw = getpwuid(uid)) == NULL) 145145132Sanholt errx(1, "unknown user: uid %lu", 146145132Sanholt (unsigned long)uid); 147145132Sanholt } else { 148183573Srnoland if ((pw = getpwnam(*argv)) == NULL) 149145132Sanholt errx(1, "unknown user: %s", *argv); 150145132Sanholt if (uid != 0 && uid != pw->pw_uid) 151182080Srnoland baduser(); 152182080Srnoland } 153189049Srnoland 154207066Srnoland /* Make a copy for later verification */ 155182080Srnoland if ((pw = pw_dup(pw)) == NULL || 156182080Srnoland (old_pw = pw_dup(pw)) == NULL) 157145132Sanholt err(1, "pw_dup"); 158189049Srnoland } 159183573Srnoland 160189049Srnoland#ifdef YP 161145132Sanholt if (pw != NULL && (pw->pw_fields & _PWF_SOURCE) == _PWF_NIS) { 162145132Sanholt ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host); 163145132Sanholt master_mode = (ypclnt != NULL && 164145132Sanholt ypclnt_connect(ypclnt) != -1 && 165189049Srnoland ypclnt_havepasswdd(ypclnt) == 1); 166145132Sanholt ypclnt_free(ypclnt); 167182080Srnoland } else 168171394Skib#endif 169182080Srnoland master_mode = (uid == 0); 170145132Sanholt 171145132Sanholt if (op == NEWSH) { 172145132Sanholt /* protect p_shell -- it thinks NULL is /bin/sh */ 173182080Srnoland if (!arg[0]) 174145132Sanholt usage(); 175145132Sanholt if (p_shell(arg, pw, (ENTRY *)NULL) == -1) 176189049Srnoland exit(1); 177207066Srnoland } 178145132Sanholt 179189049Srnoland if (op == NEWEXP) { 180189049Srnoland if (uid) /* only root can change expire */ 181189049Srnoland baduser(); 182189049Srnoland if (p_expire(arg, pw, (ENTRY *)NULL) == -1) 183189049Srnoland exit(1); 184189049Srnoland } 185189049Srnoland 186182080Srnoland if (op == LOADENTRY) { 187183573Srnoland if (uid) 188183573Srnoland baduser(); 189145132Sanholt pw = &lpw; 190183573Srnoland old_pw = NULL; 191183573Srnoland if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) 192183573Srnoland exit(1); 193183573Srnoland } 194183573Srnoland 195145132Sanholt if (op == NEWPW) { 196190021Srnoland if (uid) 197145132Sanholt baduser(); 198207066Srnoland 199183573Srnoland if (strchr(arg, ':')) 200183573Srnoland errx(1, "invalid format for password"); 201183573Srnoland pw->pw_passwd = arg; 202183573Srnoland } 203183573Srnoland 204145132Sanholt if (op == EDITENTRY) { 205145132Sanholt /* 206207066Srnoland * We don't really need pw_*() here, but pw_edit() (used 207189049Srnoland * by edit()) is just too useful... 208182080Srnoland */ 209189049Srnoland if (pw_init(NULL, NULL)) 210189049Srnoland err(1, "pw_init()"); 211189049Srnoland if ((tfd = pw_tmp(-1)) == -1) { 212190021Srnoland pw_fini(); 213145132Sanholt err(1, "pw_tmp()"); 214145132Sanholt } 215145132Sanholt free(pw); 216145132Sanholt pw = edit(pw_tempname(), old_pw); 217145132Sanholt pw_fini(); 218145132Sanholt if (pw == NULL) 219145132Sanholt err(1, "edit()"); 220145132Sanholt /* 221182080Srnoland * pw_equal does not check for crypted passwords, so we 222145132Sanholt * should do it explicitly 223145132Sanholt */ 224145132Sanholt if (pw_equal(old_pw, pw) && 225182080Srnoland strcmp(old_pw->pw_passwd, pw->pw_passwd) == 0) 226145132Sanholt errx(0, "user information unchanged"); 227145132Sanholt } 228145132Sanholt 229182080Srnoland if (old_pw && !master_mode) { 230145132Sanholt password = getpass("Password: "); 231145132Sanholt if (strcmp(crypt(password, old_pw->pw_passwd), 232207066Srnoland old_pw->pw_passwd) != 0) 233145132Sanholt baduser(); 234145132Sanholt } else { 235182080Srnoland password = ""; 236145132Sanholt } 237145132Sanholt 238145132Sanholt if (old_pw != NULL) 239145132Sanholt pw->pw_fields |= (old_pw->pw_fields & _PWF_SOURCE); 240145132Sanholt switch (pw->pw_fields & _PWF_SOURCE) { 241182080Srnoland#ifdef YP 242145132Sanholt case _PWF_NIS: 243145132Sanholt ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host); 244145132Sanholt if (ypclnt == NULL || 245182080Srnoland ypclnt_connect(ypclnt) == -1 || 246189049Srnoland ypclnt_passwd(ypclnt, pw, password) == -1) { 247182080Srnoland warnx("%s", ypclnt->error); 248189049Srnoland ypclnt_free(ypclnt); 249145132Sanholt exit(1); 250145132Sanholt } 251145132Sanholt ypclnt_free(ypclnt); 252145132Sanholt errx(0, "NIS user information updated"); 253145132Sanholt#endif /* YP */ 254182080Srnoland case 0: 255182080Srnoland case _PWF_FILES: 256145132Sanholt if (pw_init(NULL, NULL)) 257182080Srnoland err(1, "pw_init()"); 258145132Sanholt if ((pfd = pw_lock()) == -1) { 259145132Sanholt pw_fini(); 260182080Srnoland err(1, "pw_lock()"); 261145132Sanholt } 262189049Srnoland if ((tfd = pw_tmp(-1)) == -1) { 263145132Sanholt pw_fini(); 264145132Sanholt err(1, "pw_tmp()"); 265145132Sanholt } 266145132Sanholt if (pw_copy(pfd, tfd, pw, old_pw) == -1) { 267145132Sanholt pw_fini(); 268145132Sanholt err(1, "pw_copy"); 269145132Sanholt } 270145132Sanholt if (pw_mkdb(pw->pw_name) == -1) { 271145132Sanholt pw_fini(); 272145132Sanholt err(1, "pw_mkdb()"); 273145132Sanholt } 274145132Sanholt pw_fini(); 275145132Sanholt errx(0, "user information updated"); 276145132Sanholt break; 277145132Sanholt default: 278145132Sanholt errx(1, "unsupported passwd source"); 279145132Sanholt } 280145132Sanholt} 281145132Sanholt 282145132Sanholtstatic void 283145132Sanholtbaduser(void) 284145132Sanholt{ 285145132Sanholt 286145132Sanholt errx(1, "%s", strerror(EACCES)); 287145132Sanholt} 288145132Sanholt 289145132Sanholtstatic void 290145132Sanholtusage(void) 291145132Sanholt{ 292145132Sanholt 293145132Sanholt (void)fprintf(stderr, 294145132Sanholt "usage: chpass%s %s [user]\n", 295145132Sanholt#ifdef YP 296145132Sanholt " [-d domain] [-h host]", 297145132Sanholt#else 298145132Sanholt "", 299145132Sanholt#endif 300145132Sanholt "[-a list] [-p encpass] [-s shell] [-e mmm dd yy]"); 301145132Sanholt exit(1); 302145132Sanholt} 303145132Sanholt