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