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;
212 /* Unlimited resource limits. */
213 rlim
.rlim_cur
= rlim
.rlim_max
= RLIM_INFINITY
;
214 (void)setrlimit(RLIMIT_CPU
, &rlim
);
215 (void)setrlimit(RLIMIT_FSIZE
, &rlim
);
216 (void)setrlimit(RLIMIT_STACK
, &rlim
);
217 (void)setrlimit(RLIMIT_DATA
, &rlim
);
218 (void)setrlimit(RLIMIT_RSS
, &rlim
);
220 /* Don't drop core (not really necessary, but GP's). */
221 rlim
.rlim_cur
= rlim
.rlim_max
= 0;
222 (void)setrlimit(RLIMIT_CORE
, &rlim
);
224 /* Turn off signals. */
225 (void)signal(SIGALRM
, SIG_IGN
);
226 (void)signal(SIGHUP
, SIG_IGN
);
227 (void)signal(SIGINT
, SIG_IGN
);
228 (void)signal(SIGPIPE
, SIG_IGN
);
229 (void)signal(SIGQUIT
, SIG_IGN
);
230 (void)signal(SIGTERM
, SIG_IGN
);
231 (void)signal(SIGCONT
, pw_cont
);
235 pw_edit(int notsetuid
, const char *filename
)
239 const char * volatile editor
;
240 const char *argp
[] = { "sh", "-c", NULL
, NULL
};
242 if (filename
== NULL
)
243 filename
= _PATH_MASTERPASSWD_LOCK
;
245 filename
= pw_filename(filename
);
246 if (filename
== NULL
)
249 if ((editor
= getenv("EDITOR")) == NULL
)
252 p
= malloc(strlen(editor
) + 1 + strlen(filename
) + 1);
256 sprintf(p
, "%s %s", editor
, filename
);
259 switch(editpid
= vfork()) {
268 execvp(_PATH_BSHELL
, (char *const *)__UNCONST(argp
));
275 editpid
= waitpid(editpid
, (int *)&pstat
, WUNTRACED
);
277 pw_error(editor
, 1, 1);
278 else if (WIFSTOPPED(pstat
))
279 raise(WSTOPSIG(pstat
));
280 else if (WIFEXITED(pstat
) && WEXITSTATUS(pstat
) == 0)
283 pw_error(editor
, 1, 1);
293 (void)printf("re-edit the password file? [y]: ");
294 (void)fflush(stdout
);
296 if (c
!= EOF
&& c
!= '\n')
297 while (getchar() != '\n');
299 pw_error(NULL
, 0, 0);
302 /* for use in pw_copy(). Compare a pw entry to a pw struct. */
303 /* returns a character string labelling the miscompared field or 0 */
305 pw_equal(char *buf
, struct passwd
*pw
)
307 struct passwd buf_pw
;
310 _DIAGASSERT(buf
!= NULL
);
311 _DIAGASSERT(pw
!= NULL
);
314 if (buf
[len
-1] == '\n')
316 if (!pw_scan(buf
, &buf_pw
, NULL
))
317 return "corrupt line";
318 if (strcmp(pw
->pw_name
, buf_pw
.pw_name
) != 0)
320 if (pw
->pw_uid
!= buf_pw
.pw_uid
)
322 if (pw
->pw_gid
!= buf_pw
.pw_gid
)
324 if (strcmp( pw
->pw_class
, buf_pw
.pw_class
) != 0)
326 if (pw
->pw_change
!= buf_pw
.pw_change
)
328 if (pw
->pw_expire
!= buf_pw
.pw_expire
)
330 if (strcmp( pw
->pw_gecos
, buf_pw
.pw_gecos
) != 0)
332 if (strcmp( pw
->pw_dir
, buf_pw
.pw_dir
) != 0)
334 if (strcmp( pw
->pw_shell
, buf_pw
.pw_shell
) != 0)
340 pw_copy(int ffd
, int tfd
, struct passwd
*pw
, struct passwd
*old_pw
)
345 rv
= pw_copyx(ffd
, tfd
, pw
, old_pw
, errbuf
, sizeof(errbuf
));
348 pw_error(NULL
, 0, 1);
353 pw_print(FILE *to
, const struct passwd
*pw
)
355 (void)fprintf(to
, "%s:%s:%d:%d:%s:%lld:%lld:%s:%s:%s\n",
356 pw
->pw_name
, pw
->pw_passwd
, pw
->pw_uid
, pw
->pw_gid
,
357 pw
->pw_class
, (long long)pw
->pw_change
,
358 (long long)pw
->pw_expire
,
359 pw
->pw_gecos
, pw
->pw_dir
, pw
->pw_shell
);
363 pw_copyx(int ffd
, int tfd
, struct passwd
*pw
, struct passwd
*old_pw
,
364 char *errbuf
, size_t errbufsz
)
366 const char *filename
;
367 char mpwd
[MAXPATHLEN
], mpwdl
[MAXPATHLEN
], *p
, buf
[8192];
371 _DIAGASSERT(pw
!= NULL
);
372 _DIAGASSERT(errbuf
!= NULL
);
373 /* old_pw may be NULL */
375 if ((filename
= pw_filename(_PATH_MASTERPASSWD
)) == NULL
) {
376 snprintf(errbuf
, errbufsz
, "%s: %s", pw_prefix
,
380 (void)strcpy(mpwd
, filename
);
381 if ((filename
= pw_filename(_PATH_MASTERPASSWD_LOCK
)) == NULL
) {
382 snprintf(errbuf
, errbufsz
, "%s: %s", pw_prefix
,
386 (void)strcpy(mpwdl
, filename
);
388 if (!(from
= fdopen(ffd
, "r"))) {
389 snprintf(errbuf
, errbufsz
, "%s: %s", mpwd
, strerror(errno
));
392 if (!(to
= fdopen(tfd
, "w"))) {
393 snprintf(errbuf
, errbufsz
, "%s: %s", mpwdl
, strerror(errno
));
398 for (done
= 0; fgets(buf
, (int)sizeof(buf
), from
);) {
400 if (!strchr(buf
, '\n')) {
401 snprintf(errbuf
, errbufsz
, "%s: line too long", mpwd
);
407 (void)fprintf(to
, "%s", buf
);
409 snprintf(errbuf
, errbufsz
, "%s",
417 if (!(p
= strchr(buf
, ':'))) {
418 snprintf(errbuf
, errbufsz
, "%s: corrupted entry", mpwd
);
424 if (strcmp(buf
, pw
->pw_name
)) {
426 (void)fprintf(to
, "%s", buf
);
428 snprintf(errbuf
, errbufsz
, "%s",
437 if (old_pw
&& (neq
= pw_equal(buf
, old_pw
)) != NULL
) {
438 if (strcmp(neq
, "corrupt line") == 0)
439 (void)snprintf(errbuf
, errbufsz
,
440 "%s: entry %s corrupted", mpwd
,
443 (void)snprintf(errbuf
, errbufsz
,
444 "%s: entry %s inconsistent %s",
445 mpwd
, pw
->pw_name
, neq
);
453 snprintf(errbuf
, errbufsz
, "%s", strerror(errno
));
459 /* Only append a new entry if real uid is root! */
465 snprintf(errbuf
, errbufsz
,
466 "%s: changes not made, no such entry", mpwd
);
471 snprintf(errbuf
, errbufsz
, "%s", strerror(errno
));
483 pw_error(const char *name
, int error
, int eval
)
493 warnx("%s%s: unchanged", pw_prefix
, _PATH_MASTERPASSWD
);
498 /* Removes head and/or tail spaces. */
500 trim_whitespace(char *line
)
504 _DIAGASSERT(line
!= NULL
);
506 /* Remove leading spaces */
508 while (isspace((unsigned char) *p
))
510 memmove(line
, p
, strlen(p
) + 1);
512 /* Remove trailing spaces */
513 p
= line
+ strlen(line
) - 1;
514 while (isspace((unsigned char) *p
))
520 /* Get one line, remove spaces from front and tail */
522 read_line(FILE *fp
, char *line
, int max
)
526 _DIAGASSERT(fp
!= NULL
);
527 _DIAGASSERT(line
!= NULL
);
529 /* Read one line of config */
530 if (fgets(line
, max
, fp
) == NULL
)
533 if ((p
= strchr(line
, '\n')) == NULL
) {
534 warnx("line too long");
539 /* Remove comments */
540 if ((p
= strchr(line
, '#')) != NULL
)
543 trim_whitespace(line
);
548 pw_default(const char *option
)
550 static const char *options
[][2] = {
551 { "localcipher", "old" },
552 { "ypcipher", "old" },
556 _DIAGASSERT(option
!= NULL
);
557 for (i
= 0; i
< sizeof(options
) / sizeof(options
[0]); i
++)
558 if (strcmp(options
[i
][0], option
) == 0)
559 return (options
[i
][1]);
565 * Retrieve password information from the /etc/passwd.conf file, at the
566 * moment this is only for choosing the cipher to use. It could easily be
567 * used for other authentication methods as well.
570 pw_getconf(char *data
, size_t max
, const char *key
, const char *option
)
573 char line
[LINE_MAX
], *p
, *p2
;
574 static char result
[LINE_MAX
];
578 _DIAGASSERT(data
!= NULL
);
579 _DIAGASSERT(key
!= NULL
);
580 _DIAGASSERT(option
!= NULL
);
586 if ((fp
= fopen(_PATH_PASSWD_CONF
, "r")) == NULL
) {
587 if ((cp
= pw_default(option
)) != NULL
)
588 strlcpy(data
, cp
, max
);
594 while (!found
&& (got
|| read_line(fp
, line
, LINE_MAX
))) {
597 if (strncmp(key
, line
, strlen(key
)) != 0 ||
598 line
[strlen(key
)] != ':')
601 /* Now we found our specified key */
602 while (read_line(fp
, line
, LINE_MAX
)) {
603 /* Leaving key field */
604 if (line
[0] != '\0' && strchr(line
+ 1, ':') != NULL
) {
609 if ((p
= strsep(&p2
, "=")) == NULL
|| p2
== NULL
)
613 if (!strncmp(p
, option
, strlen(option
))) {
629 * If we got no result and were looking for a default
630 * value, try hard coded defaults.
633 if (strlen(result
) == 0 && strcmp(key
, "default") == 0 &&
634 (cp
= pw_default(option
)) != NULL
)
635 strlcpy(data
, cp
, max
);
637 strlcpy(data
, result
, max
);
641 pw_getpwconf(char *data
, size_t max
, const struct passwd
*pwd
,
644 char grpkey
[LINE_MAX
];
645 struct group grs
, *grp
;
648 pw_getconf(data
, max
, pwd
->pw_name
, option
);
650 /* Try to find an entry for the group */
652 (void)getgrgid_r(pwd
->pw_gid
, &grs
, grbuf
, sizeof(grbuf
), &grp
);
654 (void)snprintf(grpkey
, sizeof(grpkey
), ":%s",
656 pw_getconf(data
, max
, grpkey
, option
);
659 pw_getconf(data
, max
, "default", option
);