1 /* $NetBSD: passwd.c,v 1.19 1999/12/03 16:23:58 mjl 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.19 1999/12/03 16:23:58 mjl 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 __P((int sig)); 63 static int pw_equal __P((char *buf, struct passwd *old_pw)); 64 65 int 66 pw_lock(retries) 67 int retries; 68 { 69 int i, fd; 70 mode_t old_mode; 71 int oerrno; 72 73 /* Acquire the lock file. */ 74 old_mode = umask(0); 75 fd = open(_PATH_MASTERPASSWD_LOCK, O_WRONLY|O_CREAT|O_EXCL, 0600); 76 for (i = 0; i < retries && fd < 0 && errno == EEXIST; i++) { 77 sleep(1); 78 fd = open(_PATH_MASTERPASSWD_LOCK, O_WRONLY|O_CREAT|O_EXCL, 79 0600); 80 } 81 oerrno = errno; 82 (void)umask(old_mode); 83 errno = oerrno; 84 return(fd); 85 } 86 87 int 88 pw_mkdb() 89 { 90 int pstat; 91 pid_t pid; 92 93 pid = vfork(); 94 if (pid == -1) 95 return (-1); 96 97 if (pid == 0) { 98 execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 99 _PATH_MASTERPASSWD_LOCK, NULL); 100 _exit(1); 101 } 102 pid = waitpid(pid, &pstat, 0); 103 if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) 104 return(-1); 105 return(0); 106 } 107 108 int 109 pw_abort() 110 { 111 return(unlink(_PATH_MASTERPASSWD_LOCK)); 112 } 113 114 /* Everything below this point is intended for the convenience of programs 115 * which allow a user to interactively edit the passwd file. Errors in the 116 * routines below will cause the process to abort. */ 117 118 static pid_t editpid = -1; 119 120 static void 121 pw_cont(sig) 122 int sig; 123 { 124 125 if (editpid != -1) 126 kill(editpid, sig); 127 } 128 129 void 130 pw_init() 131 { 132 struct rlimit rlim; 133 134 /* Unlimited resource limits. */ 135 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 136 (void)setrlimit(RLIMIT_CPU, &rlim); 137 (void)setrlimit(RLIMIT_FSIZE, &rlim); 138 (void)setrlimit(RLIMIT_STACK, &rlim); 139 (void)setrlimit(RLIMIT_DATA, &rlim); 140 (void)setrlimit(RLIMIT_RSS, &rlim); 141 142 /* Don't drop core (not really necessary, but GP's). */ 143 rlim.rlim_cur = rlim.rlim_max = 0; 144 (void)setrlimit(RLIMIT_CORE, &rlim); 145 146 /* Turn off signals. */ 147 (void)signal(SIGALRM, SIG_IGN); 148 (void)signal(SIGHUP, SIG_IGN); 149 (void)signal(SIGINT, SIG_IGN); 150 (void)signal(SIGPIPE, SIG_IGN); 151 (void)signal(SIGQUIT, SIG_IGN); 152 (void)signal(SIGTERM, SIG_IGN); 153 (void)signal(SIGCONT, pw_cont); 154 } 155 156 void 157 pw_edit(notsetuid, filename) 158 int notsetuid; 159 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() 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 (buf, pw) 228 char *buf; 229 struct passwd *pw; 230 { 231 struct passwd buf_pw; 232 int len; 233 234 _DIAGASSERT(buf != NULL); 235 _DIAGASSERT(pw != NULL); 236 237 len = strlen (buf); 238 if (buf[len-1] == '\n') 239 buf[len-1] = '\0'; 240 if (!pw_scan(buf, &buf_pw, NULL)) 241 return 0; 242 return !strcmp(pw->pw_name, buf_pw.pw_name) 243 && pw->pw_uid == buf_pw.pw_uid 244 && pw->pw_gid == buf_pw.pw_gid 245 && !strcmp(pw->pw_class, buf_pw.pw_class) 246 && (long)pw->pw_change == (long)buf_pw.pw_change 247 && (long)pw->pw_expire == (long)buf_pw.pw_expire 248 && !strcmp(pw->pw_gecos, buf_pw.pw_gecos) 249 && !strcmp(pw->pw_dir, buf_pw.pw_dir) 250 && !strcmp(pw->pw_shell, buf_pw.pw_shell); 251 } 252 253 void 254 pw_copy(ffd, tfd, pw, old_pw) 255 int ffd, tfd; 256 struct passwd *pw, *old_pw; 257 { 258 FILE *from, *to; 259 int done; 260 char *p, buf[8192]; 261 262 _DIAGASSERT(pw != NULL); 263 /* old_pw may be NULL */ 264 265 if (!(from = fdopen(ffd, "r"))) 266 pw_error(_PATH_MASTERPASSWD, 1, 1); 267 if (!(to = fdopen(tfd, "w"))) 268 pw_error(_PATH_MASTERPASSWD_LOCK, 1, 1); 269 270 for (done = 0; fgets(buf, sizeof(buf), from);) { 271 if (!strchr(buf, '\n')) { 272 warnx("%s: line too long", _PATH_MASTERPASSWD); 273 pw_error(NULL, 0, 1); 274 } 275 if (done) { 276 (void)fprintf(to, "%s", buf); 277 if (ferror(to)) 278 goto err; 279 continue; 280 } 281 if (!(p = strchr(buf, ':'))) { 282 warnx("%s: corrupted entry", _PATH_MASTERPASSWD); 283 pw_error(NULL, 0, 1); 284 } 285 *p = '\0'; 286 if (strcmp(buf, pw->pw_name)) { 287 *p = ':'; 288 (void)fprintf(to, "%s", buf); 289 if (ferror(to)) 290 goto err; 291 continue; 292 } 293 *p = ':'; 294 if (old_pw && !pw_equal(buf, old_pw)) { 295 warnx("%s: entry inconsistent", 296 _PATH_MASTERPASSWD); 297 pw_error(NULL, 0, 1); 298 } 299 (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", 300 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, 301 pw->pw_class, (long)pw->pw_change, (long)pw->pw_expire, 302 pw->pw_gecos, pw->pw_dir, pw->pw_shell); 303 done = 1; 304 if (ferror(to)) 305 goto err; 306 } 307 /* Only append a new entry if real uid is root! */ 308 if (!done) { 309 if (getuid() == 0) 310 (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", 311 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, 312 pw->pw_class, (long)pw->pw_change, 313 (long)pw->pw_expire, pw->pw_gecos, pw->pw_dir, 314 pw->pw_shell); 315 else 316 warnx("%s: changes not made, no such entry", 317 _PATH_MASTERPASSWD); 318 } 319 320 if (ferror(to)) 321 err: pw_error(NULL, 1, 1); 322 (void)fclose(to); 323 } 324 325 void 326 pw_error(name, err, eval) 327 const char *name; 328 int err, eval; 329 { 330 331 if (err) 332 warn(name); 333 334 warnx("%s: unchanged", _PATH_MASTERPASSWD); 335 pw_abort(); 336 exit(eval); 337 } 338