1 /** $MirOS: src/lib/libutil/passwd.c,v 1.2 2005/03/06 20:29:37 tg Exp $ */
2 /* $OpenBSD: passwd.c,v 1.45 2004/11/04 18:44:59 millert Exp $ */
3
4 /*
5 * Copyright (c) 1987, 1993, 1994, 1995
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/time.h>
36 #include <sys/resource.h>
37 #include <sys/wait.h>
38
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <pwd.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <paths.h>
49 #include <signal.h>
50 #include <limits.h>
51
52 #include "util.h"
53
54 __RCSID("$MirOS: src/lib/libutil/passwd.c,v 1.2 2005/03/06 20:29:37 tg Exp $");
55
56 static char pw_defdir[] = "/etc";
57 static char *pw_dir = pw_defdir;
58 static char *pw_lck;
59
60 char *
pw_file(const char * nm)61 pw_file(const char *nm)
62 {
63 const char *p = strrchr(nm, '/');
64 char *new_nm;
65
66 if (p)
67 p++;
68 else
69 p = nm;
70
71 if (asprintf(&new_nm, "%s/%s", pw_dir, p) == -1)
72 return NULL;
73 return new_nm;
74 }
75
76 void
pw_setdir(const char * dir)77 pw_setdir(const char *dir)
78 {
79 char *p;
80
81 if (strcmp (dir, pw_dir) == 0)
82 return;
83 if (pw_dir != pw_defdir)
84 free(pw_dir);
85 pw_dir = strdup(dir);
86 if (pw_lck) {
87 p = pw_file(pw_lck);
88 free(pw_lck);
89 pw_lck = p;
90 }
91 }
92
93
94 int
pw_lock(int retries)95 pw_lock(int retries)
96 {
97 int i, fd;
98 mode_t old_mode;
99 int save_errno;
100
101 if (!pw_lck)
102 return (-1);
103 /* Acquire the lock file. */
104 old_mode = umask(0);
105 fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL, 0600);
106 for (i = 0; i < retries && fd < 0 && errno == EEXIST; i++) {
107 sleep(1);
108 fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL, 0600);
109 }
110 save_errno = errno;
111 if (fd != -1 && fcntl(fd, F_SETFD, 1) == -1) {
112 close(fd);
113 fd = -1;
114 }
115 (void) umask(old_mode);
116 errno = save_errno;
117 return (fd);
118 }
119
120 int
pw_mkdb(char * username,int flags)121 pw_mkdb(char *username, int flags)
122 {
123 int pstat, ac;
124 pid_t pid;
125 char *av[8];
126 struct stat sb;
127
128 if (pw_lck == NULL)
129 return(-1);
130
131 /* A zero length passwd file is never ok */
132 if (stat(pw_lck, &sb) == 0 && sb.st_size == 0) {
133 warnx("%s is zero length", pw_lck);
134 return (-1);
135 }
136
137 ac = 0;
138 av[ac++] = "pwd_mkdb";
139 av[ac++] = "-d";
140 av[ac++] = pw_dir;
141 if (flags & _PASSWORD_SECUREONLY)
142 av[ac++] = "-s";
143 else if (!(flags & _PASSWORD_OMITV7))
144 av[ac++] = "-p";
145 if (username) {
146 av[ac++] = "-u";
147 av[ac++] = username;
148 }
149 av[ac++] = pw_lck;
150 av[ac] = NULL;
151
152 pid = vfork();
153 if (pid == -1)
154 return (-1);
155 if (pid == 0) {
156 if (pw_lck)
157 execv(_PATH_PWD_MKDB, av);
158 _exit(1);
159 }
160 pid = waitpid(pid, &pstat, 0);
161 if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0)
162 return (-1);
163 return (0);
164 }
165
166 int
pw_abort(void)167 pw_abort(void)
168 {
169 return (pw_lck ? unlink(pw_lck) : -1);
170 }
171
172 /* Everything below this point is intended for the convenience of programs
173 * which allow a user to interactively edit the passwd file. Errors in the
174 * routines below will cause the process to abort. */
175
176 static pid_t editpid = -1;
177
178 static void
pw_cont(int signo)179 pw_cont(int signo)
180 {
181 int save_errno = errno;
182
183 if (editpid != -1)
184 kill(editpid, signo);
185 errno = save_errno;
186 }
187
188 void
pw_init(void)189 pw_init(void)
190 {
191 struct rlimit rlim;
192
193 /* Unlimited resource limits. */
194 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
195 (void)setrlimit(RLIMIT_CPU, &rlim);
196 (void)setrlimit(RLIMIT_TIME, &rlim);
197 (void)setrlimit(RLIMIT_FSIZE, &rlim);
198 (void)setrlimit(RLIMIT_STACK, &rlim);
199 (void)setrlimit(RLIMIT_DATA, &rlim);
200 (void)setrlimit(RLIMIT_RSS, &rlim);
201
202 /* Don't drop core (not really necessary, but GP's). */
203 rlim.rlim_cur = rlim.rlim_max = 0;
204 (void)setrlimit(RLIMIT_CORE, &rlim);
205
206 /* Turn off signals. */
207 (void)signal(SIGALRM, SIG_IGN);
208 (void)signal(SIGHUP, SIG_IGN);
209 (void)signal(SIGINT, SIG_IGN);
210 (void)signal(SIGPIPE, SIG_IGN);
211 (void)signal(SIGQUIT, SIG_IGN);
212 (void)signal(SIGTERM, SIG_IGN);
213 (void)signal(SIGCONT, pw_cont);
214
215 if (!pw_lck)
216 pw_lck = pw_file(_PATH_MASTERPASSWD_LOCK);
217 }
218
219 void
pw_edit(int notsetuid,const char * filename)220 pw_edit(int notsetuid, const char *filename)
221 {
222 int pstat;
223 char *p;
224 char * volatile editor;
225 char *argp[] = {"sh", "-c", NULL, NULL};
226
227 if (!filename) {
228 filename = pw_lck;
229 if (!filename)
230 return;
231 }
232
233 if ((editor = getenv("EDITOR")) == NULL)
234 editor = _PATH_VI;
235
236 if (asprintf(&p, "%s %s", editor, filename) == -1)
237 return;
238 argp[2] = p;
239
240 switch (editpid = vfork()) {
241 case -1: /* error */
242 free(p);
243 return;
244 case 0: /* child */
245 if (notsetuid) {
246 setgid(getgid());
247 setuid(getuid());
248 }
249 execv(_PATH_BSHELL, argp);
250 _exit(127);
251 }
252
253 free(p);
254 for (;;) {
255 editpid = waitpid(editpid, (int *)&pstat, WUNTRACED);
256 if (editpid == -1)
257 pw_error(editor, 1, 1);
258 else if (WIFSTOPPED(pstat))
259 raise(WSTOPSIG(pstat));
260 else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0)
261 break;
262 else
263 pw_error(editor, 1, 1);
264 }
265 editpid = -1;
266 }
267
268 void
pw_prompt(void)269 pw_prompt(void)
270 {
271 int first, c;
272
273 (void)printf("re-edit the password file? [y]: ");
274 (void)fflush(stdout);
275 first = c = getchar();
276 while (c != '\n' && c != EOF)
277 c = getchar();
278 switch (first) {
279 case EOF:
280 putchar('\n');
281 /* FALLTHROUGH */
282 case 'n':
283 case 'N':
284 pw_error(NULL, 0, 0);
285 break;
286 }
287 }
288
289 static int
pw_equal(const struct passwd * pw1,const struct passwd * pw2)290 pw_equal(const struct passwd *pw1, const struct passwd *pw2)
291 {
292 return (strcmp(pw1->pw_name, pw2->pw_name) == 0 &&
293 pw1->pw_uid == pw2->pw_uid &&
294 pw1->pw_gid == pw2->pw_gid &&
295 strcmp(pw1->pw_class, pw2->pw_class) == 0 &&
296 pw1->pw_change == pw2->pw_change &&
297 pw1->pw_expire == pw2->pw_expire &&
298 strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 &&
299 strcmp(pw1->pw_dir, pw2->pw_dir) == 0 &&
300 strcmp(pw1->pw_shell, pw2->pw_shell) == 0);
301 }
302
303 void
pw_copy(int ffd,int tfd,const struct passwd * pw,const struct passwd * opw)304 pw_copy(int ffd, int tfd, const struct passwd *pw, const struct passwd *opw)
305 {
306 struct passwd tpw;
307 FILE *from, *to;
308 int done;
309 char *p, *ep, buf[8192];
310 char *master = pw_file(_PATH_MASTERPASSWD);
311
312 if (!master)
313 pw_error(NULL, 0, 1);
314 if (!(from = fdopen(ffd, "r")))
315 pw_error(master, 1, 1);
316 if (!(to = fdopen(tfd, "w")))
317 pw_error(pw_lck ? pw_lck : NULL, pw_lck ? 1 : 0, 1);
318
319 for (done = 0; fgets(buf, sizeof(buf), from);) {
320 if ((ep = strchr(buf, '\n')) == NULL) {
321 warnx("%s: line too long", master);
322 pw_error(NULL, 0, 1);
323 }
324 if (done) {
325 (void)fprintf(to, "%s", buf);
326 if (ferror(to))
327 goto err;
328 continue;
329 }
330 if (!(p = strchr(buf, ':'))) {
331 warnx("%s: corrupted entry", master);
332 pw_error(NULL, 0, 1);
333 }
334 *p = '\0';
335 if (strcmp(buf, opw ? opw->pw_name : pw->pw_name)) {
336 *p = ':';
337 (void)fprintf(to, "%s", buf);
338 if (ferror(to))
339 goto err;
340 continue;
341 }
342 if (opw != NULL) {
343 *p = ':';
344 *ep = '\0';
345 if (!pw_scan(buf, &tpw, NULL))
346 pw_error(NULL, 0, 1);
347 if (!pw_equal(&tpw, opw)) {
348 warnx("%s: inconsistent entry", master);
349 pw_error(NULL, 0, 1);
350 }
351 }
352 (void)fprintf(to, "%s:%s:%u:%u:%s:%lld:%lld:%s:%s:%s\n",
353 pw->pw_name, pw->pw_passwd, (u_int)pw->pw_uid,
354 (u_int)pw->pw_gid, pw->pw_class, (int64_t)pw->pw_change,
355 (int64_t)pw->pw_expire, pw->pw_gecos, pw->pw_dir,
356 pw->pw_shell);
357 done = 1;
358 if (ferror(to))
359 goto err;
360 }
361 if (!done)
362 (void)fprintf(to, "%s:%s:%d:%d:%s:%lld:%lld:%s:%s:%s\n",
363 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
364 pw->pw_class, (int64_t)pw->pw_change,
365 (int64_t)pw->pw_expire, pw->pw_gecos,
366 pw->pw_dir, pw->pw_shell);
367
368 if (ferror(to))
369 err:
370 pw_error(NULL, 0, 1);
371 free(master);
372 (void)fclose(to);
373 }
374
375 int
pw_scan(char * bp,struct passwd * pw,int * flags)376 pw_scan(char *bp, struct passwd *pw, int *flags)
377 {
378 u_long id;
379 int root;
380 char *p, *sh, *p2;
381
382 if (flags != (int *)NULL)
383 *flags = 0;
384
385 if (!(p = strsep(&bp, ":")) || *p == '\0') /* login */
386 goto fmt;
387 pw->pw_name = p;
388 root = !strcmp(pw->pw_name, "root");
389
390 if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */
391 goto fmt;
392
393 if (!(p = strsep(&bp, ":"))) /* uid */
394 goto fmt;
395 id = strtoul(p, &p2, 10);
396 if (root && id) {
397 warnx("root uid should be 0");
398 return (0);
399 }
400 if (*p2 != '\0') {
401 warnx("illegal uid field");
402 return (0);
403 }
404 if (id > UID_MAX) {
405 /* errno is set to ERANGE by strtoul(3) */
406 warnx("uid greater than %u", UID_MAX-1);
407 return (0);
408 }
409 pw->pw_uid = (uid_t)id;
410 if ((*p == '\0') && (flags != (int *)NULL))
411 *flags |= _PASSWORD_NOUID;
412
413 if (!(p = strsep(&bp, ":"))) /* gid */
414 goto fmt;
415 id = strtoul(p, &p2, 10);
416 if (*p2 != '\0') {
417 warnx("illegal gid field");
418 return (0);
419 }
420 if (id > UID_MAX) {
421 /* errno is set to ERANGE by strtoul(3) */
422 warnx("gid greater than %u", UID_MAX-1);
423 return (0);
424 }
425 pw->pw_gid = (gid_t)id;
426 if ((*p == '\0') && (flags != (int *)NULL))
427 *flags |= _PASSWORD_NOGID;
428
429 pw->pw_class = strsep(&bp, ":"); /* class */
430 if (!(p = strsep(&bp, ":"))) /* change */
431 goto fmt;
432 pw->pw_change = atol(p);
433 if ((*p == '\0') && (flags != (int *)NULL))
434 *flags |= _PASSWORD_NOCHG;
435 if (!(p = strsep(&bp, ":"))) /* expire */
436 goto fmt;
437 pw->pw_expire = atol(p);
438 if ((*p == '\0') && (flags != (int *)NULL))
439 *flags |= _PASSWORD_NOEXP;
440 pw->pw_gecos = strsep(&bp, ":"); /* gecos */
441 pw->pw_dir = strsep(&bp, ":"); /* directory */
442 if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */
443 goto fmt;
444
445 p = pw->pw_shell;
446 if (root && *p) { /* empty == /bin/sh */
447 for (setusershell();;) {
448 if (!(sh = getusershell())) {
449 warnx("warning, unknown root shell");
450 break;
451 }
452 if (!strcmp(p, sh))
453 break;
454 }
455 endusershell();
456 }
457
458 if ((p = strsep(&bp, ":"))) { /* too many */
459 fmt: warnx("corrupted entry");
460 return (0);
461 }
462
463 return (1);
464 }
465
466 __dead void
pw_error(const char * name,int err,int eval)467 pw_error(const char *name, int err, int eval)
468 {
469 char *master = pw_file(_PATH_MASTERPASSWD);
470
471 if (err) {
472 if (name)
473 warn("%s", name);
474 else
475 warn(NULL);
476 }
477 if (master) {
478 warnx("%s: unchanged", master);
479 free(master);
480 }
481
482 pw_abort();
483 exit(eval);
484 }
485