passwd.c revision 1.7 1 /*
2 * Copyright (c) 1987, 1993, 1994, 1995
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #if defined(LIBC_SCCS) && !defined(lint)
35 static char rcsid[] = "$NetBSD: passwd.c,v 1.7 1997/06/20 06:17:27 mikel Exp $";
36 #endif /* LIBC_SCCS and not lint */
37
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/time.h>
41 #include <sys/resource.h>
42 #include <sys/wait.h>
43
44 #include <ctype.h>
45 #include <err.h>
46 #include <fcntl.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <pwd.h>
52 #include <errno.h>
53 #include <paths.h>
54 #include <signal.h>
55 #include <limits.h>
56 #include <util.h>
57
58 static void pw_cont __P((int sig));
59
60 int
61 pw_lock(retries)
62 int retries;
63 {
64 int i, fd;
65 mode_t old_mode;
66
67 /* Acquire the lock file. */
68 old_mode = umask(0);
69 fd = open(_PATH_MASTERPASSWD_LOCK, O_WRONLY|O_CREAT|O_EXCL, 0600);
70 for (i = 0; i < retries && fd < 0 && errno == EEXIST; i++) {
71 sleep(1);
72 fd = open(_PATH_MASTERPASSWD_LOCK, O_WRONLY|O_CREAT|O_EXCL,
73 0600);
74 }
75 umask(old_mode);
76 return(fd);
77 }
78
79 int
80 pw_mkdb()
81 {
82 int pstat;
83 pid_t pid;
84
85 pid = vfork();
86 if (pid == 0) {
87 execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p",
88 _PATH_MASTERPASSWD_LOCK, NULL);
89 exit(1);
90 }
91 pid = waitpid(pid, &pstat, 0);
92 if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0)
93 return(-1);
94 return(0);
95 }
96
97 int
98 pw_abort()
99 {
100 return(unlink(_PATH_MASTERPASSWD_LOCK));
101 }
102
103 /* Everything below this point is intended for the convenience of programs
104 * which allow a user to interactively edit the passwd file. Errors in the
105 * routines below will cause the process to abort. */
106
107 static pid_t editpid = -1;
108
109 static void
110 pw_cont(sig)
111 int sig;
112 {
113
114 if (editpid != -1)
115 kill(editpid, sig);
116 }
117
118 void
119 pw_init()
120 {
121 struct rlimit rlim;
122
123 /* Unlimited resource limits. */
124 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
125 (void)setrlimit(RLIMIT_CPU, &rlim);
126 (void)setrlimit(RLIMIT_FSIZE, &rlim);
127 (void)setrlimit(RLIMIT_STACK, &rlim);
128 (void)setrlimit(RLIMIT_DATA, &rlim);
129 (void)setrlimit(RLIMIT_RSS, &rlim);
130
131 /* Don't drop core (not really necessary, but GP's). */
132 rlim.rlim_cur = rlim.rlim_max = 0;
133 (void)setrlimit(RLIMIT_CORE, &rlim);
134
135 /* Turn off signals. */
136 (void)signal(SIGALRM, SIG_IGN);
137 (void)signal(SIGHUP, SIG_IGN);
138 (void)signal(SIGINT, SIG_IGN);
139 (void)signal(SIGPIPE, SIG_IGN);
140 (void)signal(SIGQUIT, SIG_IGN);
141 (void)signal(SIGTERM, SIG_IGN);
142 (void)signal(SIGCONT, pw_cont);
143 }
144
145 void
146 pw_edit(notsetuid, filename)
147 int notsetuid;
148 const char *filename;
149 {
150 int i, j, xargc, pstat;
151 char *p, *editor;
152 char **xargv;
153
154 if (filename == NULL)
155 filename = _PATH_MASTERPASSWD_LOCK;
156 if ((editor = getenv("EDITOR")) == NULL)
157 editor = strdup(_PATH_VI);
158 else
159 editor = strdup(editor);
160 if ((p = strrchr(editor, '/')))
161 ++p;
162 else
163 p = editor;
164
165 /* Scan editor string, count spaces, allocate arg vector. */
166 for (i = 0, xargc = 0; p[i] != '\0'; i++) {
167 if (isspace(p[i])) {
168 while (isspace(p[i++]))
169 /* skip white space */ ;
170 if (p[i] == '\0')
171 break;
172 xargc++;
173 }
174 }
175
176 /* argv[0] + <xargc args> + filename + NULL */
177 xargv = (char **)malloc(sizeof(char *) * (xargc + 3));
178 if (xargv == NULL)
179 pw_error("malloc failed", 1, 1);
180
181 i = 0;
182 xargv[i++] = p;
183 for (; *p != '\0'; p++) {
184 if (isspace(*p)) {
185 while(isspace(*p))
186 *p++ = '\0'; /* blast whitespace */
187 if (*p == '\0')
188 break;
189 xargv[i++] = p;
190 }
191 }
192
193 xargv[i++] = (char *)filename;
194 xargv[i] = NULL;
195
196 if (!(editpid = vfork())) {
197 if (notsetuid) {
198 setgid(getgid());
199 setuid(getuid());
200 }
201 execvp(editor, xargv);
202 _exit(1);
203 }
204 for (;;) {
205 editpid = waitpid(editpid, (int *)&pstat, WUNTRACED);
206 if (editpid == -1)
207 pw_error(editor, 1, 1);
208 else if (WIFSTOPPED(pstat))
209 raise(WSTOPSIG(pstat));
210 else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0)
211 break;
212 else
213 pw_error(editor, 1, 1);
214 }
215 editpid = -1;
216 free(editor);
217 free(xargv);
218 }
219
220 void
221 pw_prompt()
222 {
223 int c;
224
225 (void)printf("re-edit the password file? [y]: ");
226 (void)fflush(stdout);
227 c = getchar();
228 if (c != EOF && c != '\n')
229 while (getchar() != '\n');
230 if (c == 'n')
231 pw_error(NULL, 0, 0);
232 }
233
234 void
235 pw_copy(ffd, tfd, pw)
236 int ffd, tfd;
237 struct passwd *pw;
238 {
239 FILE *from, *to;
240 int done;
241 char *p, buf[8192];
242
243 if (!(from = fdopen(ffd, "r")))
244 pw_error(_PATH_MASTERPASSWD, 1, 1);
245 if (!(to = fdopen(tfd, "w")))
246 pw_error(_PATH_MASTERPASSWD_LOCK, 1, 1);
247
248 for (done = 0; fgets(buf, sizeof(buf), from);) {
249 if (!strchr(buf, '\n')) {
250 warnx("%s: line too long", _PATH_MASTERPASSWD);
251 pw_error(NULL, 0, 1);
252 }
253 if (done) {
254 (void)fprintf(to, "%s", buf);
255 if (ferror(to))
256 goto err;
257 continue;
258 }
259 if (!(p = strchr(buf, ':'))) {
260 warnx("%s: corrupted entry", _PATH_MASTERPASSWD);
261 pw_error(NULL, 0, 1);
262 }
263 *p = '\0';
264 if (strcmp(buf, pw->pw_name)) {
265 *p = ':';
266 (void)fprintf(to, "%s", buf);
267 if (ferror(to))
268 goto err;
269 continue;
270 }
271 (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
272 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
273 pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
274 pw->pw_dir, pw->pw_shell);
275 done = 1;
276 if (ferror(to))
277 goto err;
278 }
279 if (!done)
280 (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
281 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
282 pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
283 pw->pw_dir, pw->pw_shell);
284
285 if (ferror(to))
286 err: pw_error(NULL, 1, 1);
287 (void)fclose(to);
288 }
289
290 int
291 pw_scan(bp, pw, flags)
292 char *bp;
293 struct passwd *pw;
294 int *flags;
295 {
296 unsigned long id;
297 int root;
298 char *p, *sh, *ep;
299
300 if (flags != (int *)NULL)
301 *flags = 0;
302
303 if (!(pw->pw_name = strsep(&bp, ":"))) /* login */
304 goto fmt;
305 root = !strcmp(pw->pw_name, "root");
306
307 if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */
308 goto fmt;
309
310 if (!(p = strsep(&bp, ":"))) /* uid */
311 goto fmt;
312 id = strtoul(p, &ep, 10);
313 if (root && id) {
314 warnx("root uid should be 0");
315 return (0);
316 }
317 if (id > UID_MAX || *ep != '\0') {
318 warnx("invalid uid '%s'", p);
319 return (0);
320 }
321 pw->pw_uid = (uid_t)id;
322 if ((*p == '\0') && (flags != (int *)NULL))
323 *flags |= _PASSWORD_NOUID;
324
325 if (!(p = strsep(&bp, ":"))) /* gid */
326 goto fmt;
327 id = strtoul(p, &ep, 10);
328 if (id > GID_MAX || *ep != '\0') {
329 warnx("invalid gid '%s'", p);
330 return (0);
331 }
332 pw->pw_gid = (gid_t)id;
333 if ((*p == '\0') && (flags != (int *)NULL))
334 *flags |= _PASSWORD_NOGID;
335
336 pw->pw_class = strsep(&bp, ":"); /* class */
337 if (!(p = strsep(&bp, ":"))) /* change */
338 goto fmt;
339 pw->pw_change = atol(p);
340 if ((*p == '\0') && (flags != (int *)NULL))
341 *flags |= _PASSWORD_NOCHG;
342 if (!(p = strsep(&bp, ":"))) /* expire */
343 goto fmt;
344 pw->pw_expire = atol(p);
345 if ((*p == '\0') && (flags != (int *)NULL))
346 *flags |= _PASSWORD_NOEXP;
347 pw->pw_gecos = strsep(&bp, ":"); /* gecos */
348 pw->pw_dir = strsep(&bp, ":"); /* directory */
349 if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */
350 goto fmt;
351
352 p = pw->pw_shell;
353 if (root && *p) /* empty == /bin/sh */
354 for (setusershell();;) {
355 if (!(sh = getusershell())) {
356 warnx("warning, unknown root shell");
357 break;
358 }
359 if (!strcmp(p, sh))
360 break;
361 }
362
363 if ((p = strsep(&bp, ":"))) { /* too many */
364 fmt: warnx("corrupted entry");
365 return (0);
366 }
367
368 return (1);
369 }
370
371 void
372 pw_error(name, err, eval)
373 const char *name;
374 int err, eval;
375 {
376 if (err)
377 warn(name);
378
379 warnx("%s: unchanged", _PATH_MASTERPASSWD);
380 pw_abort();
381 exit(eval);
382 }
383
384