1 /*- 2 * Copyright (c) 1988, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#) Copyright (c) 1988, 1993, 1994 The Regents of the University of California. All rights reserved. 34 * @(#)chpass.c 8.4 (Berkeley) 4/2/94 35 * $FreeBSD: src/usr.bin/chpass/chpass.c,v 1.16.2.4 2002/08/11 14:16:42 dwmalone Exp $ 36 * $DragonFly: src/usr.bin/chpass/chpass.c,v 1.4 2003/11/03 19:31:28 eirikn Exp $ 37 */ 38 39 #include <sys/param.h> 40 #include <sys/stat.h> 41 #include <sys/signal.h> 42 #include <sys/time.h> 43 #include <sys/resource.h> 44 45 #include <ctype.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <pwd.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 55 #include <pw_scan.h> 56 #include <pw_util.h> 57 #include "pw_copy.h" 58 #ifdef YP 59 #include <rpcsvc/yp.h> 60 int yp_errno = YP_TRUE; 61 #include "pw_yp.h" 62 #endif 63 64 #include "chpass.h" 65 #include "pathnames.h" 66 67 char *tempname; 68 uid_t uid; 69 70 void baduser(void); 71 void usage(void); 72 73 int 74 main(int argc, char **argv) 75 { 76 enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op; 77 struct passwd *pw = NULL, lpw, old_pw, *pold_pw; 78 char *username = NULL; 79 int ch, pfd, tfd; 80 char *arg = NULL; 81 #ifdef YP 82 int force_local = 0; 83 int force_yp = 0; 84 #endif 85 86 op = EDITENTRY; 87 #ifdef YP 88 while ((ch = getopt(argc, argv, "a:p:s:e:d:h:oly")) != -1) 89 #else 90 while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1) 91 #endif 92 switch(ch) { 93 case 'a': 94 op = LOADENTRY; 95 arg = optarg; 96 break; 97 case 's': 98 op = NEWSH; 99 arg = optarg; 100 break; 101 case 'p': 102 op = NEWPW; 103 arg = optarg; 104 break; 105 case 'e': 106 op = NEWEXP; 107 arg = optarg; 108 break; 109 #ifdef YP 110 case 'h': 111 #ifdef PARANOID 112 if (getuid()) { 113 warnx("Only the superuser can use the -h flag"); 114 } else { 115 #endif 116 yp_server = optarg; 117 #ifdef PARANOID 118 } 119 #endif 120 break; 121 case 'd': 122 #ifdef PARANOID 123 if (getuid()) { 124 warnx("Only the superuser can use the -d flag"); 125 } else { 126 #endif 127 yp_domain = optarg; 128 if (yp_server == NULL) 129 yp_server = "localhost"; 130 #ifdef PARANOID 131 } 132 #endif 133 break; 134 case 'l': 135 _use_yp = 0; 136 force_local = 1; 137 break; 138 case 'y': 139 _use_yp = force_yp = 1; 140 break; 141 case 'o': 142 force_old++; 143 break; 144 #endif 145 case '?': 146 default: 147 usage(); 148 } 149 argc -= optind; 150 argv += optind; 151 152 uid = getuid(); 153 154 if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) { 155 switch(argc) { 156 #ifdef YP 157 case 0: 158 GETPWUID(uid) 159 get_yp_master(1); /* XXX just to set the suser flag */ 160 break; 161 case 1: 162 GETPWNAM(*argv) 163 get_yp_master(1); /* XXX just to set the suser flag */ 164 #else 165 case 0: 166 if (!(pw = getpwuid(uid))) 167 errx(1, "unknown user: uid %lu", 168 (unsigned long)uid); 169 break; 170 case 1: 171 if (!(pw = getpwnam(*argv))) 172 errx(1, "unknown user: %s", *argv); 173 #endif 174 if (uid && uid != pw->pw_uid) 175 baduser(); 176 break; 177 default: 178 usage(); 179 } 180 181 /* Make a copy for later verification */ 182 old_pw = *pw; 183 old_pw.pw_gecos = strdup(old_pw.pw_gecos); 184 pold_pw = &old_pw; 185 } 186 187 if (op == NEWSH) { 188 /* protect p_shell -- it thinks NULL is /bin/sh */ 189 if (!arg[0]) 190 usage(); 191 if (p_shell(arg, pw, (ENTRY *)NULL)) 192 pw_error((char *)NULL, 0, 1); 193 } 194 195 if (op == NEWEXP) { 196 if (uid) /* only root can change expire */ 197 baduser(); 198 if (p_expire(arg, pw, (ENTRY *)NULL)) 199 pw_error((char *)NULL, 0, 1); 200 } 201 202 if (op == LOADENTRY) { 203 if (uid) 204 baduser(); 205 pw = &lpw; 206 if (!pw_scan(arg, pw)) 207 exit(1); 208 if ((pold_pw = getpwnam(pw->pw_name)) != NULL) { 209 old_pw = *pold_pw; 210 old_pw.pw_gecos = strdup(old_pw.pw_gecos); 211 } 212 } 213 username = pw->pw_name; 214 215 if (op == NEWPW) { 216 if (uid) 217 baduser(); 218 219 if(strchr(arg, ':')) { 220 errx(1, "invalid format for password"); 221 } 222 pw->pw_passwd = arg; 223 } 224 225 /* 226 * The temporary file/file descriptor usage is a little tricky here. 227 * 1: We start off with two fd's, one for the master password 228 * file (used to lock everything), and one for a temporary file. 229 * 2: Display() gets an fp for the temporary file, and copies the 230 * user's information into it. It then gives the temporary file 231 * to the user and closes the fp, closing the underlying fd. 232 * 3: The user edits the temporary file some number of times. 233 * 4: Verify() gets an fp for the temporary file, and verifies the 234 * contents. It can't use an fp derived from the step #2 fd, 235 * because the user's editor may have created a new instance of 236 * the file. Once the file is verified, its contents are stored 237 * in a password structure. The verify routine closes the fp, 238 * closing the underlying fd. 239 * 5: Delete the temporary file. 240 * 6: Get a new temporary file/fd. Pw_copy() gets an fp for it 241 * file and copies the master password file into it, replacing 242 * the user record with a new one. We can't use the first 243 * temporary file for this because it was owned by the user. 244 * Pw_copy() closes its fp, flushing the data and closing the 245 * underlying file descriptor. We can't close the master 246 * password fp, or we'd lose the lock. 247 * 7: Call pw_mkdb() (which renames the temporary file) and exit. 248 * The exit closes the master passwd fp/fd. 249 */ 250 pw_init(); 251 tfd = pw_tmp(); 252 253 if (op == EDITENTRY) { 254 display(tfd, pw); 255 edit(pw); 256 (void)unlink(tempname); 257 tfd = pw_tmp(); 258 } 259 260 #ifdef YP 261 if (_use_yp) { 262 yp_submit(pw); 263 (void)unlink(tempname); 264 } else { 265 #endif /* YP */ 266 pfd = pw_lock(); 267 pw_copy(pfd, tfd, pw, pold_pw); 268 269 if (!pw_mkdb(username)) 270 pw_error((char *)NULL, 0, 1); 271 #ifdef YP 272 } 273 #endif /* YP */ 274 exit(0); 275 } 276 277 void 278 baduser(void) 279 { 280 errx(1, "%s", strerror(EACCES)); 281 } 282 283 void 284 usage(void) 285 { 286 287 (void)fprintf(stderr, 288 #ifdef YP 289 "usage: chpass [-o] [-l] [-y] [-d domain] [-h host] [-a list] [-p encpass] [-s shell] [-e mmm dd yy] [user]\n"); 290 #else 291 "usage: chpass [-a list] [-p encpass] [-s shell] [-e mmm dd yy] [user]\n"); 292 #endif 293 exit(1); 294 } 295