1 /* $NetBSD: passwd.c,v 1.18 1999/09/20 04:48:11 lukem 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.18 1999/09/20 04:48:11 lukem 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 == 0) { 95 execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 96 _PATH_MASTERPASSWD_LOCK, NULL); 97 _exit(1); 98 } 99 pid = waitpid(pid, &pstat, 0); 100 if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) 101 return(-1); 102 return(0); 103 } 104 105 int 106 pw_abort() 107 { 108 return(unlink(_PATH_MASTERPASSWD_LOCK)); 109 } 110 111 /* Everything below this point is intended for the convenience of programs 112 * which allow a user to interactively edit the passwd file. Errors in the 113 * routines below will cause the process to abort. */ 114 115 static pid_t editpid = -1; 116 117 static void 118 pw_cont(sig) 119 int sig; 120 { 121 122 if (editpid != -1) 123 kill(editpid, sig); 124 } 125 126 void 127 pw_init() 128 { 129 struct rlimit rlim; 130 131 /* Unlimited resource limits. */ 132 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 133 (void)setrlimit(RLIMIT_CPU, &rlim); 134 (void)setrlimit(RLIMIT_FSIZE, &rlim); 135 (void)setrlimit(RLIMIT_STACK, &rlim); 136 (void)setrlimit(RLIMIT_DATA, &rlim); 137 (void)setrlimit(RLIMIT_RSS, &rlim); 138 139 /* Don't drop core (not really necessary, but GP's). */ 140 rlim.rlim_cur = rlim.rlim_max = 0; 141 (void)setrlimit(RLIMIT_CORE, &rlim); 142 143 /* Turn off signals. */ 144 (void)signal(SIGALRM, SIG_IGN); 145 (void)signal(SIGHUP, SIG_IGN); 146 (void)signal(SIGINT, SIG_IGN); 147 (void)signal(SIGPIPE, SIG_IGN); 148 (void)signal(SIGQUIT, SIG_IGN); 149 (void)signal(SIGTERM, SIG_IGN); 150 (void)signal(SIGCONT, pw_cont); 151 } 152 153 void 154 pw_edit(notsetuid, filename) 155 int notsetuid; 156 const char *filename; 157 { 158 int i, xargc, pstat; 159 char *p, *editor; 160 char **xargv; 161 #ifdef __GNUC__ 162 (void) &editor; 163 #endif 164 165 if (filename == NULL) 166 filename = _PATH_MASTERPASSWD_LOCK; 167 if ((editor = getenv("EDITOR")) == NULL) 168 editor = strdup(_PATH_VI); 169 else 170 editor = strdup(editor); 171 if ((p = strrchr(editor, '/')) != NULL) 172 ++p; 173 else 174 p = editor; 175 176 /* Scan editor string, count spaces, allocate arg vector. */ 177 for (i = 0, xargc = 0; p[i] != '\0'; i++) { 178 if (isspace(p[i])) { 179 while (isspace(p[i++])) 180 /* skip white space */ ; 181 if (p[i] == '\0') 182 break; 183 xargc++; 184 } 185 } 186 187 /* argv[0] + <xargc args> + filename + NULL */ 188 xargv = (char **)malloc(sizeof(char *) * (xargc + 3)); 189 if (xargv == NULL) 190 pw_error("malloc failed", 1, 1); 191 192 i = 0; 193 xargv[i++] = p; 194 for (; *p != '\0'; p++) { 195 if (isspace(*p)) { 196 while(isspace(*p)) 197 *p++ = '\0'; /* blast whitespace */ 198 if (*p == '\0') 199 break; 200 xargv[i++] = p; 201 } 202 } 203 204 /*LINTED*/ 205 xargv[i++] = (char *)filename; 206 xargv[i] = NULL; 207 208 if (!(editpid = vfork())) { 209 if (notsetuid) { 210 setgid(getgid()); 211 setuid(getuid()); 212 } 213 execvp(editor, xargv); 214 _exit(1); 215 } 216 for (;;) { 217 editpid = waitpid(editpid, (int *)&pstat, WUNTRACED); 218 if (editpid == -1) 219 pw_error(editor, 1, 1); 220 else if (WIFSTOPPED(pstat)) 221 raise(WSTOPSIG(pstat)); 222 else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) 223 break; 224 else 225 pw_error(editor, 1, 1); 226 } 227 editpid = -1; 228 free(editor); 229 free(xargv); 230 } 231 232 void 233 pw_prompt() 234 { 235 int c; 236 237 (void)printf("re-edit the password file? [y]: "); 238 (void)fflush(stdout); 239 c = getchar(); 240 if (c != EOF && c != '\n') 241 while (getchar() != '\n'); 242 if (c == 'n') 243 pw_error(NULL, 0, 0); 244 } 245 246 /* for use in pw_copy(). Compare a pw entry to a pw struct. */ 247 static int 248 pw_equal (buf, pw) 249 char *buf; 250 struct passwd *pw; 251 { 252 struct passwd buf_pw; 253 int len; 254 255 _DIAGASSERT(buf != NULL); 256 _DIAGASSERT(pw != NULL); 257 258 len = strlen (buf); 259 if (buf[len-1] == '\n') 260 buf[len-1] = '\0'; 261 if (!pw_scan(buf, &buf_pw, NULL)) 262 return 0; 263 return !strcmp(pw->pw_name, buf_pw.pw_name) 264 && pw->pw_uid == buf_pw.pw_uid 265 && pw->pw_gid == buf_pw.pw_gid 266 && !strcmp(pw->pw_class, buf_pw.pw_class) 267 && (long)pw->pw_change == (long)buf_pw.pw_change 268 && (long)pw->pw_expire == (long)buf_pw.pw_expire 269 && !strcmp(pw->pw_gecos, buf_pw.pw_gecos) 270 && !strcmp(pw->pw_dir, buf_pw.pw_dir) 271 && !strcmp(pw->pw_shell, buf_pw.pw_shell); 272 } 273 274 void 275 pw_copy(ffd, tfd, pw, old_pw) 276 int ffd, tfd; 277 struct passwd *pw, *old_pw; 278 { 279 FILE *from, *to; 280 int done; 281 char *p, buf[8192]; 282 283 _DIAGASSERT(pw != NULL); 284 /* old_pw may be NULL */ 285 286 if (!(from = fdopen(ffd, "r"))) 287 pw_error(_PATH_MASTERPASSWD, 1, 1); 288 if (!(to = fdopen(tfd, "w"))) 289 pw_error(_PATH_MASTERPASSWD_LOCK, 1, 1); 290 291 for (done = 0; fgets(buf, sizeof(buf), from);) { 292 if (!strchr(buf, '\n')) { 293 warnx("%s: line too long", _PATH_MASTERPASSWD); 294 pw_error(NULL, 0, 1); 295 } 296 if (done) { 297 (void)fprintf(to, "%s", buf); 298 if (ferror(to)) 299 goto err; 300 continue; 301 } 302 if (!(p = strchr(buf, ':'))) { 303 warnx("%s: corrupted entry", _PATH_MASTERPASSWD); 304 pw_error(NULL, 0, 1); 305 } 306 *p = '\0'; 307 if (strcmp(buf, pw->pw_name)) { 308 *p = ':'; 309 (void)fprintf(to, "%s", buf); 310 if (ferror(to)) 311 goto err; 312 continue; 313 } 314 *p = ':'; 315 if (old_pw && !pw_equal(buf, old_pw)) { 316 warnx("%s: entry inconsistent", 317 _PATH_MASTERPASSWD); 318 pw_error(NULL, 0, 1); 319 } 320 (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", 321 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, 322 pw->pw_class, (long)pw->pw_change, (long)pw->pw_expire, 323 pw->pw_gecos, pw->pw_dir, pw->pw_shell); 324 done = 1; 325 if (ferror(to)) 326 goto err; 327 } 328 /* Only append a new entry if real uid is root! */ 329 if (!done) { 330 if (getuid() == 0) 331 (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", 332 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, 333 pw->pw_class, (long)pw->pw_change, 334 (long)pw->pw_expire, pw->pw_gecos, pw->pw_dir, 335 pw->pw_shell); 336 else 337 warnx("%s: changes not made, no such entry", 338 _PATH_MASTERPASSWD); 339 } 340 341 if (ferror(to)) 342 err: pw_error(NULL, 1, 1); 343 (void)fclose(to); 344 } 345 346 void 347 pw_error(name, err, eval) 348 const char *name; 349 int err, eval; 350 { 351 352 if (err) 353 warn(name); 354 355 warnx("%s: unchanged", _PATH_MASTERPASSWD); 356 pw_abort(); 357 exit(eval); 358 } 359