1 /* $NetBSD: passwd.c,v 1.52 2012/06/25 22:32:47 abs 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.52 2012/06/25 22:32:47 abs 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(const char *username
, int secureonly
)
148 args
[0] = "pwd_mkdb";
156 if (username
!= NULL
) {
158 args
[i
++] = username
;
161 args
[i
++] = pw_filename(_PATH_MASTERPASSWD_LOCK
);
163 execv(_PATH_PWD_MKDB
, (char * const *)__UNCONST(args
));
166 pid
= waitpid(pid
, &pstat
, 0);
168 warn("error waiting for pid %lu", (unsigned long)pid
);
171 if (WIFEXITED(pstat
)) {
172 if (WEXITSTATUS(pstat
) != 0) {
173 warnx("pwd_mkdb exited with status %d",
177 } else if (WIFSIGNALED(pstat
)) {
178 warnx("pwd_mkdb exited with signal %d", WTERMSIG(pstat
));
187 const char *filename
;
189 filename
= pw_filename(_PATH_MASTERPASSWD_LOCK
);
190 return((filename
== NULL
) ? -1 : unlink(filename
));
193 /* Everything below this point is intended for the convenience of programs
194 * which allow a user to interactively edit the passwd file. Errors in the
195 * routines below will cause the process to abort. */
197 static pid_t editpid
= -1;
213 /* Unlimited resource limits. */
214 rlim
.rlim_cur
= rlim
.rlim_max
= RLIM_INFINITY
;
215 (void)setrlimit(RLIMIT_CPU
, &rlim
);
216 (void)setrlimit(RLIMIT_FSIZE
, &rlim
);
217 (void)setrlimit(RLIMIT_STACK
, &rlim
);
218 (void)setrlimit(RLIMIT_DATA
, &rlim
);
219 (void)setrlimit(RLIMIT_RSS
, &rlim
);
221 /* Don't drop core (not really necessary, but GP's). */
222 rlim
.rlim_cur
= rlim
.rlim_max
= 0;
223 (void)setrlimit(RLIMIT_CORE
, &rlim
);
226 /* Turn off signals. */
227 (void)signal(SIGALRM
, SIG_IGN
);
228 (void)signal(SIGHUP
, SIG_IGN
);
229 (void)signal(SIGINT
, SIG_IGN
);
230 (void)signal(SIGPIPE
, SIG_IGN
);
231 (void)signal(SIGQUIT
, SIG_IGN
);
232 (void)signal(SIGTERM
, SIG_IGN
);
233 (void)signal(SIGCONT
, pw_cont
);
237 pw_edit(int notsetuid
, const char *filename
)
241 const char * volatile editor
;
242 const char *argp
[] = { "sh", "-c", NULL
, NULL
};
244 if (filename
== NULL
)
245 filename
= _PATH_MASTERPASSWD_LOCK
;
247 filename
= pw_filename(filename
);
248 if (filename
== NULL
)
251 if ((editor
= getenv("EDITOR")) == NULL
)
254 p
= malloc(strlen(editor
) + 1 + strlen(filename
) + 1);
258 sprintf(p
, "%s %s", editor
, filename
);
261 switch(editpid
= vfork()) {
270 execvp(_PATH_BSHELL
, (char *const *)__UNCONST(argp
));
277 editpid
= waitpid(editpid
, (int *)&pstat
, WUNTRACED
);
279 pw_error(editor
, 1, 1);
280 else if (WIFSTOPPED(pstat
))
281 raise(WSTOPSIG(pstat
));
282 else if (WIFEXITED(pstat
) && WEXITSTATUS(pstat
) == 0)
285 pw_error(editor
, 1, 1);
295 (void)printf("re-edit the password file? [y]: ");
296 (void)fflush(stdout
);
298 if (c
!= EOF
&& c
!= '\n')
299 while (getchar() != '\n');
301 pw_error(NULL
, 0, 0);
304 /* for use in pw_copy(). Compare a pw entry to a pw struct. */
305 /* returns a character string labelling the miscompared field or 0 */
307 pw_equal(char *buf
, struct passwd
*pw
)
309 struct passwd buf_pw
;
312 _DIAGASSERT(buf
!= NULL
);
313 _DIAGASSERT(pw
!= NULL
);
316 if (buf
[len
-1] == '\n')
318 if (!pw_scan(buf
, &buf_pw
, NULL
))
319 return "corrupt line";
320 if (strcmp(pw
->pw_name
, buf_pw
.pw_name
) != 0)
322 if (pw
->pw_uid
!= buf_pw
.pw_uid
)
324 if (pw
->pw_gid
!= buf_pw
.pw_gid
)
326 if (strcmp( pw
->pw_class
, buf_pw
.pw_class
) != 0)
328 if (pw
->pw_change
!= buf_pw
.pw_change
)
330 if (pw
->pw_expire
!= buf_pw
.pw_expire
)
332 if (strcmp( pw
->pw_gecos
, buf_pw
.pw_gecos
) != 0)
334 if (strcmp( pw
->pw_dir
, buf_pw
.pw_dir
) != 0)
336 if (strcmp( pw
->pw_shell
, buf_pw
.pw_shell
) != 0)
342 pw_copy(int ffd
, int tfd
, struct passwd
*pw
, struct passwd
*old_pw
)
347 rv
= pw_copyx(ffd
, tfd
, pw
, old_pw
, errbuf
, sizeof(errbuf
));
350 pw_error(NULL
, 0, 1);
355 pw_print(FILE *to
, const struct passwd
*pw
)
357 (void)fprintf(to
, "%s:%s:%d:%d:%s:%lld:%lld:%s:%s:%s\n",
358 pw
->pw_name
, pw
->pw_passwd
, pw
->pw_uid
, pw
->pw_gid
,
359 pw
->pw_class
, (long long)pw
->pw_change
,
360 (long long)pw
->pw_expire
,
361 pw
->pw_gecos
, pw
->pw_dir
, pw
->pw_shell
);
365 pw_copyx(int ffd
, int tfd
, struct passwd
*pw
, struct passwd
*old_pw
,
366 char *errbuf
, size_t errbufsz
)
368 const char *filename
;
369 char mpwd
[MAXPATHLEN
], mpwdl
[MAXPATHLEN
], *p
, buf
[8192];
373 _DIAGASSERT(pw
!= NULL
);
374 _DIAGASSERT(errbuf
!= NULL
);
375 /* old_pw may be NULL */
377 if ((filename
= pw_filename(_PATH_MASTERPASSWD
)) == NULL
) {
378 snprintf(errbuf
, errbufsz
, "%s: %s", pw_prefix
,
382 (void)strcpy(mpwd
, filename
);
383 if ((filename
= pw_filename(_PATH_MASTERPASSWD_LOCK
)) == NULL
) {
384 snprintf(errbuf
, errbufsz
, "%s: %s", pw_prefix
,
388 (void)strcpy(mpwdl
, filename
);
390 if (!(from
= fdopen(ffd
, "r"))) {
391 snprintf(errbuf
, errbufsz
, "%s: %s", mpwd
, strerror(errno
));
394 if (!(to
= fdopen(tfd
, "w"))) {
395 snprintf(errbuf
, errbufsz
, "%s: %s", mpwdl
, strerror(errno
));
400 for (done
= 0; fgets(buf
, (int)sizeof(buf
), from
);) {
402 if (!strchr(buf
, '\n')) {
403 snprintf(errbuf
, errbufsz
, "%s: line too long", mpwd
);
409 (void)fprintf(to
, "%s", buf
);
411 snprintf(errbuf
, errbufsz
, "%s",
419 if (!(p
= strchr(buf
, ':'))) {
420 snprintf(errbuf
, errbufsz
, "%s: corrupted entry", mpwd
);
426 if (strcmp(buf
, pw
->pw_name
)) {
428 (void)fprintf(to
, "%s", buf
);
430 snprintf(errbuf
, errbufsz
, "%s",
439 if (old_pw
&& (neq
= pw_equal(buf
, old_pw
)) != NULL
) {
440 if (strcmp(neq
, "corrupt line") == 0)
441 (void)snprintf(errbuf
, errbufsz
,
442 "%s: entry %s corrupted", mpwd
,
445 (void)snprintf(errbuf
, errbufsz
,
446 "%s: entry %s inconsistent %s",
447 mpwd
, pw
->pw_name
, neq
);
455 snprintf(errbuf
, errbufsz
, "%s", strerror(errno
));
461 /* Only append a new entry if real uid is root! */
467 snprintf(errbuf
, errbufsz
,
468 "%s: changes not made, no such entry", mpwd
);
473 snprintf(errbuf
, errbufsz
, "%s", strerror(errno
));
485 pw_error(const char *name
, int error
, int eval
)
495 warnx("%s%s: unchanged", pw_prefix
, _PATH_MASTERPASSWD
);
500 /* Removes head and/or tail spaces. */
502 trim_whitespace(char *line
)
506 _DIAGASSERT(line
!= NULL
);
508 /* Remove leading spaces */
510 while (isspace((unsigned char) *p
))
512 memmove(line
, p
, strlen(p
) + 1);
514 /* Remove trailing spaces */
515 p
= line
+ strlen(line
) - 1;
516 while (isspace((unsigned char) *p
))
522 /* Get one line, remove spaces from front and tail */
524 read_line(FILE *fp
, char *line
, int max
)
528 _DIAGASSERT(fp
!= NULL
);
529 _DIAGASSERT(line
!= NULL
);
531 /* Read one line of config */
532 if (fgets(line
, max
, fp
) == NULL
)
535 if ((p
= strchr(line
, '\n')) == NULL
) {
536 warnx("line too long");
541 /* Remove comments */
542 if ((p
= strchr(line
, '#')) != NULL
)
545 trim_whitespace(line
);
550 pw_default(const char *option
)
552 static const char *options
[][2] = {
553 { "localcipher", "old" },
554 { "ypcipher", "old" },
558 _DIAGASSERT(option
!= NULL
);
559 for (i
= 0; i
< sizeof(options
) / sizeof(options
[0]); i
++)
560 if (strcmp(options
[i
][0], option
) == 0)
561 return (options
[i
][1]);
567 * Retrieve password information from the /etc/passwd.conf file, at the
568 * moment this is only for choosing the cipher to use. It could easily be
569 * used for other authentication methods as well.
572 pw_getconf(char *data
, size_t max
, const char *key
, const char *option
)
575 char line
[LINE_MAX
], *p
, *p2
;
576 static char result
[LINE_MAX
];
580 _DIAGASSERT(data
!= NULL
);
581 _DIAGASSERT(key
!= NULL
);
582 _DIAGASSERT(option
!= NULL
);
588 if ((fp
= fopen(_PATH_PASSWD_CONF
, "r")) == NULL
) {
589 if ((cp
= pw_default(option
)) != NULL
)
590 strlcpy(data
, cp
, max
);
596 while (!found
&& (got
|| read_line(fp
, line
, LINE_MAX
))) {
599 if (strncmp(key
, line
, strlen(key
)) != 0 ||
600 line
[strlen(key
)] != ':')
603 /* Now we found our specified key */
604 while (read_line(fp
, line
, LINE_MAX
)) {
605 /* Leaving key field */
606 if (line
[0] != '\0' && strchr(line
+ 1, ':') != NULL
) {
611 if ((p
= strsep(&p2
, "=")) == NULL
|| p2
== NULL
)
615 if (!strncmp(p
, option
, strlen(option
))) {
631 * If we got no result and were looking for a default
632 * value, try hard coded defaults.
635 if (strlen(result
) == 0 && strcmp(key
, "default") == 0 &&
636 (cp
= pw_default(option
)) != NULL
)
637 strlcpy(data
, cp
, max
);
639 strlcpy(data
, result
, max
);
643 pw_getpwconf(char *data
, size_t max
, const struct passwd
*pwd
,
646 char grpkey
[LINE_MAX
];
647 struct group grs
, *grp
;
650 pw_getconf(data
, max
, pwd
->pw_name
, option
);
652 /* Try to find an entry for the group */
654 (void)getgrgid_r(pwd
->pw_gid
, &grs
, grbuf
, sizeof(grbuf
), &grp
);
656 (void)snprintf(grpkey
, sizeof(grpkey
), ":%s",
658 pw_getconf(data
, max
, grpkey
, option
);
661 pw_getconf(data
, max
, "default", option
);
665 #if defined(__minix) && defined(__weak_alias)
666 __weak_alias(pw_copy
, __pw_copy50
)
667 __weak_alias(pw_copyx
, __pw_copyx50
)
668 __weak_alias(pw_getpwconf
, __pw_getpwconf50
)