1 /* $OpenBSD: passwd.c,v 1.49 2006/12/20 23:07:36 ray 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <sys/time.h> 35 #include <sys/resource.h> 36 #include <sys/wait.h> 37 38 #include <fcntl.h> 39 #include <unistd.h> 40 #include <stdlib.h> 41 #include <stdio.h> 42 #include <string.h> 43 #include <ctype.h> 44 #include <pwd.h> 45 #include <err.h> 46 #include <errno.h> 47 #include <paths.h> 48 #include <signal.h> 49 #include <limits.h> 50 51 #include "util.h" 52 53 static char pw_defdir[] = "/etc"; 54 static char *pw_dir = pw_defdir; 55 static char *pw_lck; 56 57 char * 58 pw_file(const char *nm) 59 { 60 const char *p = strrchr(nm, '/'); 61 char *new_nm; 62 63 if (p) 64 p++; 65 else 66 p = nm; 67 68 if (asprintf(&new_nm, "%s/%s", pw_dir, p) == -1) 69 return NULL; 70 return new_nm; 71 } 72 73 void 74 pw_setdir(const char *dir) 75 { 76 char *p; 77 78 if (strcmp (dir, pw_dir) == 0) 79 return; 80 if (pw_dir != pw_defdir) 81 free(pw_dir); 82 pw_dir = strdup(dir); 83 if (pw_lck) { 84 p = pw_file(pw_lck); 85 free(pw_lck); 86 pw_lck = p; 87 } 88 } 89 90 91 int 92 pw_lock(int retries) 93 { 94 int i, fd; 95 mode_t old_mode; 96 int save_errno; 97 98 if (!pw_lck) { 99 errno = EINVAL; 100 return (-1); 101 } 102 /* Acquire the lock file. */ 103 old_mode = umask(0); 104 fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL, 0600); 105 for (i = 0; i < retries && fd < 0 && errno == EEXIST; i++) { 106 sleep(1); 107 fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL, 0600); 108 } 109 save_errno = errno; 110 if (fd != -1 && fcntl(fd, F_SETFD, 1) == -1) { 111 close(fd); 112 fd = -1; 113 } 114 (void) umask(old_mode); 115 errno = save_errno; 116 return (fd); 117 } 118 119 int 120 pw_mkdb(char *username, int flags) 121 { 122 int pstat, ac; 123 pid_t pid; 124 char *av[8]; 125 struct stat sb; 126 127 if (pw_lck == NULL) 128 return(-1); 129 130 /* A zero length passwd file is never ok */ 131 if (stat(pw_lck, &sb) == 0 && sb.st_size == 0) { 132 warnx("%s is zero length", pw_lck); 133 return (-1); 134 } 135 136 ac = 0; 137 av[ac++] = "pwd_mkdb"; 138 av[ac++] = "-d"; 139 av[ac++] = pw_dir; 140 if (flags & _PASSWORD_SECUREONLY) 141 av[ac++] = "-s"; 142 else if (!(flags & _PASSWORD_OMITV7)) 143 av[ac++] = "-p"; 144 if (username) { 145 av[ac++] = "-u"; 146 av[ac++] = username; 147 } 148 av[ac++] = pw_lck; 149 av[ac] = NULL; 150 151 pid = vfork(); 152 if (pid == -1) 153 return (-1); 154 if (pid == 0) { 155 if (pw_lck) 156 execv(_PATH_PWD_MKDB, av); 157 _exit(1); 158 } 159 pid = waitpid(pid, &pstat, 0); 160 if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) 161 return (-1); 162 return (0); 163 } 164 165 int 166 pw_abort(void) 167 { 168 return (pw_lck ? unlink(pw_lck) : -1); 169 } 170 171 /* Everything below this point is intended for the convenience of programs 172 * which allow a user to interactively edit the passwd file. Errors in the 173 * routines below will cause the process to abort. */ 174 175 static pid_t editpid = -1; 176 177 static void 178 pw_cont(int signo) 179 { 180 int save_errno = errno; 181 182 if (editpid != -1) 183 kill(editpid, signo); 184 errno = save_errno; 185 } 186 187 void 188 pw_init(void) 189 { 190 struct rlimit rlim; 191 192 /* Unlimited resource limits. */ 193 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 194 (void)setrlimit(RLIMIT_CPU, &rlim); 195 (void)setrlimit(RLIMIT_FSIZE, &rlim); 196 (void)setrlimit(RLIMIT_STACK, &rlim); 197 (void)setrlimit(RLIMIT_DATA, &rlim); 198 (void)setrlimit(RLIMIT_RSS, &rlim); 199 200 /* Don't drop core (not really necessary, but GP's). */ 201 rlim.rlim_cur = rlim.rlim_max = 0; 202 (void)setrlimit(RLIMIT_CORE, &rlim); 203 204 /* Turn off signals. */ 205 (void)signal(SIGALRM, SIG_IGN); 206 (void)signal(SIGHUP, SIG_IGN); 207 (void)signal(SIGINT, SIG_IGN); 208 (void)signal(SIGPIPE, SIG_IGN); 209 (void)signal(SIGQUIT, SIG_IGN); 210 (void)signal(SIGTERM, SIG_IGN); 211 (void)signal(SIGCONT, pw_cont); 212 213 if (!pw_lck) 214 pw_lck = pw_file(_PATH_MASTERPASSWD_LOCK); 215 } 216 217 void 218 pw_edit(int notsetuid, const char *filename) 219 { 220 int pstat; 221 char *p; 222 char * volatile editor; 223 char *argp[] = {"sh", "-c", NULL, NULL}; 224 225 if (!filename) { 226 filename = pw_lck; 227 if (!filename) 228 return; 229 } 230 231 if ((editor = getenv("EDITOR")) == NULL) 232 editor = _PATH_VI; 233 234 if (asprintf(&p, "%s %s", editor, filename) == -1) 235 return; 236 argp[2] = p; 237 238 switch (editpid = vfork()) { 239 case -1: /* error */ 240 free(p); 241 return; 242 case 0: /* child */ 243 if (notsetuid) { 244 setgid(getgid()); 245 setuid(getuid()); 246 } 247 execv(_PATH_BSHELL, argp); 248 _exit(127); 249 } 250 251 free(p); 252 for (;;) { 253 editpid = waitpid(editpid, (int *)&pstat, WUNTRACED); 254 if (editpid == -1) 255 pw_error(editor, 1, 1); 256 else if (WIFSTOPPED(pstat)) 257 raise(WSTOPSIG(pstat)); 258 else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) 259 break; 260 else 261 pw_error(editor, 1, 1); 262 } 263 editpid = -1; 264 } 265 266 void 267 pw_prompt(void) 268 { 269 int first, c; 270 271 (void)printf("re-edit the password file? [y]: "); 272 (void)fflush(stdout); 273 first = c = getchar(); 274 while (c != '\n' && c != EOF) 275 c = getchar(); 276 switch (first) { 277 case EOF: 278 putchar('\n'); 279 /* FALLTHROUGH */ 280 case 'n': 281 case 'N': 282 pw_error(NULL, 0, 0); 283 break; 284 } 285 } 286 287 static int 288 pw_equal(const struct passwd *pw1, const struct passwd *pw2) 289 { 290 return (strcmp(pw1->pw_name, pw2->pw_name) == 0 && 291 pw1->pw_uid == pw2->pw_uid && 292 pw1->pw_gid == pw2->pw_gid && 293 strcmp(pw1->pw_class, pw2->pw_class) == 0 && 294 pw1->pw_change == pw2->pw_change && 295 pw1->pw_expire == pw2->pw_expire && 296 strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 && 297 strcmp(pw1->pw_dir, pw2->pw_dir) == 0 && 298 strcmp(pw1->pw_shell, pw2->pw_shell) == 0); 299 } 300 301 void 302 pw_copy(int ffd, int tfd, const struct passwd *pw, const struct passwd *opw) 303 { 304 struct passwd tpw; 305 FILE *from, *to; 306 int done; 307 char *p, *ep, buf[8192]; 308 char *master = pw_file(_PATH_MASTERPASSWD); 309 310 if (!master) 311 pw_error(NULL, 0, 1); 312 if (!(from = fdopen(ffd, "r"))) 313 pw_error(master, 1, 1); 314 if (!(to = fdopen(tfd, "w"))) 315 pw_error(pw_lck ? pw_lck : NULL, pw_lck ? 1 : 0, 1); 316 317 for (done = 0; fgets(buf, (int)sizeof(buf), from);) { 318 if ((ep = strchr(buf, '\n')) == NULL) { 319 warnx("%s: line too long", master); 320 pw_error(NULL, 0, 1); 321 } 322 if (done) { 323 (void)fprintf(to, "%s", buf); 324 if (ferror(to)) 325 goto fail; 326 continue; 327 } 328 if (!(p = strchr(buf, ':'))) { 329 warnx("%s: corrupted entry", master); 330 pw_error(NULL, 0, 1); 331 } 332 *p = '\0'; 333 if (strcmp(buf, opw ? opw->pw_name : pw->pw_name)) { 334 *p = ':'; 335 (void)fprintf(to, "%s", buf); 336 if (ferror(to)) 337 goto fail; 338 continue; 339 } 340 if (opw != NULL) { 341 *p = ':'; 342 *ep = '\0'; 343 if (!pw_scan(buf, &tpw, NULL)) 344 pw_error(NULL, 0, 1); 345 if (!pw_equal(&tpw, opw)) { 346 warnx("%s: inconsistent entry", master); 347 pw_error(NULL, 0, 1); 348 } 349 } 350 (void)fprintf(to, "%s:%s:%u:%u:%s:%d:%d:%s:%s:%s\n", 351 pw->pw_name, pw->pw_passwd, (u_int)pw->pw_uid, 352 (u_int)pw->pw_gid, pw->pw_class, pw->pw_change, 353 pw->pw_expire, pw->pw_gecos, pw->pw_dir, 354 pw->pw_shell); 355 done = 1; 356 if (ferror(to)) 357 goto fail; 358 } 359 if (!done) 360 (void)fprintf(to, "%s:%s:%d:%d:%s:%d:%d:%s:%s:%s\n", 361 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, 362 pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos, 363 pw->pw_dir, pw->pw_shell); 364 365 if (ferror(to)) 366 fail: 367 pw_error(NULL, 0, 1); 368 free(master); 369 (void)fclose(to); 370 } 371 372 int 373 pw_scan(char *bp, struct passwd *pw, int *flags) 374 { 375 u_long id; 376 int root; 377 char *p, *sh, *p2; 378 379 if (flags != (int *)NULL) 380 *flags = 0; 381 382 if (!(p = strsep(&bp, ":")) || *p == '\0') /* login */ 383 goto fmt; 384 pw->pw_name = p; 385 root = !strcmp(pw->pw_name, "root"); 386 387 if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */ 388 goto fmt; 389 390 if (!(p = strsep(&bp, ":"))) /* uid */ 391 goto fmt; 392 id = strtoul(p, &p2, 10); 393 if (root && id) { 394 warnx("root uid should be 0"); 395 return (0); 396 } 397 if (*p2 != '\0') { 398 warnx("illegal uid field"); 399 return (0); 400 } 401 if (id > UID_MAX) { 402 /* errno is set to ERANGE by strtoul(3) */ 403 warnx("uid greater than %u", UID_MAX-1); 404 return (0); 405 } 406 pw->pw_uid = (uid_t)id; 407 if ((*p == '\0') && (flags != (int *)NULL)) 408 *flags |= _PASSWORD_NOUID; 409 410 if (!(p = strsep(&bp, ":"))) /* gid */ 411 goto fmt; 412 id = strtoul(p, &p2, 10); 413 if (*p2 != '\0') { 414 warnx("illegal gid field"); 415 return (0); 416 } 417 if (id > UID_MAX) { 418 /* errno is set to ERANGE by strtoul(3) */ 419 warnx("gid greater than %u", UID_MAX-1); 420 return (0); 421 } 422 pw->pw_gid = (gid_t)id; 423 if ((*p == '\0') && (flags != (int *)NULL)) 424 *flags |= _PASSWORD_NOGID; 425 426 pw->pw_class = strsep(&bp, ":"); /* class */ 427 if (!(p = strsep(&bp, ":"))) /* change */ 428 goto fmt; 429 pw->pw_change = atol(p); 430 if ((*p == '\0') && (flags != (int *)NULL)) 431 *flags |= _PASSWORD_NOCHG; 432 if (!(p = strsep(&bp, ":"))) /* expire */ 433 goto fmt; 434 pw->pw_expire = atol(p); 435 if ((*p == '\0') && (flags != (int *)NULL)) 436 *flags |= _PASSWORD_NOEXP; 437 pw->pw_gecos = strsep(&bp, ":"); /* gecos */ 438 pw->pw_dir = strsep(&bp, ":"); /* directory */ 439 if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */ 440 goto fmt; 441 442 p = pw->pw_shell; 443 if (root && *p) { /* empty == /bin/sh */ 444 for (setusershell();;) { 445 if (!(sh = getusershell())) { 446 warnx("warning, unknown root shell"); 447 break; 448 } 449 if (!strcmp(p, sh)) 450 break; 451 } 452 endusershell(); 453 } 454 455 if ((p = strsep(&bp, ":"))) { /* too many */ 456 fmt: warnx("corrupted entry"); 457 return (0); 458 } 459 460 return (1); 461 } 462 463 __dead void 464 pw_error(const char *name, int error, int eval) 465 { 466 char *master = pw_file(_PATH_MASTERPASSWD); 467 468 if (error) { 469 if (name) 470 warn("%s", name); 471 else 472 warn(NULL); 473 } 474 if (master) { 475 warnx("%s: unchanged", master); 476 free(master); 477 } 478 479 pw_abort(); 480 exit(eval); 481 } 482