1 /* $NetBSD: passwd.c,v 1.50 2010/08/18 08:32:02 christos Exp $ */
4 * Copyright (c) 1987, 1993, 1994, 1995
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: passwd.c,v 1.50 2010/08/18 08:32:02 christos Exp $");
35 #endif /* LIBC_SCCS and not lint */
37 #include <sys/types.h>
38 #include <sys/param.h>
41 #include <sys/resource.h>
60 static const char *pw_filename(const char *filename
);
61 static void pw_cont(int sig
);
62 static const char * pw_equal(char *buf
, struct passwd
*old_pw
);
63 static const char *pw_default(const char *option
);
64 static int read_line(FILE *fp
, char *line
, int max
);
65 static void trim_whitespace(char *line
);
67 static char pw_prefix
[MAXPATHLEN
];
77 pw_setprefix(const char *new_prefix
)
81 _DIAGASSERT(new_prefix
!= NULL
);
83 length
= strlen(new_prefix
);
84 if (length
< sizeof(pw_prefix
)) {
85 (void)strcpy(pw_prefix
, new_prefix
);
86 while (length
> 0 && pw_prefix
[length
- 1] == '/')
87 pw_prefix
[--length
] = '\0';
95 pw_filename(const char *filename
)
97 static char newfilename
[MAXPATHLEN
];
99 _DIAGASSERT(filename
!= NULL
);
101 if (pw_prefix
[0] == '\0')
104 if (strlen(pw_prefix
) + strlen(filename
) < sizeof(newfilename
))
105 return strcat(strcpy(newfilename
, pw_prefix
), filename
);
107 errno
= ENAMETOOLONG
;
114 const char *filename
;
119 /* Acquire the lock file. */
120 filename
= pw_filename(_PATH_MASTERPASSWD_LOCK
);
121 if (filename
== NULL
)
124 fd
= open(filename
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600);
125 for (i
= 0; i
< retries
&& fd
< 0 && errno
== EEXIST
; i
++) {
127 fd
= open(filename
, O_WRONLY
|O_CREAT
|O_EXCL
,
131 (void)umask(old_mode
);
137 pw_mkdb(username
, secureonly
)
138 const char *username
;
150 args
[0] = "pwd_mkdb";
158 if (username
!= NULL
) {
160 args
[i
++] = username
;
163 args
[i
++] = pw_filename(_PATH_MASTERPASSWD_LOCK
);
165 execv(_PATH_PWD_MKDB
, (char * const *)__UNCONST(args
));
168 pid
= waitpid(pid
, &pstat
, 0);
170 warn("error waiting for pid %lu", (unsigned long)pid
);
173 if (WIFEXITED(pstat
)) {
174 if (WEXITSTATUS(pstat
) != 0) {
175 warnx("pwd_mkdb exited with static %d",
179 } else if (WIFSIGNALED(pstat
)) {
180 warnx("pwd_mkdb exited with signal %d", WTERMSIG(pstat
));
189 const char *filename
;
191 filename
= pw_filename(_PATH_MASTERPASSWD_LOCK
);
192 return((filename
== NULL
) ? -1 : unlink(filename
));
195 /* Everything below this point is intended for the convenience of programs
196 * which allow a user to interactively edit the passwd file. Errors in the
197 * routines below will cause the process to abort. */
199 static pid_t editpid
= -1;
215 /* Unlimited resource limits. */
216 rlim
.rlim_cur
= rlim
.rlim_max
= RLIM_INFINITY
;
217 (void)setrlimit(RLIMIT_CPU
, &rlim
);
218 (void)setrlimit(RLIMIT_FSIZE
, &rlim
);
219 (void)setrlimit(RLIMIT_STACK
, &rlim
);
220 (void)setrlimit(RLIMIT_DATA
, &rlim
);
221 (void)setrlimit(RLIMIT_RSS
, &rlim
);
223 /* Don't drop core (not really necessary, but GP's). */
224 rlim
.rlim_cur
= rlim
.rlim_max
= 0;
225 (void)setrlimit(RLIMIT_CORE
, &rlim
);
228 /* Turn off signals. */
229 (void)signal(SIGALRM
, SIG_IGN
);
230 (void)signal(SIGHUP
, SIG_IGN
);
231 (void)signal(SIGINT
, SIG_IGN
);
232 (void)signal(SIGPIPE
, SIG_IGN
);
233 (void)signal(SIGQUIT
, SIG_IGN
);
234 (void)signal(SIGTERM
, SIG_IGN
);
235 (void)signal(SIGCONT
, pw_cont
);
239 pw_edit(int notsetuid
, const char *filename
)
243 const char * volatile editor
;
244 const char *argp
[] = { "sh", "-c", NULL
, NULL
};
246 if (filename
== NULL
)
247 filename
= _PATH_MASTERPASSWD_LOCK
;
249 filename
= pw_filename(filename
);
250 if (filename
== NULL
)
253 if ((editor
= getenv("EDITOR")) == NULL
)
256 p
= malloc(strlen(editor
) + 1 + strlen(filename
) + 1);
260 sprintf(p
, "%s %s", editor
, filename
);
263 switch(editpid
= vfork()) {
272 execvp(_PATH_BSHELL
, (char *const *)__UNCONST(argp
));
279 editpid
= waitpid(editpid
, (int *)&pstat
, WUNTRACED
);
281 pw_error(editor
, 1, 1);
282 else if (WIFSTOPPED(pstat
))
283 raise(WSTOPSIG(pstat
));
284 else if (WIFEXITED(pstat
) && WEXITSTATUS(pstat
) == 0)
287 pw_error(editor
, 1, 1);
297 (void)printf("re-edit the password file? [y]: ");
298 (void)fflush(stdout
);
300 if (c
!= EOF
&& c
!= '\n')
301 while (getchar() != '\n');
303 pw_error(NULL
, 0, 0);
306 /* for use in pw_copy(). Compare a pw entry to a pw struct. */
307 /* returns a character string labelling the miscompared field or 0 */
309 pw_equal(char *buf
, struct passwd
*pw
)
311 struct passwd buf_pw
;
314 _DIAGASSERT(buf
!= NULL
);
315 _DIAGASSERT(pw
!= NULL
);
318 if (buf
[len
-1] == '\n')
320 if (!pw_scan(buf
, &buf_pw
, NULL
))
321 return "corrupt line";
322 if (strcmp(pw
->pw_name
, buf_pw
.pw_name
) != 0)
324 if (pw
->pw_uid
!= buf_pw
.pw_uid
)
326 if (pw
->pw_gid
!= buf_pw
.pw_gid
)
328 if (strcmp( pw
->pw_class
, buf_pw
.pw_class
) != 0)
330 if (pw
->pw_change
!= buf_pw
.pw_change
)
332 if (pw
->pw_expire
!= buf_pw
.pw_expire
)
334 if (strcmp( pw
->pw_gecos
, buf_pw
.pw_gecos
) != 0)
336 if (strcmp( pw
->pw_dir
, buf_pw
.pw_dir
) != 0)
338 if (strcmp( pw
->pw_shell
, buf_pw
.pw_shell
) != 0)
344 pw_copy(int ffd
, int tfd
, struct passwd
*pw
, struct passwd
*old_pw
)
349 rv
= pw_copyx(ffd
, tfd
, pw
, old_pw
, errbuf
, sizeof(errbuf
));
352 pw_error(NULL
, 0, 1);
357 pw_print(FILE *to
, const struct passwd
*pw
)
359 (void)fprintf(to
, "%s:%s:%d:%d:%s:%lld:%lld:%s:%s:%s\n",
360 pw
->pw_name
, pw
->pw_passwd
, pw
->pw_uid
, pw
->pw_gid
,
361 pw
->pw_class
, (long long)pw
->pw_change
,
362 (long long)pw
->pw_expire
,
363 pw
->pw_gecos
, pw
->pw_dir
, pw
->pw_shell
);
367 pw_copyx(int ffd
, int tfd
, struct passwd
*pw
, struct passwd
*old_pw
,
368 char *errbuf
, size_t errbufsz
)
370 const char *filename
;
371 char mpwd
[MAXPATHLEN
], mpwdl
[MAXPATHLEN
], *p
, buf
[8192];
375 _DIAGASSERT(pw
!= NULL
);
376 _DIAGASSERT(errbuf
!= NULL
);
377 /* old_pw may be NULL */
379 if ((filename
= pw_filename(_PATH_MASTERPASSWD
)) == NULL
) {
380 snprintf(errbuf
, errbufsz
, "%s: %s", pw_prefix
,
384 (void)strcpy(mpwd
, filename
);
385 if ((filename
= pw_filename(_PATH_MASTERPASSWD_LOCK
)) == NULL
) {
386 snprintf(errbuf
, errbufsz
, "%s: %s", pw_prefix
,
390 (void)strcpy(mpwdl
, filename
);
392 if (!(from
= fdopen(ffd
, "r"))) {
393 snprintf(errbuf
, errbufsz
, "%s: %s", mpwd
, strerror(errno
));
396 if (!(to
= fdopen(tfd
, "w"))) {
397 snprintf(errbuf
, errbufsz
, "%s: %s", mpwdl
, strerror(errno
));
402 for (done
= 0; fgets(buf
, (int)sizeof(buf
), from
);) {
404 if (!strchr(buf
, '\n')) {
405 snprintf(errbuf
, errbufsz
, "%s: line too long", mpwd
);
411 (void)fprintf(to
, "%s", buf
);
413 snprintf(errbuf
, errbufsz
, "%s",
421 if (!(p
= strchr(buf
, ':'))) {
422 snprintf(errbuf
, errbufsz
, "%s: corrupted entry", mpwd
);
428 if (strcmp(buf
, pw
->pw_name
)) {
430 (void)fprintf(to
, "%s", buf
);
432 snprintf(errbuf
, errbufsz
, "%s",
441 if (old_pw
&& (neq
= pw_equal(buf
, old_pw
)) != NULL
) {
442 if (strcmp(neq
, "corrupt line") == 0)
443 (void)snprintf(errbuf
, errbufsz
,
444 "%s: entry %s corrupted", mpwd
,
447 (void)snprintf(errbuf
, errbufsz
,
448 "%s: entry %s inconsistent %s",
449 mpwd
, pw
->pw_name
, neq
);
457 snprintf(errbuf
, errbufsz
, "%s", strerror(errno
));
463 /* Only append a new entry if real uid is root! */
469 snprintf(errbuf
, errbufsz
,
470 "%s: changes not made, no such entry", mpwd
);
475 snprintf(errbuf
, errbufsz
, "%s", strerror(errno
));
487 pw_error(const char *name
, int error
, int eval
)
497 warnx("%s%s: unchanged", pw_prefix
, _PATH_MASTERPASSWD
);
502 /* Removes head and/or tail spaces. */
504 trim_whitespace(char *line
)
508 _DIAGASSERT(line
!= NULL
);
510 /* Remove leading spaces */
512 while (isspace((unsigned char) *p
))
514 memmove(line
, p
, strlen(p
) + 1);
516 /* Remove trailing spaces */
517 p
= line
+ strlen(line
) - 1;
518 while (isspace((unsigned char) *p
))
524 /* Get one line, remove spaces from front and tail */
526 read_line(FILE *fp
, char *line
, int max
)
530 _DIAGASSERT(fp
!= NULL
);
531 _DIAGASSERT(line
!= NULL
);
533 /* Read one line of config */
534 if (fgets(line
, max
, fp
) == NULL
)
537 if ((p
= strchr(line
, '\n')) == NULL
) {
538 warnx("line too long");
543 /* Remove comments */
544 if ((p
= strchr(line
, '#')) != NULL
)
547 trim_whitespace(line
);
552 pw_default(const char *option
)
554 static const char *options
[][2] = {
555 { "localcipher", "old" },
556 { "ypcipher", "old" },
560 _DIAGASSERT(option
!= NULL
);
561 for (i
= 0; i
< sizeof(options
) / sizeof(options
[0]); i
++)
562 if (strcmp(options
[i
][0], option
) == 0)
563 return (options
[i
][1]);
569 * Retrieve password information from the /etc/passwd.conf file, at the
570 * moment this is only for choosing the cipher to use. It could easily be
571 * used for other authentication methods as well.
574 pw_getconf(char *data
, size_t max
, const char *key
, const char *option
)
577 char line
[LINE_MAX
], *p
, *p2
;
578 static char result
[LINE_MAX
];
582 _DIAGASSERT(data
!= NULL
);
583 _DIAGASSERT(key
!= NULL
);
584 _DIAGASSERT(option
!= NULL
);
590 if ((fp
= fopen(_PATH_PASSWD_CONF
, "r")) == NULL
) {
591 if ((cp
= pw_default(option
)) != NULL
)
592 strlcpy(data
, cp
, max
);
598 while (!found
&& (got
|| read_line(fp
, line
, LINE_MAX
))) {
601 if (strncmp(key
, line
, strlen(key
)) != 0 ||
602 line
[strlen(key
)] != ':')
605 /* Now we found our specified key */
606 while (read_line(fp
, line
, LINE_MAX
)) {
607 /* Leaving key field */
608 if (line
[0] != '\0' && strchr(line
+ 1, ':') != NULL
) {
613 if ((p
= strsep(&p2
, "=")) == NULL
|| p2
== NULL
)
617 if (!strncmp(p
, option
, strlen(option
))) {
633 * If we got no result and were looking for a default
634 * value, try hard coded defaults.
637 if (strlen(result
) == 0 && strcmp(key
, "default") == 0 &&
638 (cp
= pw_default(option
)) != NULL
)
639 strlcpy(data
, cp
, max
);
641 strlcpy(data
, result
, max
);
645 pw_getpwconf(char *data
, size_t max
, const struct passwd
*pwd
,
648 char grpkey
[LINE_MAX
];
649 struct group grs
, *grp
;
652 pw_getconf(data
, max
, pwd
->pw_name
, option
);
654 /* Try to find an entry for the group */
656 (void)getgrgid_r(pwd
->pw_gid
, &grs
, grbuf
, sizeof(grbuf
), &grp
);
658 (void)snprintf(grpkey
, sizeof(grpkey
), ":%s",
660 pw_getconf(data
, max
, grpkey
, option
);
663 pw_getconf(data
, max
, "default", option
);