Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.bin/chpass/chpass.c
34677 views
1
/*-
2
* SPDX-License-Identifier: BSD-4-Clause
3
*
4
* Copyright (c) 1988, 1993, 1994
5
* The Regents of the University of California. All rights reserved.
6
* Copyright (c) 2002 Networks Associates Technology, Inc.
7
* All rights reserved.
8
*
9
* Portions of this software were developed for the FreeBSD Project by
10
* ThinkSec AS and NAI Labs, the Security Research Division of Network
11
* Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
12
* ("CBOSS"), as part of the DARPA CHATS research program.
13
*
14
* Redistribution and use in source and binary forms, with or without
15
* modification, are permitted provided that the following conditions
16
* are met:
17
* 1. Redistributions of source code must retain the above copyright
18
* notice, this list of conditions and the following disclaimer.
19
* 2. Redistributions in binary form must reproduce the above copyright
20
* notice, this list of conditions and the following disclaimer in the
21
* documentation and/or other materials provided with the distribution.
22
* 3. All advertising materials mentioning features or use of this software
23
* must display the following acknowledgement:
24
* This product includes software developed by the University of
25
* California, Berkeley and its contributors.
26
* 4. Neither the name of the University nor the names of its contributors
27
* may be used to endorse or promote products derived from this software
28
* without specific prior written permission.
29
*
30
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40
* SUCH DAMAGE.
41
*/
42
43
#include <sys/param.h>
44
45
#include <err.h>
46
#include <errno.h>
47
#include <pwd.h>
48
#include <stdio.h>
49
#include <stdlib.h>
50
#include <string.h>
51
#include <unistd.h>
52
#ifdef YP
53
#include <ypclnt.h>
54
#endif
55
56
#include <pw_scan.h>
57
#include <libutil.h>
58
59
#include "chpass.h"
60
61
int master_mode;
62
63
static void baduser(void);
64
static void usage(void);
65
66
int
67
main(int argc, char *argv[])
68
{
69
enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op;
70
struct passwd lpw, *old_pw, *pw;
71
int ch, pfd, tfd;
72
const char *password;
73
char *arg = NULL, *cryptpw;
74
uid_t uid;
75
#ifdef YP
76
struct ypclnt *ypclnt;
77
const char *yp_domain = NULL, *yp_host = NULL;
78
#endif
79
80
pw = old_pw = NULL;
81
op = EDITENTRY;
82
#ifdef YP
83
while ((ch = getopt(argc, argv, "a:p:s:e:d:h:loy")) != -1)
84
#else
85
while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1)
86
#endif
87
switch (ch) {
88
case 'a':
89
op = LOADENTRY;
90
arg = optarg;
91
break;
92
case 's':
93
op = NEWSH;
94
arg = optarg;
95
break;
96
case 'p':
97
op = NEWPW;
98
arg = optarg;
99
break;
100
case 'e':
101
op = NEWEXP;
102
arg = optarg;
103
break;
104
#ifdef YP
105
case 'd':
106
yp_domain = optarg;
107
break;
108
case 'h':
109
yp_host = optarg;
110
break;
111
case 'l':
112
case 'o':
113
case 'y':
114
/* compatibility */
115
break;
116
#endif
117
case '?':
118
default:
119
usage();
120
}
121
122
argc -= optind;
123
argv += optind;
124
125
if (argc > 1)
126
usage();
127
128
uid = getuid();
129
130
if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) {
131
if (argc == 0) {
132
if ((pw = getpwuid(uid)) == NULL)
133
errx(1, "unknown user: uid %lu",
134
(unsigned long)uid);
135
} else {
136
if ((pw = getpwnam(*argv)) == NULL)
137
errx(1, "unknown user: %s", *argv);
138
if (uid != 0 && uid != pw->pw_uid)
139
baduser();
140
}
141
142
/* Make a copy for later verification */
143
if ((pw = pw_dup(pw)) == NULL ||
144
(old_pw = pw_dup(pw)) == NULL)
145
err(1, "pw_dup");
146
}
147
148
#ifdef YP
149
if (pw != NULL && (pw->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
150
ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
151
master_mode = (ypclnt != NULL &&
152
ypclnt_connect(ypclnt) != -1 &&
153
ypclnt_havepasswdd(ypclnt) == 1);
154
ypclnt_free(ypclnt);
155
} else
156
#endif
157
master_mode = (uid == 0);
158
159
if (op == NEWSH) {
160
/* protect p_shell -- it thinks NULL is /bin/sh */
161
if (!arg[0])
162
usage();
163
if (p_shell(arg, pw, (ENTRY *)NULL) == -1)
164
exit(1);
165
}
166
167
if (op == NEWEXP) {
168
if (uid) /* only root can change expire */
169
baduser();
170
if (p_expire(arg, pw, (ENTRY *)NULL) == -1)
171
exit(1);
172
}
173
174
if (op == LOADENTRY) {
175
if (uid)
176
baduser();
177
pw = &lpw;
178
old_pw = NULL;
179
if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER))
180
exit(1);
181
}
182
183
if (op == NEWPW) {
184
if (uid)
185
baduser();
186
187
if (strchr(arg, ':'))
188
errx(1, "invalid format for password");
189
pw->pw_passwd = arg;
190
}
191
192
if (op == EDITENTRY) {
193
/*
194
* We don't really need pw_*() here, but pw_edit() (used
195
* by edit()) is just too useful...
196
*/
197
if (pw_init(NULL, NULL))
198
err(1, "pw_init()");
199
if ((tfd = pw_tmp(-1)) == -1) {
200
pw_fini();
201
err(1, "pw_tmp()");
202
}
203
free(pw);
204
pw = edit(pw_tempname(), old_pw);
205
pw_fini();
206
if (pw == NULL)
207
err(1, "edit()");
208
/*
209
* pw_equal does not check for crypted passwords, so we
210
* should do it explicitly
211
*/
212
if (pw_equal(old_pw, pw) &&
213
strcmp(old_pw->pw_passwd, pw->pw_passwd) == 0)
214
errx(0, "user information unchanged");
215
}
216
217
if (old_pw && !master_mode) {
218
password = getpass("Password: ");
219
cryptpw = crypt(password, old_pw->pw_passwd);
220
if (cryptpw == NULL || strcmp(cryptpw, old_pw->pw_passwd) != 0)
221
baduser();
222
} else {
223
password = "";
224
}
225
226
if (old_pw != NULL)
227
pw->pw_fields |= (old_pw->pw_fields & _PWF_SOURCE);
228
switch (pw->pw_fields & _PWF_SOURCE) {
229
#ifdef YP
230
case _PWF_NIS:
231
ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
232
if (ypclnt == NULL) {
233
warnx("ypclnt_new failed");
234
exit(1);
235
}
236
if (ypclnt_connect(ypclnt) == -1 ||
237
ypclnt_passwd(ypclnt, pw, password) == -1) {
238
warnx("%s", ypclnt->error);
239
ypclnt_free(ypclnt);
240
exit(1);
241
}
242
ypclnt_free(ypclnt);
243
errx(0, "NIS user information updated");
244
#endif /* YP */
245
case 0:
246
case _PWF_FILES:
247
if (pw_init(NULL, NULL))
248
err(1, "pw_init()");
249
if ((pfd = pw_lock()) == -1) {
250
pw_fini();
251
err(1, "pw_lock()");
252
}
253
if ((tfd = pw_tmp(-1)) == -1) {
254
pw_fini();
255
err(1, "pw_tmp()");
256
}
257
if (pw_copy(pfd, tfd, pw, old_pw) == -1) {
258
pw_fini();
259
err(1, "pw_copy");
260
}
261
if (pw_mkdb(pw->pw_name) == -1) {
262
pw_fini();
263
err(1, "pw_mkdb()");
264
}
265
pw_fini();
266
errx(0, "user information updated");
267
break;
268
default:
269
errx(1, "unsupported passwd source");
270
}
271
}
272
273
static void
274
baduser(void)
275
{
276
277
errx(1, "%s", strerror(EACCES));
278
}
279
280
static void
281
usage(void)
282
{
283
284
(void)fprintf(stderr,
285
"usage: chpass%s %s [user]\n",
286
#ifdef YP
287
" [-d domain] [-h host]",
288
#else
289
"",
290
#endif
291
"[-a list] [-p encpass] [-s shell] [-e mmm dd yy]");
292
exit(1);
293
}
294
295