GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/chpass/chpass.c Lines: 0 109 0.0 %
Date: 2017-11-07 Branches: 0 67 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: chpass.c,v 1.43 2015/11/26 19:01:47 deraadt Exp $	*/
2
/*	$NetBSD: chpass.c,v 1.8 1996/05/15 21:50:43 jtc Exp $	*/
3
4
/*-
5
 * Copyright (c) 1988, 1993, 1994
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/resource.h>
34
#include <sys/stat.h>
35
#include <sys/time.h>
36
#include <sys/uio.h>
37
38
#include <err.h>
39
#include <errno.h>
40
#include <fcntl.h>
41
#include <paths.h>
42
#include <pwd.h>
43
#include <signal.h>
44
#include <stdio.h>
45
#include <stdlib.h>
46
#include <string.h>
47
#include <unistd.h>
48
#include <util.h>
49
50
#include "chpass.h"
51
52
extern char *__progname;
53
54
enum { NEWSH, LOADENTRY, EDITENTRY } op;
55
uid_t uid;
56
57
void	baduser(void);
58
void	kbintr(int);
59
void	usage(void);
60
61
int
62
main(int argc, char *argv[])
63
{
64
	struct passwd *pw = NULL, *opw = NULL, lpw;
65
	int i, ch, pfd, tfd, dfd;
66
	char *tz, *arg = NULL;
67
	sigset_t fullset;
68
69
	/* We need to use the system timezone for date conversions. */
70
	if ((tz = getenv("TZ")) != NULL) {
71
	    unsetenv("TZ");
72
	    tzset();
73
	    setenv("TZ", tz, 1);
74
	}
75
76
	op = EDITENTRY;
77
	while ((ch = getopt(argc, argv, "a:s:")) != -1)
78
		switch(ch) {
79
		case 'a':
80
			op = LOADENTRY;
81
			arg = optarg;
82
			break;
83
		case 's':
84
			op = NEWSH;
85
			arg = optarg;
86
			break;
87
		case '?':
88
		default:
89
			usage();
90
		}
91
	argc -= optind;
92
	argv += optind;
93
94
	uid = getuid();
95
96
	if (op == EDITENTRY || op == NEWSH)
97
		switch(argc) {
98
		case 0:
99
			pw = getpwuid_shadow(uid);
100
			if (!pw)
101
				errx(1, "unknown user: uid %u", uid);
102
			break;
103
		case 1:
104
			pw = getpwnam_shadow(*argv);
105
			if (!pw)
106
				errx(1, "unknown user: %s", *argv);
107
			if (uid && uid != pw->pw_uid)
108
				baduser();
109
			break;
110
		default:
111
			usage();
112
		}
113
114
	if (op == LOADENTRY) {
115
		if (argc != 0)
116
			errx(1, "option -a does not accept user argument");
117
		if (uid)
118
			baduser();
119
		pw = &lpw;
120
		if (!pw_scan(arg, pw, NULL))
121
			exit(1);
122
		opw = getpwnam_shadow(pw->pw_name);
123
	}
124
	if (opw == NULL && (opw = pw_dup(pw)) == NULL)
125
		err(1, NULL);
126
127
	/* Edit the user passwd information if requested. */
128
	if (op == EDITENTRY) {
129
		char tempname[] = _PATH_VARTMP "pw.XXXXXXXXXX";
130
		int edit_status;
131
132
		if ((pw = pw_dup(pw)) == NULL)
133
			pw_error(NULL, 1, 1);
134
		dfd = mkostemp(tempname, O_CLOEXEC);
135
		if (dfd == -1)
136
			pw_error(tempname, 1, 1);
137
		display(tempname, dfd, pw);
138
139
		if (pledge("stdio rpath wpath cpath id proc exec",
140
		    NULL) == -1)
141
			err(1, "pledge");
142
143
		edit_status = edit(tempname, pw);
144
		close(dfd);
145
		unlink(tempname);
146
147
		switch (edit_status) {
148
		case EDIT_OK:
149
			break;
150
		case EDIT_NOCHANGE:
151
			pw_error(NULL, 0, 0);
152
			break;
153
		case EDIT_ERROR:
154
		default:
155
			pw_error(tempname, 1, 1);
156
			break;
157
		}
158
	}
159
160
	if (op == NEWSH) {
161
		if (pledge("stdio rpath wpath cpath id proc exec",
162
		    NULL) == -1)
163
			err(1, "pledge");
164
165
		/* protect p_shell -- it thinks NULL is /bin/sh */
166
		if (!arg[0])
167
			usage();
168
		if (p_shell(arg, pw, NULL))
169
			pw_error(NULL, 0, 1);
170
	}
171
172
	/* Drop user's real uid and block all signals to avoid a DoS. */
173
	setuid(0);
174
	sigfillset(&fullset);
175
	sigdelset(&fullset, SIGINT);
176
	sigprocmask(SIG_BLOCK, &fullset, NULL);
177
178
	if (pledge("stdio rpath wpath cpath proc exec flock", NULL) == -1)
179
		err(1, "pledge");
180
181
	/* Get the passwd lock file and open the passwd file for reading. */
182
	pw_init();
183
	for (i = 1; (tfd = pw_lock(0)) == -1; i++) {
184
		if (i == 4)
185
			(void)fputs("Attempting to lock password file, "
186
			    "please wait or press ^C to abort", stderr);
187
		(void)signal(SIGINT, kbintr);
188
		if (i % 16 == 0)
189
			fputc('.', stderr);
190
		usleep(250000);
191
		(void)signal(SIGINT, SIG_IGN);
192
	}
193
	if (i >= 4)
194
		fputc('\n', stderr);
195
	pfd = open(_PATH_MASTERPASSWD, O_RDONLY|O_CLOEXEC, 0);
196
	if (pfd == -1)
197
		pw_error(_PATH_MASTERPASSWD, 1, 1);
198
199
	/* Copy the passwd file to the lock file, updating pw. */
200
	pw_copy(pfd, tfd, pw, opw);
201
202
	/* If username changed we need to rebuild the entire db. */
203
	arg = !strcmp(opw->pw_name, pw->pw_name) ? pw->pw_name : NULL;
204
205
	/* Now finish the passwd file update. */
206
	if (pw_mkdb(arg, 0) == -1)
207
		pw_error(NULL, 0, 1);
208
	exit(0);
209
}
210
211
void
212
baduser(void)
213
{
214
215
	errx(1, "%s", strerror(EACCES));
216
}
217
218
/* ARGSUSED */
219
void
220
kbintr(int signo)
221
{
222
	struct iovec iv[5];
223
224
	iv[0].iov_base = "\n";
225
	iv[0].iov_len = 1;
226
	iv[1].iov_base = __progname;
227
	iv[1].iov_len = strlen(__progname);
228
	iv[2].iov_base = ": ";
229
	iv[2].iov_len = 2;
230
	iv[3].iov_base = _PATH_MASTERPASSWD;
231
	iv[3].iov_len = sizeof(_PATH_MASTERPASSWD) - 1;
232
	iv[4].iov_base = " unchanged\n";
233
	iv[4].iov_len = 11;
234
	writev(STDERR_FILENO, iv, 5);
235
236
	_exit(1);
237
}
238
239
void
240
usage(void)
241
{
242
243
	(void)fprintf(stderr, "usage: %s [-s newshell] [user]\n", __progname);
244
	(void)fprintf(stderr, "       %s -a list\n", __progname);
245
	exit(1);
246
}