1 /* $OpenBSD: chpass.c,v 1.50 2023/03/08 04:43:10 guenther 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
main(int argc,char * argv[])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 default:
88 usage();
89 }
90 argc -= optind;
91 argv += optind;
92
93 uid = getuid();
94
95 if (op == EDITENTRY || op == NEWSH)
96 switch(argc) {
97 case 0:
98 pw = getpwuid_shadow(uid);
99 if (!pw)
100 errx(1, "unknown user: uid %u", uid);
101 break;
102 case 1:
103 pw = getpwnam_shadow(*argv);
104 if (!pw)
105 errx(1, "unknown user: %s", *argv);
106 if (uid && uid != pw->pw_uid)
107 baduser();
108 break;
109 default:
110 usage();
111 }
112
113 if (op == LOADENTRY) {
114 if (argc != 0)
115 errx(1, "option -a does not accept user argument");
116 if (uid)
117 baduser();
118 pw = &lpw;
119 if (!pw_scan(arg, pw, NULL))
120 exit(1);
121 opw = getpwnam_shadow(pw->pw_name);
122 }
123 if (opw == NULL && (opw = pw_dup(pw)) == NULL)
124 err(1, NULL);
125
126 /* Edit the user passwd information if requested. */
127 if (op == EDITENTRY) {
128 char tempname[] = _PATH_VARTMP "pw.XXXXXXXXXX";
129 int edit_status;
130
131 if ((pw = pw_dup(pw)) == NULL)
132 pw_error(NULL, 1, 1);
133 dfd = mkostemp(tempname, O_CLOEXEC);
134 if (dfd == -1)
135 pw_error(tempname, 1, 1);
136 display(tempname, dfd, pw);
137
138 if (unveil(_PATH_BSHELL, "x") == -1)
139 err(1, "unveil %s", _PATH_BSHELL);
140 if (unveil(_PATH_SHELLS, "r") == -1)
141 err(1, "unveil %s", _PATH_SHELLS);
142 if (unveil(tempname, "rc") == -1)
143 err(1, "unveil %s", tempname);
144 if (pledge("stdio rpath wpath cpath id proc exec unveil",
145 NULL) == -1)
146 err(1, "pledge");
147
148 edit_status = edit(tempname, pw);
149 close(dfd);
150 unlink(tempname);
151
152 switch (edit_status) {
153 case EDIT_OK:
154 break;
155 case EDIT_NOCHANGE:
156 pw_error(NULL, 0, 0);
157 break;
158 case EDIT_ERROR:
159 default:
160 pw_error(tempname, 1, 1);
161 break;
162 }
163 }
164
165 if (op == NEWSH) {
166 if (unveil(_PATH_SHELLS, "r") == -1)
167 err(1, "unveil %s", _PATH_SHELLS);
168 if (pledge("stdio rpath wpath cpath id proc exec unveil",
169 NULL) == -1)
170 err(1, "pledge");
171
172 /* protect p_shell -- it thinks NULL is /bin/sh */
173 if (!arg[0])
174 usage();
175 if (p_shell(arg, pw, NULL))
176 pw_error(NULL, 0, 1);
177 }
178
179 /* Drop user's real uid and block all signals to avoid a DoS. */
180 setuid(0);
181 sigfillset(&fullset);
182 sigdelset(&fullset, SIGINT);
183 sigprocmask(SIG_BLOCK, &fullset, NULL);
184
185 if (unveil(_PATH_MASTERPASSWD_LOCK, "rwc") == -1)
186 err(1, "unveil %s", _PATH_MASTERPASSWD_LOCK);
187 if (unveil(_PATH_MASTERPASSWD, "r") == -1)
188 err(1, "unveil %s", _PATH_MASTERPASSWD);
189 if (unveil(_PATH_PWD_MKDB, "x") == -1)
190 err(1, "unveil %s", _PATH_PWD_MKDB);
191 if (pledge("stdio rpath wpath cpath proc exec", NULL) == -1)
192 err(1, "pledge");
193
194 /* Get the passwd lock file and open the passwd file for reading. */
195 pw_init();
196 for (i = 1; (tfd = pw_lock(0)) == -1; i++) {
197 if (i == 4)
198 (void)fputs("Attempting to lock password file, "
199 "please wait or press ^C to abort", stderr);
200 (void)signal(SIGINT, kbintr);
201 if (i % 16 == 0)
202 fputc('.', stderr);
203 usleep(250000);
204 (void)signal(SIGINT, SIG_IGN);
205 }
206 if (i >= 4)
207 fputc('\n', stderr);
208 pfd = open(_PATH_MASTERPASSWD, O_RDONLY|O_CLOEXEC);
209 if (pfd == -1)
210 pw_error(_PATH_MASTERPASSWD, 1, 1);
211
212 /* Copy the passwd file to the lock file, updating pw. */
213 pw_copy(pfd, tfd, pw, opw);
214
215 /* If username changed we need to rebuild the entire db. */
216 arg = !strcmp(opw->pw_name, pw->pw_name) ? pw->pw_name : NULL;
217
218 /* Now finish the passwd file update. */
219 if (pw_mkdb(arg, 0) == -1)
220 pw_error(NULL, 0, 1);
221 exit(0);
222 }
223
224 void
baduser(void)225 baduser(void)
226 {
227
228 errx(1, "%s", strerror(EACCES));
229 }
230
231 void
kbintr(int signo)232 kbintr(int signo)
233 {
234 dprintf(STDERR_FILENO, "\n%s: %s unchanged\n",
235 __progname, _PATH_MASTERPASSWD);
236 _exit(1);
237 }
238
239 void
usage(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 }
247