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