1 /* $OpenBSD: passwd.c,v 1.53 2014/08/15 03:51:40 guenther 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 97 if (!pw_lck) { 98 errno = EINVAL; 99 return (-1); 100 } 101 /* Acquire the lock file. */ 102 old_mode = umask(0); 103 fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600); 104 for (i = 0; i < retries && fd < 0 && errno == EEXIST; i++) { 105 sleep(1); 106 fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600); 107 } 108 (void) umask(old_mode); 109 return (fd); 110 } 111 112 int 113 pw_mkdb(char *username, int flags) 114 { 115 int pstat, ac; 116 pid_t pid; 117 char *av[8]; 118 struct stat sb; 119 120 if (pw_lck == NULL) 121 return(-1); 122 123 /* A zero length passwd file is never ok */ 124 if (stat(pw_lck, &sb) == 0 && sb.st_size == 0) { 125 warnx("%s is zero length", pw_lck); 126 return (-1); 127 } 128 129 ac = 0; 130 av[ac++] = "pwd_mkdb"; 131 av[ac++] = "-d"; 132 av[ac++] = pw_dir; 133 if (flags & _PASSWORD_SECUREONLY) 134 av[ac++] = "-s"; 135 else if (!(flags & _PASSWORD_OMITV7)) 136 av[ac++] = "-p"; 137 if (username) { 138 av[ac++] = "-u"; 139 av[ac++] = username; 140 } 141 av[ac++] = pw_lck; 142 av[ac] = NULL; 143 144 pid = vfork(); 145 if (pid == -1) 146 return (-1); 147 if (pid == 0) { 148 if (pw_lck) 149 execv(_PATH_PWD_MKDB, av); 150 _exit(1); 151 } 152 pid = waitpid(pid, &pstat, 0); 153 if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) 154 return (-1); 155 return (0); 156 } 157 158 int 159 pw_abort(void) 160 { 161 return (pw_lck ? unlink(pw_lck) : -1); 162 } 163 164 /* Everything below this point is intended for the convenience of programs 165 * which allow a user to interactively edit the passwd file. Errors in the 166 * routines below will cause the process to abort. */ 167 168 static pid_t editpid = -1; 169 170 static void 171 pw_cont(int signo) 172 { 173 int save_errno = errno; 174 175 if (editpid != -1) 176 kill(editpid, signo); 177 errno = save_errno; 178 } 179 180 void 181 pw_init(void) 182 { 183 struct rlimit rlim; 184 185 /* Unlimited resource limits. */ 186 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 187 (void)setrlimit(RLIMIT_CPU, &rlim); 188 (void)setrlimit(RLIMIT_FSIZE, &rlim); 189 (void)setrlimit(RLIMIT_STACK, &rlim); 190 (void)setrlimit(RLIMIT_DATA, &rlim); 191 (void)setrlimit(RLIMIT_RSS, &rlim); 192 193 /* Don't drop core (not really necessary, but GP's). */ 194 rlim.rlim_cur = rlim.rlim_max = 0; 195 (void)setrlimit(RLIMIT_CORE, &rlim); 196 197 /* Turn off signals. */ 198 (void)signal(SIGALRM, SIG_IGN); 199 (void)signal(SIGHUP, SIG_IGN); 200 (void)signal(SIGINT, SIG_IGN); 201 (void)signal(SIGPIPE, SIG_IGN); 202 (void)signal(SIGQUIT, SIG_IGN); 203 (void)signal(SIGTERM, SIG_IGN); 204 (void)signal(SIGCONT, pw_cont); 205 206 if (!pw_lck) 207 pw_lck = pw_file(_PATH_MASTERPASSWD_LOCK); 208 } 209 210 void 211 pw_edit(int notsetuid, const char *filename) 212 { 213 int pstat; 214 char *p; 215 char * volatile editor; 216 char *argp[] = {"sh", "-c", NULL, NULL}; 217 218 if (!filename) { 219 filename = pw_lck; 220 if (!filename) 221 return; 222 } 223 224 if ((editor = getenv("EDITOR")) == NULL) 225 editor = _PATH_VI; 226 227 if (asprintf(&p, "%s %s", editor, filename) == -1) 228 return; 229 argp[2] = p; 230 231 switch (editpid = vfork()) { 232 case -1: /* error */ 233 free(p); 234 return; 235 case 0: /* child */ 236 if (notsetuid) { 237 setgid(getgid()); 238 setuid(getuid()); 239 } 240 execv(_PATH_BSHELL, argp); 241 _exit(127); 242 } 243 244 free(p); 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 } 258 259 void 260 pw_prompt(void) 261 { 262 int first, c; 263 264 (void)printf("re-edit the password file? [y]: "); 265 (void)fflush(stdout); 266 first = c = getchar(); 267 while (c != '\n' && c != EOF) 268 c = getchar(); 269 switch (first) { 270 case EOF: 271 putchar('\n'); 272 /* FALLTHROUGH */ 273 case 'n': 274 case 'N': 275 pw_error(NULL, 0, 0); 276 break; 277 } 278 } 279 280 static int 281 pw_equal(const struct passwd *pw1, const struct passwd *pw2) 282 { 283 return (strcmp(pw1->pw_name, pw2->pw_name) == 0 && 284 pw1->pw_uid == pw2->pw_uid && 285 pw1->pw_gid == pw2->pw_gid && 286 strcmp(pw1->pw_class, pw2->pw_class) == 0 && 287 pw1->pw_change == pw2->pw_change && 288 pw1->pw_expire == pw2->pw_expire && 289 strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 && 290 strcmp(pw1->pw_dir, pw2->pw_dir) == 0 && 291 strcmp(pw1->pw_shell, pw2->pw_shell) == 0); 292 } 293 294 void 295 pw_copy(int ffd, int tfd, const struct passwd *pw, const struct passwd *opw) 296 { 297 struct passwd tpw; 298 FILE *from, *to; 299 int done; 300 char *p, *ep, buf[8192]; 301 char *master = pw_file(_PATH_MASTERPASSWD); 302 303 if (!master) 304 pw_error(NULL, 0, 1); 305 if (!(from = fdopen(ffd, "r"))) 306 pw_error(master, 1, 1); 307 if (!(to = fdopen(tfd, "w"))) 308 pw_error(pw_lck ? pw_lck : NULL, pw_lck ? 1 : 0, 1); 309 310 for (done = 0; fgets(buf, (int)sizeof(buf), from);) { 311 if ((ep = strchr(buf, '\n')) == NULL) { 312 warnx("%s: line too long", master); 313 pw_error(NULL, 0, 1); 314 } 315 if (done) { 316 (void)fprintf(to, "%s", buf); 317 if (ferror(to)) 318 goto fail; 319 continue; 320 } 321 if (!(p = strchr(buf, ':'))) { 322 warnx("%s: corrupted entry", master); 323 pw_error(NULL, 0, 1); 324 } 325 *p = '\0'; 326 if (strcmp(buf, opw ? opw->pw_name : pw->pw_name)) { 327 *p = ':'; 328 (void)fprintf(to, "%s", buf); 329 if (ferror(to)) 330 goto fail; 331 continue; 332 } 333 if (opw != NULL) { 334 *p = ':'; 335 *ep = '\0'; 336 if (!pw_scan(buf, &tpw, NULL)) 337 pw_error(NULL, 0, 1); 338 if (!pw_equal(&tpw, opw)) { 339 warnx("%s: inconsistent entry", master); 340 pw_error(NULL, 0, 1); 341 } 342 } 343 (void)fprintf(to, "%s:%s:%u:%u:%s:%lld:%lld:%s:%s:%s\n", 344 pw->pw_name, pw->pw_passwd, (u_int)pw->pw_uid, 345 (u_int)pw->pw_gid, pw->pw_class, (long long)pw->pw_change, 346 (long long)pw->pw_expire, pw->pw_gecos, pw->pw_dir, 347 pw->pw_shell); 348 done = 1; 349 if (ferror(to)) 350 goto fail; 351 } 352 if (!done) 353 (void)fprintf(to, "%s:%s:%u:%u:%s:%lld:%lld:%s:%s:%s\n", 354 pw->pw_name, pw->pw_passwd, (u_int)pw->pw_uid, 355 (u_int)pw->pw_gid, pw->pw_class, (long long)pw->pw_change, 356 (long long)pw->pw_expire, pw->pw_gecos, 357 pw->pw_dir, pw->pw_shell); 358 359 if (ferror(to)) 360 fail: 361 pw_error(NULL, 0, 1); 362 free(master); 363 (void)fclose(to); 364 } 365 366 int 367 pw_scan(char *bp, struct passwd *pw, int *flags) 368 { 369 u_long id; 370 int root; 371 char *p, *sh, *p2; 372 373 if (flags != (int *)NULL) 374 *flags = 0; 375 376 if (!(p = strsep(&bp, ":")) || *p == '\0') /* login */ 377 goto fmt; 378 pw->pw_name = p; 379 root = !strcmp(pw->pw_name, "root"); 380 381 if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */ 382 goto fmt; 383 384 if (!(p = strsep(&bp, ":"))) /* uid */ 385 goto fmt; 386 id = strtoul(p, &p2, 10); 387 if (root && id) { 388 warnx("root uid should be 0"); 389 return (0); 390 } 391 if (*p2 != '\0') { 392 warnx("illegal uid field"); 393 return (0); 394 } 395 if (id > UID_MAX) { 396 /* errno is set to ERANGE by strtoul(3) */ 397 warnx("uid greater than %u", UID_MAX-1); 398 return (0); 399 } 400 pw->pw_uid = (uid_t)id; 401 if ((*p == '\0') && (flags != (int *)NULL)) 402 *flags |= _PASSWORD_NOUID; 403 404 if (!(p = strsep(&bp, ":"))) /* gid */ 405 goto fmt; 406 id = strtoul(p, &p2, 10); 407 if (*p2 != '\0') { 408 warnx("illegal gid field"); 409 return (0); 410 } 411 if (id > UID_MAX) { 412 /* errno is set to ERANGE by strtoul(3) */ 413 warnx("gid greater than %u", UID_MAX-1); 414 return (0); 415 } 416 pw->pw_gid = (gid_t)id; 417 if ((*p == '\0') && (flags != (int *)NULL)) 418 *flags |= _PASSWORD_NOGID; 419 420 pw->pw_class = strsep(&bp, ":"); /* class */ 421 if (!(p = strsep(&bp, ":"))) /* change */ 422 goto fmt; 423 pw->pw_change = atoll(p); 424 if ((*p == '\0') && (flags != (int *)NULL)) 425 *flags |= _PASSWORD_NOCHG; 426 if (!(p = strsep(&bp, ":"))) /* expire */ 427 goto fmt; 428 pw->pw_expire = atoll(p); 429 if ((*p == '\0') && (flags != (int *)NULL)) 430 *flags |= _PASSWORD_NOEXP; 431 pw->pw_gecos = strsep(&bp, ":"); /* gecos */ 432 pw->pw_dir = strsep(&bp, ":"); /* directory */ 433 if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */ 434 goto fmt; 435 436 p = pw->pw_shell; 437 if (root && *p) { /* empty == /bin/sh */ 438 for (setusershell();;) { 439 if (!(sh = getusershell())) { 440 warnx("warning, unknown root shell"); 441 break; 442 } 443 if (!strcmp(p, sh)) 444 break; 445 } 446 endusershell(); 447 } 448 449 if ((p = strsep(&bp, ":"))) { /* too many */ 450 fmt: warnx("corrupted entry"); 451 return (0); 452 } 453 454 return (1); 455 } 456 457 __dead void 458 pw_error(const char *name, int error, int eval) 459 { 460 char *master = pw_file(_PATH_MASTERPASSWD); 461 462 if (error) { 463 if (name) 464 warn("%s", name); 465 else 466 warn(NULL); 467 } 468 if (master) { 469 warnx("%s: unchanged", master); 470 free(master); 471 } 472 473 pw_abort(); 474 exit(eval); 475 } 476