11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1988, 1993, 1994 31590Srgrimes * The Regents of the University of California. All rights reserved. 496201Sdes * Copyright (c) 2002 Networks Associates Technology, Inc. 596201Sdes * All rights reserved. 61590Srgrimes * 796201Sdes * Portions of this software were developed for the FreeBSD Project by 896201Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network 996201Sdes * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 1096201Sdes * ("CBOSS"), as part of the DARPA CHATS research program. 1196201Sdes * 121590Srgrimes * Redistribution and use in source and binary forms, with or without 131590Srgrimes * modification, are permitted provided that the following conditions 141590Srgrimes * are met: 151590Srgrimes * 1. Redistributions of source code must retain the above copyright 161590Srgrimes * notice, this list of conditions and the following disclaimer. 171590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 181590Srgrimes * notice, this list of conditions and the following disclaimer in the 191590Srgrimes * documentation and/or other materials provided with the distribution. 201590Srgrimes * 3. All advertising materials mentioning features or use of this software 211590Srgrimes * must display the following acknowledgement: 221590Srgrimes * This product includes software developed by the University of 231590Srgrimes * California, Berkeley and its contributors. 241590Srgrimes * 4. Neither the name of the University nor the names of its contributors 251590Srgrimes * may be used to endorse or promote products derived from this software 261590Srgrimes * without specific prior written permission. 271590Srgrimes * 281590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 291590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 301590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 311590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 321590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 331590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 341590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 351590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 361590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 371590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 381590Srgrimes * SUCH DAMAGE. 391590Srgrimes */ 401590Srgrimes 41114594Sobrien#if 0 421590Srgrimes#ifndef lint 4341568Sarchiestatic const char copyright[] = 441590Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\ 451590Srgrimes The Regents of the University of California. All rights reserved.\n"; 461590Srgrimes#endif /* not lint */ 471590Srgrimes 481590Srgrimes#ifndef lint 4993429Sdwmalonestatic char sccsid[] = "@(#)chpass.c 8.4 (Berkeley) 4/2/94"; 501590Srgrimes#endif /* not lint */ 5193429Sdwmalone#endif 5293086Smarkm#include <sys/cdefs.h> 5393086Smarkm__FBSDID("$FreeBSD: releng/11.0/usr.bin/chpass/chpass.c 243081 2012-11-15 15:06:18Z eadler $"); 5493086Smarkm 551590Srgrimes#include <sys/param.h> 561590Srgrimes 571590Srgrimes#include <err.h> 581590Srgrimes#include <errno.h> 591590Srgrimes#include <pwd.h> 601590Srgrimes#include <stdio.h> 611590Srgrimes#include <stdlib.h> 621590Srgrimes#include <string.h> 631590Srgrimes#include <unistd.h> 6410050Swpaul#ifdef YP 6596201Sdes#include <ypclnt.h> 6610050Swpaul#endif 671590Srgrimes 6896201Sdes#include <pw_scan.h> 6996201Sdes#include <libutil.h> 7096201Sdes 711590Srgrimes#include "chpass.h" 721590Srgrimes 7396201Sdesint master_mode; 741590Srgrimes 7596201Sdesstatic void baduser(void); 7696201Sdesstatic void usage(void); 771590Srgrimes 781590Srgrimesint 7993086Smarkmmain(int argc, char *argv[]) 801590Srgrimes{ 8117544Speter enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op; 82108434Sobrien struct passwd lpw, *old_pw, *pw; 831590Srgrimes int ch, pfd, tfd; 8496201Sdes const char *password; 8541568Sarchie char *arg = NULL; 8696201Sdes uid_t uid; 8710519Swpaul#ifdef YP 8896201Sdes struct ypclnt *ypclnt; 8996201Sdes const char *yp_domain = NULL, *yp_host = NULL; 9010519Swpaul#endif 911590Srgrimes 92108434Sobrien pw = old_pw = NULL; 931590Srgrimes op = EDITENTRY; 9410519Swpaul#ifdef YP 9596201Sdes while ((ch = getopt(argc, argv, "a:p:s:e:d:h:loy")) != -1) 9610519Swpaul#else 9724360Simp while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1) 9810519Swpaul#endif 9996201Sdes switch (ch) { 1001590Srgrimes case 'a': 1011590Srgrimes op = LOADENTRY; 1021590Srgrimes arg = optarg; 1031590Srgrimes break; 1041590Srgrimes case 's': 1051590Srgrimes op = NEWSH; 1061590Srgrimes arg = optarg; 1071590Srgrimes break; 1085628Swollman case 'p': 1095628Swollman op = NEWPW; 1105628Swollman arg = optarg; 1115628Swollman break; 11217544Speter case 'e': 11317544Speter op = NEWEXP; 11417544Speter arg = optarg; 11517544Speter break; 11610519Swpaul#ifdef YP 11796201Sdes case 'd': 11896201Sdes yp_domain = optarg; 11996201Sdes break; 12014212Swpaul case 'h': 12196201Sdes yp_host = optarg; 12214212Swpaul break; 12310519Swpaul case 'l': 12496201Sdes case 'o': 12510519Swpaul case 'y': 12696201Sdes /* compatibility */ 12710519Swpaul break; 12810519Swpaul#endif 1291590Srgrimes case '?': 1301590Srgrimes default: 1311590Srgrimes usage(); 1321590Srgrimes } 13396201Sdes 1341590Srgrimes argc -= optind; 1351590Srgrimes argv += optind; 1361590Srgrimes 13796201Sdes if (argc > 1) 13896201Sdes usage(); 13996201Sdes 1401590Srgrimes uid = getuid(); 1411590Srgrimes 14292528Scjc if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) { 14396201Sdes if (argc == 0) { 14496201Sdes if ((pw = getpwuid(uid)) == NULL) 14580424Smike errx(1, "unknown user: uid %lu", 14680424Smike (unsigned long)uid); 14796201Sdes } else { 14896201Sdes if ((pw = getpwnam(*argv)) == NULL) 1491590Srgrimes errx(1, "unknown user: %s", *argv); 15096201Sdes if (uid != 0 && uid != pw->pw_uid) 1511590Srgrimes baduser(); 1521590Srgrimes } 15392528Scjc 15492528Scjc /* Make a copy for later verification */ 15596201Sdes if ((pw = pw_dup(pw)) == NULL || 15696201Sdes (old_pw = pw_dup(pw)) == NULL) 15796201Sdes err(1, "pw_dup"); 15892528Scjc } 15992528Scjc 16096201Sdes#ifdef YP 161106138Sjoerg if (pw != NULL && (pw->pw_fields & _PWF_SOURCE) == _PWF_NIS) { 16296201Sdes ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host); 16396201Sdes master_mode = (ypclnt != NULL && 16496201Sdes ypclnt_connect(ypclnt) != -1 && 16596201Sdes ypclnt_havepasswdd(ypclnt) == 1); 16696201Sdes ypclnt_free(ypclnt); 16796201Sdes } else 16896201Sdes#endif 16996201Sdes master_mode = (uid == 0); 17096201Sdes 17113274Sjoerg if (op == NEWSH) { 17213274Sjoerg /* protect p_shell -- it thinks NULL is /bin/sh */ 17313274Sjoerg if (!arg[0]) 17413274Sjoerg usage(); 17596201Sdes if (p_shell(arg, pw, (ENTRY *)NULL) == -1) 17696201Sdes exit(1); 17713274Sjoerg } 17813274Sjoerg 17917544Speter if (op == NEWEXP) { 18017544Speter if (uid) /* only root can change expire */ 18117544Speter baduser(); 18296201Sdes if (p_expire(arg, pw, (ENTRY *)NULL) == -1) 18396201Sdes exit(1); 18417544Speter } 18517544Speter 18613274Sjoerg if (op == LOADENTRY) { 18713274Sjoerg if (uid) 18813274Sjoerg baduser(); 18913274Sjoerg pw = &lpw; 19096201Sdes old_pw = NULL; 19165532Snectar if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) 19213274Sjoerg exit(1); 19313274Sjoerg } 19413274Sjoerg 19513274Sjoerg if (op == NEWPW) { 19613274Sjoerg if (uid) 19713274Sjoerg baduser(); 19813274Sjoerg 19996201Sdes if (strchr(arg, ':')) 20013274Sjoerg errx(1, "invalid format for password"); 20113274Sjoerg pw->pw_passwd = arg; 20213274Sjoerg } 20313274Sjoerg 2041590Srgrimes if (op == EDITENTRY) { 20596201Sdes /* 20696201Sdes * We don't really need pw_*() here, but pw_edit() (used 20796201Sdes * by edit()) is just too useful... 20896201Sdes */ 20996201Sdes if (pw_init(NULL, NULL)) 21096201Sdes err(1, "pw_init()"); 21196201Sdes if ((tfd = pw_tmp(-1)) == -1) { 21296201Sdes pw_fini(); 21396201Sdes err(1, "pw_tmp()"); 21496201Sdes } 21596201Sdes free(pw); 21696201Sdes pw = edit(pw_tempname(), old_pw); 21796201Sdes pw_fini(); 21896201Sdes if (pw == NULL) 21996201Sdes err(1, "edit()"); 220162633Smarck /* 221162633Smarck * pw_equal does not check for crypted passwords, so we 222162633Smarck * should do it explicitly 223162633Smarck */ 224162633Smarck if (pw_equal(old_pw, pw) && 225162633Smarck strcmp(old_pw->pw_passwd, pw->pw_passwd) == 0) 22696201Sdes errx(0, "user information unchanged"); 2271590Srgrimes } 2288874Srgrimes 22996201Sdes if (old_pw && !master_mode) { 23096201Sdes password = getpass("Password: "); 23196201Sdes if (strcmp(crypt(password, old_pw->pw_passwd), 23296201Sdes old_pw->pw_passwd) != 0) 23396201Sdes baduser(); 23410050Swpaul } else { 23596201Sdes password = ""; 23696201Sdes } 2371590Srgrimes 23896201Sdes if (old_pw != NULL) 23996201Sdes pw->pw_fields |= (old_pw->pw_fields & _PWF_SOURCE); 24096201Sdes switch (pw->pw_fields & _PWF_SOURCE) { 24110050Swpaul#ifdef YP 24296201Sdes case _PWF_NIS: 24396201Sdes ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host); 244243081Seadler if (ypclnt == NULL) { 245243081Seadler warnx("ypclnt_new failed"); 246243081Seadler exit(1); 247243081Seadler } 248243081Seadler if (ypclnt_connect(ypclnt) == -1 || 24996201Sdes ypclnt_passwd(ypclnt, pw, password) == -1) { 25096201Sdes warnx("%s", ypclnt->error); 25196201Sdes ypclnt_free(ypclnt); 25296201Sdes exit(1); 25396201Sdes } 25496201Sdes ypclnt_free(ypclnt); 25596201Sdes errx(0, "NIS user information updated"); 25696201Sdes#endif /* YP */ 25796201Sdes case 0: 25896201Sdes case _PWF_FILES: 25996201Sdes if (pw_init(NULL, NULL)) 26096201Sdes err(1, "pw_init()"); 26196201Sdes if ((pfd = pw_lock()) == -1) { 26296201Sdes pw_fini(); 26396201Sdes err(1, "pw_lock()"); 26496201Sdes } 26596201Sdes if ((tfd = pw_tmp(-1)) == -1) { 26696201Sdes pw_fini(); 26796201Sdes err(1, "pw_tmp()"); 26896201Sdes } 26996201Sdes if (pw_copy(pfd, tfd, pw, old_pw) == -1) { 27096201Sdes pw_fini(); 27196201Sdes err(1, "pw_copy"); 27296201Sdes } 27396201Sdes if (pw_mkdb(pw->pw_name) == -1) { 27496201Sdes pw_fini(); 27596201Sdes err(1, "pw_mkdb()"); 27696201Sdes } 27796201Sdes pw_fini(); 27896201Sdes errx(0, "user information updated"); 27996201Sdes break; 28096201Sdes default: 28196201Sdes errx(1, "unsupported passwd source"); 28210050Swpaul } 2831590Srgrimes} 2841590Srgrimes 28596201Sdesstatic void 28693086Smarkmbaduser(void) 2871590Srgrimes{ 28896201Sdes 2891590Srgrimes errx(1, "%s", strerror(EACCES)); 2901590Srgrimes} 2911590Srgrimes 29296201Sdesstatic void 29393086Smarkmusage(void) 2941590Srgrimes{ 2951590Srgrimes 2965628Swollman (void)fprintf(stderr, 297124692Scharnier "usage: chpass%s %s [user]\n", 29810519Swpaul#ifdef YP 29996201Sdes " [-d domain] [-h host]", 30010519Swpaul#else 30196201Sdes "", 30210519Swpaul#endif 30396201Sdes "[-a list] [-p encpass] [-s shell] [-e mmm dd yy]"); 3041590Srgrimes exit(1); 3051590Srgrimes} 306