1 /* $NetBSD: passwd.c,v 1.11 1997/12/31 05:47:15 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1987, 1993, 1994, 1995 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #if defined(LIBC_SCCS) && !defined(lint) 38 __RCSID("$NetBSD: passwd.c,v 1.11 1997/12/31 05:47:15 thorpej Exp $"); 39 #endif /* LIBC_SCCS and not lint */ 40 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <sys/time.h> 44 #include <sys/resource.h> 45 #include <sys/wait.h> 46 47 #include <ctype.h> 48 #include <err.h> 49 #include <fcntl.h> 50 #include <unistd.h> 51 #include <stdlib.h> 52 #include <stdio.h> 53 #include <string.h> 54 #include <pwd.h> 55 #include <errno.h> 56 #include <paths.h> 57 #include <signal.h> 58 #include <limits.h> 59 #include <util.h> 60 61 static void pw_cont __P((int sig)); 62 static int pw_equal __P((char *buf, struct passwd *old_pw)); 63 64 int 65 pw_lock(retries) 66 int retries; 67 { 68 int i, fd; 69 mode_t old_mode; 70 71 /* Acquire the lock file. */ 72 old_mode = umask(0); 73 fd = open(_PATH_MASTERPASSWD_LOCK, O_WRONLY|O_CREAT|O_EXCL, 0600); 74 for (i = 0; i < retries && fd < 0 && errno == EEXIST; i++) { 75 sleep(1); 76 fd = open(_PATH_MASTERPASSWD_LOCK, O_WRONLY|O_CREAT|O_EXCL, 77 0600); 78 } 79 umask(old_mode); 80 return(fd); 81 } 82 83 int 84 pw_mkdb() 85 { 86 int pstat; 87 pid_t pid; 88 89 pid = vfork(); 90 if (pid == 0) { 91 execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 92 _PATH_MASTERPASSWD_LOCK, NULL); 93 _exit(1); 94 } 95 pid = waitpid(pid, &pstat, 0); 96 if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) 97 return(-1); 98 return(0); 99 } 100 101 int 102 pw_abort() 103 { 104 return(unlink(_PATH_MASTERPASSWD_LOCK)); 105 } 106 107 /* Everything below this point is intended for the convenience of programs 108 * which allow a user to interactively edit the passwd file. Errors in the 109 * routines below will cause the process to abort. */ 110 111 static pid_t editpid = -1; 112 113 static void 114 pw_cont(sig) 115 int sig; 116 { 117 118 if (editpid != -1) 119 kill(editpid, sig); 120 } 121 122 void 123 pw_init() 124 { 125 struct rlimit rlim; 126 127 /* Unlimited resource limits. */ 128 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 129 (void)setrlimit(RLIMIT_CPU, &rlim); 130 (void)setrlimit(RLIMIT_FSIZE, &rlim); 131 (void)setrlimit(RLIMIT_STACK, &rlim); 132 (void)setrlimit(RLIMIT_DATA, &rlim); 133 (void)setrlimit(RLIMIT_RSS, &rlim); 134 135 /* Don't drop core (not really necessary, but GP's). */ 136 rlim.rlim_cur = rlim.rlim_max = 0; 137 (void)setrlimit(RLIMIT_CORE, &rlim); 138 139 /* Turn off signals. */ 140 (void)signal(SIGALRM, SIG_IGN); 141 (void)signal(SIGHUP, SIG_IGN); 142 (void)signal(SIGINT, SIG_IGN); 143 (void)signal(SIGPIPE, SIG_IGN); 144 (void)signal(SIGQUIT, SIG_IGN); 145 (void)signal(SIGTERM, SIG_IGN); 146 (void)signal(SIGCONT, pw_cont); 147 } 148 149 void 150 pw_edit(notsetuid, filename) 151 int notsetuid; 152 const char *filename; 153 { 154 int i, xargc, pstat; 155 char *p, *editor; 156 char **xargv; 157 #ifdef __GNUC__ 158 (void) &editor; 159 #endif 160 161 if (filename == NULL) 162 filename = _PATH_MASTERPASSWD_LOCK; 163 if ((editor = getenv("EDITOR")) == NULL) 164 editor = strdup(_PATH_VI); 165 else 166 editor = strdup(editor); 167 if ((p = strrchr(editor, '/'))) 168 ++p; 169 else 170 p = editor; 171 172 /* Scan editor string, count spaces, allocate arg vector. */ 173 for (i = 0, xargc = 0; p[i] != '\0'; i++) { 174 if (isspace(p[i])) { 175 while (isspace(p[i++])) 176 /* skip white space */ ; 177 if (p[i] == '\0') 178 break; 179 xargc++; 180 } 181 } 182 183 /* argv[0] + <xargc args> + filename + NULL */ 184 xargv = (char **)malloc(sizeof(char *) * (xargc + 3)); 185 if (xargv == NULL) 186 pw_error("malloc failed", 1, 1); 187 188 i = 0; 189 xargv[i++] = p; 190 for (; *p != '\0'; p++) { 191 if (isspace(*p)) { 192 while(isspace(*p)) 193 *p++ = '\0'; /* blast whitespace */ 194 if (*p == '\0') 195 break; 196 xargv[i++] = p; 197 } 198 } 199 200 xargv[i++] = (char *)filename; 201 xargv[i] = NULL; 202 203 if (!(editpid = vfork())) { 204 if (notsetuid) { 205 setgid(getgid()); 206 setuid(getuid()); 207 } 208 execvp(editor, xargv); 209 _exit(1); 210 } 211 for (;;) { 212 editpid = waitpid(editpid, (int *)&pstat, WUNTRACED); 213 if (editpid == -1) 214 pw_error(editor, 1, 1); 215 else if (WIFSTOPPED(pstat)) 216 raise(WSTOPSIG(pstat)); 217 else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) 218 break; 219 else 220 pw_error(editor, 1, 1); 221 } 222 editpid = -1; 223 free(editor); 224 free(xargv); 225 } 226 227 void 228 pw_prompt() 229 { 230 int c; 231 232 (void)printf("re-edit the password file? [y]: "); 233 (void)fflush(stdout); 234 c = getchar(); 235 if (c != EOF && c != '\n') 236 while (getchar() != '\n'); 237 if (c == 'n') 238 pw_error(NULL, 0, 0); 239 } 240 241 /* for use in pw_copy(). Compare a pw entry to a pw struct. */ 242 static int 243 pw_equal (buf, pw) 244 char *buf; 245 struct passwd *pw; 246 { 247 struct passwd buf_pw; 248 int len = strlen (buf); 249 if (buf[len-1] == '\n') 250 buf[len-1] = '\0'; 251 if (!pw_scan(buf, &buf_pw, NULL)) 252 return 0; 253 return !strcmp(pw->pw_name, buf_pw.pw_name) 254 && pw->pw_uid == buf_pw.pw_uid 255 && pw->pw_gid == buf_pw.pw_gid 256 && !strcmp(pw->pw_class, buf_pw.pw_class) 257 && (long)pw->pw_change == (long)buf_pw.pw_change 258 && (long)pw->pw_expire == (long)buf_pw.pw_expire 259 && !strcmp(pw->pw_gecos, buf_pw.pw_gecos) 260 && !strcmp(pw->pw_dir, buf_pw.pw_dir) 261 && !strcmp(pw->pw_shell, buf_pw.pw_shell); 262 } 263 264 void 265 pw_copy(ffd, tfd, pw, old_pw) 266 int ffd, tfd; 267 struct passwd *pw, *old_pw; 268 { 269 FILE *from, *to; 270 int done; 271 char *p, buf[8192]; 272 273 if (!(from = fdopen(ffd, "r"))) 274 pw_error(_PATH_MASTERPASSWD, 1, 1); 275 if (!(to = fdopen(tfd, "w"))) 276 pw_error(_PATH_MASTERPASSWD_LOCK, 1, 1); 277 278 for (done = 0; fgets(buf, sizeof(buf), from);) { 279 if (!strchr(buf, '\n')) { 280 warnx("%s: line too long", _PATH_MASTERPASSWD); 281 pw_error(NULL, 0, 1); 282 } 283 if (done) { 284 (void)fprintf(to, "%s", buf); 285 if (ferror(to)) 286 goto err; 287 continue; 288 } 289 if (!(p = strchr(buf, ':'))) { 290 warnx("%s: corrupted entry", _PATH_MASTERPASSWD); 291 pw_error(NULL, 0, 1); 292 } 293 *p = '\0'; 294 if (strcmp(buf, pw->pw_name)) { 295 *p = ':'; 296 (void)fprintf(to, "%s", buf); 297 if (ferror(to)) 298 goto err; 299 continue; 300 } 301 *p = ':'; 302 if (old_pw && !pw_equal(buf, old_pw)) { 303 warnx("%s: entry inconsistent", 304 _PATH_MASTERPASSWD); 305 pw_error(NULL, 0, 1); 306 } 307 (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", 308 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, 309 pw->pw_class, (long)pw->pw_change, (long)pw->pw_expire, 310 pw->pw_gecos, pw->pw_dir, pw->pw_shell); 311 done = 1; 312 if (ferror(to)) 313 goto err; 314 } 315 /* Only append a new entry if real uid is root! */ 316 if (!done) 317 if (getuid() == 0) 318 (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", 319 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, 320 pw->pw_class, (long)pw->pw_change, 321 (long)pw->pw_expire, pw->pw_gecos, pw->pw_dir, 322 pw->pw_shell); 323 else 324 warnx("%s: changes not made, no such entry", 325 _PATH_MASTERPASSWD); 326 327 if (ferror(to)) 328 err: pw_error(NULL, 1, 1); 329 (void)fclose(to); 330 } 331 332 int 333 pw_scan(bp, pw, flags) 334 char *bp; 335 struct passwd *pw; 336 int *flags; 337 { 338 unsigned long id; 339 int root; 340 char *p, *sh, *ep; 341 342 if (flags != (int *)NULL) 343 *flags = 0; 344 345 if (!(pw->pw_name = strsep(&bp, ":"))) /* login */ 346 goto fmt; 347 root = !strcmp(pw->pw_name, "root"); 348 349 if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */ 350 goto fmt; 351 352 if (!(p = strsep(&bp, ":"))) /* uid */ 353 goto fmt; 354 id = strtoul(p, &ep, 10); 355 if (root && id) { 356 warnx("root uid should be 0"); 357 return (0); 358 } 359 if (id > UID_MAX || *ep != '\0') { 360 warnx("invalid uid '%s'", p); 361 return (0); 362 } 363 pw->pw_uid = (uid_t)id; 364 if ((*p == '\0') && (flags != (int *)NULL)) 365 *flags |= _PASSWORD_NOUID; 366 367 if (!(p = strsep(&bp, ":"))) /* gid */ 368 goto fmt; 369 id = strtoul(p, &ep, 10); 370 if (id > GID_MAX || *ep != '\0') { 371 warnx("invalid gid '%s'", p); 372 return (0); 373 } 374 pw->pw_gid = (gid_t)id; 375 if ((*p == '\0') && (flags != (int *)NULL)) 376 *flags |= _PASSWORD_NOGID; 377 378 pw->pw_class = strsep(&bp, ":"); /* class */ 379 if (!(p = strsep(&bp, ":"))) /* change */ 380 goto fmt; 381 pw->pw_change = atol(p); 382 if ((*p == '\0') && (flags != (int *)NULL)) 383 *flags |= _PASSWORD_NOCHG; 384 if (!(p = strsep(&bp, ":"))) /* expire */ 385 goto fmt; 386 pw->pw_expire = atol(p); 387 if ((*p == '\0') && (flags != (int *)NULL)) 388 *flags |= _PASSWORD_NOEXP; 389 pw->pw_gecos = strsep(&bp, ":"); /* gecos */ 390 pw->pw_dir = strsep(&bp, ":"); /* directory */ 391 if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */ 392 goto fmt; 393 394 p = pw->pw_shell; 395 if (root && *p) /* empty == /bin/sh */ 396 for (setusershell();;) { 397 if (!(sh = getusershell())) { 398 warnx("warning, unknown root shell"); 399 break; 400 } 401 if (!strcmp(p, sh)) 402 break; 403 } 404 405 if ((p = strsep(&bp, ":"))) { /* too many */ 406 fmt: warnx("corrupted entry"); 407 return (0); 408 } 409 410 return (1); 411 } 412 413 void 414 pw_error(name, err, eval) 415 const char *name; 416 int err, eval; 417 { 418 if (err) 419 warn(name); 420 421 warnx("%s: unchanged", _PATH_MASTERPASSWD); 422 pw_abort(); 423 exit(eval); 424 } 425 426