1 /* $NetBSD: passwd.c,v 1.47 2009/01/11 02:57:17 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.47 2009/01/11 02:57:17 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);
169 if (pid
== -1 || !WIFEXITED(pstat
) || WEXITSTATUS(pstat
) != 0)
177 const char *filename
;
179 filename
= pw_filename(_PATH_MASTERPASSWD_LOCK
);
180 return((filename
== NULL
) ? -1 : unlink(filename
));
183 /* Everything below this point is intended for the convenience of programs
184 * which allow a user to interactively edit the passwd file. Errors in the
185 * routines below will cause the process to abort. */
187 static pid_t editpid
= -1;
202 /* Unlimited resource limits. */
203 rlim
.rlim_cur
= rlim
.rlim_max
= RLIM_INFINITY
;
204 (void)setrlimit(RLIMIT_CPU
, &rlim
);
205 (void)setrlimit(RLIMIT_FSIZE
, &rlim
);
206 (void)setrlimit(RLIMIT_STACK
, &rlim
);
207 (void)setrlimit(RLIMIT_DATA
, &rlim
);
208 (void)setrlimit(RLIMIT_RSS
, &rlim
);
210 /* Don't drop core (not really necessary, but GP's). */
211 rlim
.rlim_cur
= rlim
.rlim_max
= 0;
212 (void)setrlimit(RLIMIT_CORE
, &rlim
);
214 /* Turn off signals. */
215 (void)signal(SIGALRM
, SIG_IGN
);
216 (void)signal(SIGHUP
, SIG_IGN
);
217 (void)signal(SIGINT
, SIG_IGN
);
218 (void)signal(SIGPIPE
, SIG_IGN
);
219 (void)signal(SIGQUIT
, SIG_IGN
);
220 (void)signal(SIGTERM
, SIG_IGN
);
221 (void)signal(SIGCONT
, pw_cont
);
225 pw_edit(int notsetuid
, const char *filename
)
229 const char * volatile editor
;
230 const char *argp
[] = { "sh", "-c", NULL
, NULL
};
232 if (filename
== NULL
)
233 filename
= _PATH_MASTERPASSWD_LOCK
;
235 filename
= pw_filename(filename
);
236 if (filename
== NULL
)
239 if ((editor
= getenv("EDITOR")) == NULL
)
242 p
= malloc(strlen(editor
) + 1 + strlen(filename
) + 1);
246 sprintf(p
, "%s %s", editor
, filename
);
249 switch(editpid
= vfork()) {
258 execvp(_PATH_BSHELL
, (char *const *)__UNCONST(argp
));
265 editpid
= waitpid(editpid
, (int *)&pstat
, WUNTRACED
);
267 pw_error(editor
, 1, 1);
268 else if (WIFSTOPPED(pstat
))
269 raise(WSTOPSIG(pstat
));
270 else if (WIFEXITED(pstat
) && WEXITSTATUS(pstat
) == 0)
273 pw_error(editor
, 1, 1);
283 (void)printf("re-edit the password file? [y]: ");
284 (void)fflush(stdout
);
286 if (c
!= EOF
&& c
!= '\n')
287 while (getchar() != '\n');
289 pw_error(NULL
, 0, 0);
292 /* for use in pw_copy(). Compare a pw entry to a pw struct. */
293 /* returns a character string labelling the miscompared field or 0 */
295 pw_equal(char *buf
, struct passwd
*pw
)
297 struct passwd buf_pw
;
300 _DIAGASSERT(buf
!= NULL
);
301 _DIAGASSERT(pw
!= NULL
);
304 if (buf
[len
-1] == '\n')
306 if (!pw_scan(buf
, &buf_pw
, NULL
))
307 return "corrupt line";
308 if (strcmp(pw
->pw_name
, buf_pw
.pw_name
) != 0)
310 if (pw
->pw_uid
!= buf_pw
.pw_uid
)
312 if (pw
->pw_gid
!= buf_pw
.pw_gid
)
314 if (strcmp( pw
->pw_class
, buf_pw
.pw_class
) != 0)
316 if (pw
->pw_change
!= buf_pw
.pw_change
)
318 if (pw
->pw_expire
!= buf_pw
.pw_expire
)
320 if (strcmp( pw
->pw_gecos
, buf_pw
.pw_gecos
) != 0)
322 if (strcmp( pw
->pw_dir
, buf_pw
.pw_dir
) != 0)
324 if (strcmp( pw
->pw_shell
, buf_pw
.pw_shell
) != 0)
330 pw_copy(int ffd
, int tfd
, struct passwd
*pw
, struct passwd
*old_pw
)
335 rv
= pw_copyx(ffd
, tfd
, pw
, old_pw
, errbuf
, sizeof(errbuf
));
338 pw_error(NULL
, 0, 1);
343 pw_print(FILE *to
, const struct passwd
*pw
)
345 (void)fprintf(to
, "%s:%s:%d:%d:%s:%lld:%lld:%s:%s:%s\n",
346 pw
->pw_name
, pw
->pw_passwd
, pw
->pw_uid
, pw
->pw_gid
,
347 pw
->pw_class
, (long long)pw
->pw_change
,
348 (long long)pw
->pw_expire
,
349 pw
->pw_gecos
, pw
->pw_dir
, pw
->pw_shell
);
353 pw_copyx(int ffd
, int tfd
, struct passwd
*pw
, struct passwd
*old_pw
,
354 char *errbuf
, size_t errbufsz
)
356 const char *filename
;
357 char mpwd
[MAXPATHLEN
], mpwdl
[MAXPATHLEN
], *p
, buf
[8192];
361 _DIAGASSERT(pw
!= NULL
);
362 _DIAGASSERT(errbuf
!= NULL
);
363 /* old_pw may be NULL */
365 if ((filename
= pw_filename(_PATH_MASTERPASSWD
)) == NULL
) {
366 snprintf(errbuf
, errbufsz
, "%s: %s", pw_prefix
,
370 (void)strcpy(mpwd
, filename
);
371 if ((filename
= pw_filename(_PATH_MASTERPASSWD_LOCK
)) == NULL
) {
372 snprintf(errbuf
, errbufsz
, "%s: %s", pw_prefix
,
376 (void)strcpy(mpwdl
, filename
);
378 if (!(from
= fdopen(ffd
, "r"))) {
379 snprintf(errbuf
, errbufsz
, "%s: %s", mpwd
, strerror(errno
));
382 if (!(to
= fdopen(tfd
, "w"))) {
383 snprintf(errbuf
, errbufsz
, "%s: %s", mpwdl
, strerror(errno
));
388 for (done
= 0; fgets(buf
, (int)sizeof(buf
), from
);) {
390 if (!strchr(buf
, '\n')) {
391 snprintf(errbuf
, errbufsz
, "%s: line too long", mpwd
);
397 (void)fprintf(to
, "%s", buf
);
399 snprintf(errbuf
, errbufsz
, "%s",
407 if (!(p
= strchr(buf
, ':'))) {
408 snprintf(errbuf
, errbufsz
, "%s: corrupted entry", mpwd
);
414 if (strcmp(buf
, pw
->pw_name
)) {
416 (void)fprintf(to
, "%s", buf
);
418 snprintf(errbuf
, errbufsz
, "%s",
427 if (old_pw
&& (neq
= pw_equal(buf
, old_pw
)) != NULL
) {
428 if (strcmp(neq
, "corrupt line") == 0)
429 (void)snprintf(errbuf
, errbufsz
,
430 "%s: entry %s corrupted", mpwd
,
433 (void)snprintf(errbuf
, errbufsz
,
434 "%s: entry %s inconsistent %s",
435 mpwd
, pw
->pw_name
, neq
);
443 snprintf(errbuf
, errbufsz
, "%s", strerror(errno
));
449 /* Only append a new entry if real uid is root! */
455 snprintf(errbuf
, errbufsz
,
456 "%s: changes not made, no such entry", mpwd
);
461 snprintf(errbuf
, errbufsz
, "%s", strerror(errno
));
473 pw_error(const char *name
, int error
, int eval
)
483 warnx("%s%s: unchanged", pw_prefix
, _PATH_MASTERPASSWD
);
488 /* Removes head and/or tail spaces. */
490 trim_whitespace(char *line
)
494 _DIAGASSERT(line
!= NULL
);
496 /* Remove leading spaces */
498 while (isspace((unsigned char) *p
))
500 memmove(line
, p
, strlen(p
) + 1);
502 /* Remove trailing spaces */
503 p
= line
+ strlen(line
) - 1;
504 while (isspace((unsigned char) *p
))
510 /* Get one line, remove spaces from front and tail */
512 read_line(FILE *fp
, char *line
, int max
)
516 _DIAGASSERT(fp
!= NULL
);
517 _DIAGASSERT(line
!= NULL
);
519 /* Read one line of config */
520 if (fgets(line
, max
, fp
) == NULL
)
523 if ((p
= strchr(line
, '\n')) == NULL
) {
524 warnx("line too long");
529 /* Remove comments */
530 if ((p
= strchr(line
, '#')) != NULL
)
533 trim_whitespace(line
);
538 pw_default(const char *option
)
540 static const char *options
[][2] = {
541 { "localcipher", "old" },
542 { "ypcipher", "old" },
546 _DIAGASSERT(option
!= NULL
);
547 for (i
= 0; i
< sizeof(options
) / sizeof(options
[0]); i
++)
548 if (strcmp(options
[i
][0], option
) == 0)
549 return (options
[i
][1]);
555 * Retrieve password information from the /etc/passwd.conf file, at the
556 * moment this is only for choosing the cipher to use. It could easily be
557 * used for other authentication methods as well.
560 pw_getconf(char *data
, size_t max
, const char *key
, const char *option
)
563 char line
[LINE_MAX
], *p
, *p2
;
564 static char result
[LINE_MAX
];
568 _DIAGASSERT(data
!= NULL
);
569 _DIAGASSERT(key
!= NULL
);
570 _DIAGASSERT(option
!= NULL
);
576 if ((fp
= fopen(_PATH_PASSWD_CONF
, "r")) == NULL
) {
577 if ((cp
= pw_default(option
)) != NULL
)
578 strlcpy(data
, cp
, max
);
584 while (!found
&& (got
|| read_line(fp
, line
, LINE_MAX
))) {
587 if (strncmp(key
, line
, strlen(key
)) != 0 ||
588 line
[strlen(key
)] != ':')
591 /* Now we found our specified key */
592 while (read_line(fp
, line
, LINE_MAX
)) {
593 /* Leaving key field */
594 if (line
[0] != '\0' && strchr(line
+ 1, ':') != NULL
) {
599 if ((p
= strsep(&p2
, "=")) == NULL
|| p2
== NULL
)
603 if (!strncmp(p
, option
, strlen(option
))) {
619 * If we got no result and were looking for a default
620 * value, try hard coded defaults.
623 if (strlen(result
) == 0 && strcmp(key
, "default") == 0 &&
624 (cp
= pw_default(option
)) != NULL
)
625 strlcpy(data
, cp
, max
);
627 strlcpy(data
, result
, max
);
631 pw_getpwconf(char *data
, size_t max
, const struct passwd
*pwd
,
634 char grpkey
[LINE_MAX
];
635 struct group grs
, *grp
;
638 pw_getconf(data
, max
, pwd
->pw_name
, option
);
640 /* Try to find an entry for the group */
642 (void)getgrgid_r(pwd
->pw_gid
, &grs
, grbuf
, sizeof(grbuf
), &grp
);
644 (void)snprintf(grpkey
, sizeof(grpkey
), ":%s",
646 pw_getconf(data
, max
, grpkey
, option
);
649 pw_getconf(data
, max
, "default", option
);