1 /* $NetBSD: passwd.c,v 1.24 2000/07/11 06:07:27 itohy 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.24 2000/07/11 06:07:27 itohy 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 <assert.h> 48 #include <ctype.h> 49 #include <err.h> 50 #include <errno.h> 51 #include <fcntl.h> 52 #include <limits.h> 53 #include <paths.h> 54 #include <pwd.h> 55 #include <signal.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 #include <util.h> 61 62 static void pw_cont(int sig); 63 static int pw_equal(char *buf, struct passwd *old_pw); 64 static const char *pw_default(const char *option); 65 static int read_line(FILE *fp, char *line, int max); 66 static void trim_whitespace(char *line); 67 68 int 69 pw_lock(int retries) 70 { 71 int i, fd; 72 mode_t old_mode; 73 int oerrno; 74 75 /* Acquire the lock file. */ 76 old_mode = umask(0); 77 fd = open(_PATH_MASTERPASSWD_LOCK, O_WRONLY|O_CREAT|O_EXCL, 0600); 78 for (i = 0; i < retries && fd < 0 && errno == EEXIST; i++) { 79 sleep(1); 80 fd = open(_PATH_MASTERPASSWD_LOCK, O_WRONLY|O_CREAT|O_EXCL, 81 0600); 82 } 83 oerrno = errno; 84 (void)umask(old_mode); 85 errno = oerrno; 86 return(fd); 87 } 88 89 int 90 pw_mkdb(void) 91 { 92 int pstat; 93 pid_t pid; 94 95 pid = vfork(); 96 if (pid == -1) 97 return (-1); 98 99 if (pid == 0) { 100 execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 101 _PATH_MASTERPASSWD_LOCK, NULL); 102 _exit(1); 103 } 104 pid = waitpid(pid, &pstat, 0); 105 if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) 106 return(-1); 107 return(0); 108 } 109 110 int 111 pw_abort(void) 112 { 113 114 return(unlink(_PATH_MASTERPASSWD_LOCK)); 115 } 116 117 /* Everything below this point is intended for the convenience of programs 118 * which allow a user to interactively edit the passwd file. Errors in the 119 * routines below will cause the process to abort. */ 120 121 static pid_t editpid = -1; 122 123 static void 124 pw_cont(int sig) 125 { 126 127 if (editpid != -1) 128 kill(editpid, sig); 129 } 130 131 void 132 pw_init(void) 133 { 134 struct rlimit rlim; 135 136 /* Unlimited resource limits. */ 137 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 138 (void)setrlimit(RLIMIT_CPU, &rlim); 139 (void)setrlimit(RLIMIT_FSIZE, &rlim); 140 (void)setrlimit(RLIMIT_STACK, &rlim); 141 (void)setrlimit(RLIMIT_DATA, &rlim); 142 (void)setrlimit(RLIMIT_RSS, &rlim); 143 144 /* Don't drop core (not really necessary, but GP's). */ 145 rlim.rlim_cur = rlim.rlim_max = 0; 146 (void)setrlimit(RLIMIT_CORE, &rlim); 147 148 /* Turn off signals. */ 149 (void)signal(SIGALRM, SIG_IGN); 150 (void)signal(SIGHUP, SIG_IGN); 151 (void)signal(SIGINT, SIG_IGN); 152 (void)signal(SIGPIPE, SIG_IGN); 153 (void)signal(SIGQUIT, SIG_IGN); 154 (void)signal(SIGTERM, SIG_IGN); 155 (void)signal(SIGCONT, pw_cont); 156 } 157 158 void 159 pw_edit(int notsetuid, const char *filename) 160 { 161 int pstat; 162 char *p, *editor; 163 char *argp[] = { "sh", "-c", NULL, NULL }; 164 165 #ifdef __GNUC__ 166 (void) &editor; 167 #endif 168 169 if (filename == NULL) 170 filename = _PATH_MASTERPASSWD_LOCK; 171 172 if ((editor = getenv("EDITOR")) == NULL) 173 editor = _PATH_VI; 174 175 p = malloc(strlen(editor) + 1 + strlen(filename) + 1); 176 if (p == NULL) 177 return; 178 179 sprintf(p, "%s %s", editor, filename); 180 argp[2] = p; 181 182 switch(editpid = vfork()) { 183 case -1: 184 free(p); 185 return; 186 case 0: 187 if (notsetuid) { 188 setgid(getgid()); 189 setuid(getuid()); 190 } 191 execvp(_PATH_BSHELL, argp); 192 _exit(1); 193 } 194 195 free(p); 196 197 for (;;) { 198 editpid = waitpid(editpid, (int *)&pstat, WUNTRACED); 199 if (editpid == -1) 200 pw_error(editor, 1, 1); 201 else if (WIFSTOPPED(pstat)) 202 raise(WSTOPSIG(pstat)); 203 else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) 204 break; 205 else 206 pw_error(editor, 1, 1); 207 } 208 editpid = -1; 209 } 210 211 void 212 pw_prompt(void) 213 { 214 int c; 215 216 (void)printf("re-edit the password file? [y]: "); 217 (void)fflush(stdout); 218 c = getchar(); 219 if (c != EOF && c != '\n') 220 while (getchar() != '\n'); 221 if (c == 'n') 222 pw_error(NULL, 0, 0); 223 } 224 225 /* for use in pw_copy(). Compare a pw entry to a pw struct. */ 226 static int 227 pw_equal(char *buf, struct passwd *pw) 228 { 229 struct passwd buf_pw; 230 int len; 231 232 _DIAGASSERT(buf != NULL); 233 _DIAGASSERT(pw != NULL); 234 235 len = strlen (buf); 236 if (buf[len-1] == '\n') 237 buf[len-1] = '\0'; 238 if (!pw_scan(buf, &buf_pw, NULL)) 239 return 0; 240 return !strcmp(pw->pw_name, buf_pw.pw_name) 241 && pw->pw_uid == buf_pw.pw_uid 242 && pw->pw_gid == buf_pw.pw_gid 243 && !strcmp(pw->pw_class, buf_pw.pw_class) 244 && (long)pw->pw_change == (long)buf_pw.pw_change 245 && (long)pw->pw_expire == (long)buf_pw.pw_expire 246 && !strcmp(pw->pw_gecos, buf_pw.pw_gecos) 247 && !strcmp(pw->pw_dir, buf_pw.pw_dir) 248 && !strcmp(pw->pw_shell, buf_pw.pw_shell); 249 } 250 251 void 252 pw_copy(int ffd, int tfd, struct passwd *pw, struct passwd *old_pw) 253 { 254 FILE *from, *to; 255 int done; 256 char *p, buf[8192]; 257 258 _DIAGASSERT(pw != NULL); 259 /* old_pw may be NULL */ 260 261 if (!(from = fdopen(ffd, "r"))) 262 pw_error(_PATH_MASTERPASSWD, 1, 1); 263 if (!(to = fdopen(tfd, "w"))) 264 pw_error(_PATH_MASTERPASSWD_LOCK, 1, 1); 265 266 for (done = 0; fgets(buf, sizeof(buf), from);) { 267 if (!strchr(buf, '\n')) { 268 warnx("%s: line too long", _PATH_MASTERPASSWD); 269 pw_error(NULL, 0, 1); 270 } 271 if (done) { 272 (void)fprintf(to, "%s", buf); 273 if (ferror(to)) 274 goto err; 275 continue; 276 } 277 if (!(p = strchr(buf, ':'))) { 278 warnx("%s: corrupted entry", _PATH_MASTERPASSWD); 279 pw_error(NULL, 0, 1); 280 } 281 *p = '\0'; 282 if (strcmp(buf, pw->pw_name)) { 283 *p = ':'; 284 (void)fprintf(to, "%s", buf); 285 if (ferror(to)) 286 goto err; 287 continue; 288 } 289 *p = ':'; 290 if (old_pw && !pw_equal(buf, old_pw)) { 291 warnx("%s: entry inconsistent", 292 _PATH_MASTERPASSWD); 293 pw_error(NULL, 0, 1); 294 } 295 (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", 296 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, 297 pw->pw_class, (long)pw->pw_change, (long)pw->pw_expire, 298 pw->pw_gecos, pw->pw_dir, pw->pw_shell); 299 done = 1; 300 if (ferror(to)) 301 goto err; 302 } 303 /* Only append a new entry if real uid is root! */ 304 if (!done) { 305 if (getuid() == 0) 306 (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", 307 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, 308 pw->pw_class, (long)pw->pw_change, 309 (long)pw->pw_expire, pw->pw_gecos, pw->pw_dir, 310 pw->pw_shell); 311 else 312 warnx("%s: changes not made, no such entry", 313 _PATH_MASTERPASSWD); 314 } 315 316 if (ferror(to)) 317 err: pw_error(NULL, 1, 1); 318 (void)fclose(to); 319 } 320 321 void 322 pw_error(const char *name, int err, int eval) 323 { 324 325 if (err) 326 warn(name); 327 328 warnx("%s: unchanged", _PATH_MASTERPASSWD); 329 pw_abort(); 330 exit(eval); 331 } 332 333 /* Removes head and/or tail spaces. */ 334 static void 335 trim_whitespace(char *line) 336 { 337 char *p; 338 339 /* Remove leading spaces */ 340 p = line; 341 while (isspace((unsigned char) *p)) 342 p++; 343 memmove(line, p, strlen(p) + 1); 344 345 /* Remove trailing spaces */ 346 p = line + strlen(line) - 1; 347 while (isspace((unsigned char) *p)) 348 p--; 349 *(p + 1) = '\0'; 350 } 351 352 353 /* Get one line, remove spaces from front and tail */ 354 static int 355 read_line(FILE *fp, char *line, int max) 356 { 357 char *p; 358 359 /* Read one line of config */ 360 if (fgets(line, max, fp) == NULL) 361 return (0); 362 363 if ((p = strchr(line, '\n')) == NULL) { 364 warnx("line too long"); 365 return (0); 366 } 367 *p = '\0'; 368 369 /* Remove comments */ 370 if ((p = strchr(line, '#')) != NULL) 371 *p = '\0'; 372 373 trim_whitespace(line); 374 return (1); 375 } 376 377 static const char * 378 pw_default(const char *option) 379 { 380 static const char *options[][2] = { 381 { "localcipher", "old" }, 382 { "ypcipher", "old" }, 383 }; 384 int i; 385 386 for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) 387 if (strcmp(options[i][0], option) == 0) 388 return (options[i][1]); 389 390 return (NULL); 391 } 392 393 /* 394 * Retrieve password information from the /etc/passwd.conf file, at the 395 * moment this is only for choosing the cipher to use. It could easily be 396 * used for other authentication methods as well. 397 */ 398 void 399 pw_getconf(char *data, size_t max, const char *key, const char *option) 400 { 401 FILE *fp; 402 char line[LINE_MAX], *p, *p2; 403 static char result[LINE_MAX]; 404 int got, found; 405 const char *cp; 406 407 got = 0; 408 found = 0; 409 result[0] = '\0'; 410 411 if ((fp = fopen(_PATH_PASSWDCONF, "r")) == NULL) { 412 if ((cp = pw_default(option)) != NULL) 413 strlcpy(data, cp, max); 414 else 415 data[0] = '\0'; 416 return; 417 } 418 419 while (!found && (got || read_line(fp, line, LINE_MAX))) { 420 got = 0; 421 422 if (strncmp(key, line, strlen(key)) != 0 || 423 line[strlen(key)] != ':') 424 continue; 425 426 /* Now we found our specified key */ 427 while (read_line(fp, line, LINE_MAX)) { 428 /* Leaving key field */ 429 if (line[0] != '\0' && strchr(line + 1, ':') != NULL) { 430 got = 1; 431 break; 432 } 433 p2 = line; 434 if ((p = strsep(&p2, "=")) == NULL || p2 == NULL) 435 continue; 436 trim_whitespace(p); 437 438 if (!strncmp(p, option, strlen(option))) { 439 trim_whitespace(p2); 440 strcpy(result, p2); 441 found = 1; 442 break; 443 } 444 } 445 } 446 fclose(fp); 447 448 /* 449 * If we got no result and were looking for a default 450 * value, try hard coded defaults. 451 */ 452 453 if (strlen(result) == 0 && strcmp(key, "default") == 0 && 454 (cp = pw_default(option)) != NULL) 455 strlcpy(data, cp, max); 456 else 457 strlcpy(data, result, max); 458 } 459