]> git.saurik.com Git - apple/system_cmds.git/blame - chpass.tproj/chpass.c
system_cmds-880.100.5.tar.gz
[apple/system_cmds.git] / chpass.tproj / chpass.c
CommitLineData
1815bff5 1/*
cf37c299 2 * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
1815bff5
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
cf37c299 5 *
34d340d7
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
cf37c299 12 *
1815bff5
A
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
34d340d7
A
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
cf37c299 20 *
1815bff5
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*-
34d340d7
A
24 * Copyright (c) 1988, 1993, 1994
25 * The Regents of the University of California. All rights reserved.
26 * Copyright (c) 2002 Networks Associates Technology, Inc.
27 * All rights reserved.
28 *
29 * Portions of this software were developed for the FreeBSD Project by
30 * ThinkSec AS and NAI Labs, the Security Research Division of Network
31 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
32 * ("CBOSS"), as part of the DARPA CHATS research program.
1815bff5
A
33 *
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
36 * are met:
37 * 1. Redistributions of source code must retain the above copyright
38 * notice, this list of conditions and the following disclaimer.
39 * 2. Redistributions in binary form must reproduce the above copyright
40 * notice, this list of conditions and the following disclaimer in the
41 * documentation and/or other materials provided with the distribution.
42 * 3. All advertising materials mentioning features or use of this software
43 * must display the following acknowledgement:
34d340d7
A
44 * This product includes software developed by the University of
45 * California, Berkeley and its contributors.
1815bff5
A
46 * 4. Neither the name of the University nor the names of its contributors
47 * may be used to endorse or promote products derived from this software
48 * without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * SUCH DAMAGE.
61 */
62
34d340d7
A
63#if 0
64#if 0
1815bff5 65#ifndef lint
34d340d7 66static const char copyright[] =
1815bff5
A
67"@(#) Copyright (c) 1988, 1993, 1994\n\
68 The Regents of the University of California. All rights reserved.\n";
69#endif /* not lint */
70
34d340d7
A
71#ifndef lint
72static char sccsid[] = "@(#)chpass.c 8.4 (Berkeley) 4/2/94";
73#endif /* not lint */
74#endif
75#include <sys/cdefs.h>
76__FBSDID("$FreeBSD: src/usr.bin/chpass/chpass.c,v 1.27.8.1 2006/09/29 06:13:20 marck Exp $");
77#endif
78
1815bff5
A
79#include <sys/param.h>
80#include <sys/stat.h>
81#include <sys/signal.h>
82#include <sys/time.h>
83#include <sys/resource.h>
84
1815bff5
A
85#include <err.h>
86#include <errno.h>
1815bff5
A
87#include <pwd.h>
88#include <stdio.h>
89#include <stdlib.h>
90#include <string.h>
91#include <unistd.h>
34d340d7
A
92#ifdef YP
93#include <ypclnt.h>
94#endif
1815bff5 95
34d340d7 96#ifndef OPEN_DIRECTORY
1815bff5 97#include <pw_scan.h>
34d340d7
A
98#include <libutil.h>
99#endif
1815bff5
A
100
101#include "chpass.h"
c3a08f59 102
34d340d7 103int master_mode;
1815bff5 104
34d340d7
A
105#ifdef OPEN_DIRECTORY
106#include "open_directory.h"
107char *progname = NULL;
108CFStringRef DSPath = NULL;
109#endif /* OPEN_DIRECTORY */
1815bff5 110
34d340d7
A
111static void baduser(void);
112static void usage(void);
1815bff5
A
113
114int
34d340d7 115main(int argc, char *argv[])
1815bff5 116{
34d340d7
A
117 enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op;
118#ifndef OPEN_DIRECTORY
119 struct passwd lpw, *old_pw, *pw;
1815bff5 120 int ch, pfd, tfd;
34d340d7
A
121 const char *password;
122#else
123 struct passwd *old_pw, *pw;
124 int ch, tfd;
125 char tfn[MAXPATHLEN];
8459d725 126 char *tmpdir;
34d340d7
A
127#endif
128 char *arg = NULL;
129 uid_t uid;
130#ifdef YP
131 struct ypclnt *ypclnt;
132 const char *yp_domain = NULL, *yp_host = NULL;
133#endif
134#ifdef OPEN_DIRECTORY
135 CFStringRef username = NULL;
136 CFStringRef authname = NULL;
137 CFStringRef location = NULL;
cf37c299 138
34d340d7
A
139 progname = strrchr(argv[0], '/');
140 if (progname) progname++;
141 else progname = argv[0];
142#endif /* OPEN_DIRECTORY */
1815bff5 143
34d340d7 144 pw = old_pw = NULL;
1815bff5 145 op = EDITENTRY;
34d340d7
A
146#ifdef OPEN_DIRECTORY
147 while ((ch = getopt(argc, argv, "a:s:l:u:")) != -1)
148#else /* OPEN_DIRECTORY */
149#ifdef YP
150 while ((ch = getopt(argc, argv, "a:p:s:e:d:h:loy")) != -1)
151#else
152 while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1)
153#endif
154#endif /* OPEN_DIRECTORY */
155 switch (ch) {
1815bff5
A
156 case 'a':
157 op = LOADENTRY;
158 arg = optarg;
159 break;
160 case 's':
161 op = NEWSH;
162 arg = optarg;
163 break;
34d340d7
A
164#ifndef OPEN_DIRECTORY
165 case 'p':
166 op = NEWPW;
167 arg = optarg;
168 break;
169 case 'e':
170 op = NEWEXP;
171 arg = optarg;
172 break;
173#ifdef YP
174 case 'd':
175 yp_domain = optarg;
176 break;
177 case 'h':
178 yp_host = optarg;
179 break;
180 case 'l':
181 case 'o':
182 case 'y':
183 /* compatibility */
184 break;
185#endif
186#else /* OPEN_DIRECTORY */
187 case 'l':
188 location = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
189 break;
190 case 'u':
191 authname = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
192 break;
193#endif
1815bff5
A
194 case '?':
195 default:
196 usage();
197 }
34d340d7 198
1815bff5
A
199 argc -= optind;
200 argv += optind;
201
34d340d7
A
202 if (argc > 1)
203 usage();
204
1815bff5
A
205 uid = getuid();
206
34d340d7
A
207 if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) {
208 if (argc == 0) {
209 if ((pw = getpwuid(uid)) == NULL)
210 errx(1, "unknown user: uid %lu",
211 (unsigned long)uid);
212 } else {
213 if ((pw = getpwnam(*argv)) == NULL)
1815bff5 214 errx(1, "unknown user: %s", *argv);
34d340d7
A
215#ifndef OPEN_DIRECTORY
216 if (uid != 0 && uid != pw->pw_uid)
1815bff5 217 baduser();
34d340d7 218#endif
1815bff5
A
219 }
220
34d340d7
A
221#ifndef OPEN_DIRECTORY
222 /* Make a copy for later verification */
223 if ((pw = pw_dup(pw)) == NULL ||
224 (old_pw = pw_dup(pw)) == NULL)
225 err(1, "pw_dup");
226#endif
227 }
09fd88e4 228
34d340d7
A
229#if OPEN_DIRECTORY
230 master_mode = (uid == 0);
09fd88e4 231
34d340d7
A
232 /*
233 * Find the user record and copy its details.
234 */
235 username = CFStringCreateWithCString(NULL, pw->pw_name, kCFStringEncodingUTF8);
236
237 if (strcmp(progname, "chsh") == 0 || op == NEWSH) {
238 cfprintf(stderr, "Changing shell for %@.\n", username);
239 } else if (strcmp(progname, "chfn") == 0) {
240 cfprintf(stderr, "Changing finger information for %@.\n", username);
241 } else if (strcmp(progname, "chpass") == 0) {
242 cfprintf(stderr, "Changing account information for %@.\n", username);
c3a08f59 243 }
cf37c299 244
34d340d7
A
245 /*
246 * odGetUser updates DSPath global variable, performs authentication
247 * if necessary, and extracts the attributes.
248 */
249 CFDictionaryRef attrs_orig = NULL;
250 CFDictionaryRef attrs = NULL;
251 ODRecordRef rec = odGetUser(location, authname, username, &attrs_orig);
cf37c299 252
34d340d7
A
253 if (!rec || !attrs_orig) exit(1);
254#endif /* OPEN_DIRECTORY */
255
256#ifdef YP
257 if (pw != NULL && (pw->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
258 ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
259 master_mode = (ypclnt != NULL &&
260 ypclnt_connect(ypclnt) != -1 &&
261 ypclnt_havepasswdd(ypclnt) == 1);
262 ypclnt_free(ypclnt);
263 } else
264#endif
265 master_mode = (uid == 0);
c3a08f59 266
1815bff5
A
267 if (op == NEWSH) {
268 /* protect p_shell -- it thinks NULL is /bin/sh */
269 if (!arg[0])
270 usage();
34d340d7
A
271 if (p_shell(arg, pw, (ENTRY *)NULL) == -1)
272 exit(1);
273#ifdef OPEN_DIRECTORY
274 else {
275 ENTRY* ep;
cf37c299 276
34d340d7 277 setrestricted(attrs_orig);
cf37c299 278
34d340d7
A
279 for (ep = list; ep->prompt; ep++) {
280 if (strncasecmp(ep->prompt, "shell", ep->len) == 0) {
281 if (!ep->restricted) {
282 CFStringRef shell = CFStringCreateWithCString(NULL, arg, kCFStringEncodingUTF8);
283 if (shell) {
284 attrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
ef8ad44b 285 if (attrs) CFDictionarySetValue((CFMutableDictionaryRef)attrs, kODAttributeTypeUserShell, shell);
34d340d7
A
286 CFRelease(shell);
287 }
288 } else {
289 warnx("shell is restricted");
290 exit(1);
291 }
292 }
293 }
294 }
295#endif
1815bff5
A
296 }
297
34d340d7
A
298#ifndef OPEN_DIRECTORY
299 if (op == NEWEXP) {
300 if (uid) /* only root can change expire */
301 baduser();
302 if (p_expire(arg, pw, (ENTRY *)NULL) == -1)
303 exit(1);
304 }
305#endif
306
1815bff5
A
307 if (op == LOADENTRY) {
308 if (uid)
309 baduser();
34d340d7
A
310#ifdef OPEN_DIRECTORY
311 warnx("-a is not supported for Open Directory.");
312 exit(1);
313#else
1815bff5 314 pw = &lpw;
34d340d7
A
315 old_pw = NULL;
316 if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER))
1815bff5 317 exit(1);
34d340d7 318#endif /* OPEN_DIRECTORY */
1815bff5
A
319 }
320
34d340d7
A
321#ifndef OPEN_DIRECTORY
322 if (op == NEWPW) {
323 if (uid)
324 baduser();
1815bff5 325
34d340d7
A
326 if (strchr(arg, ':'))
327 errx(1, "invalid format for password");
328 pw->pw_passwd = arg;
1815bff5 329 }
34d340d7 330#endif /* OPEN_DIRECTORY */
1815bff5 331
34d340d7
A
332 if (op == EDITENTRY) {
333#ifdef OPEN_DIRECTORY
334 setrestricted(attrs_orig);
8459d725
A
335 tmpdir = getenv("TMPDIR");
336 if (!tmpdir)
337 tmpdir = P_tmpdir; // defined in the system headers, defaults to /tmp
338 snprintf(tfn, sizeof(tfn), "%s/%s.XXXXXX", tmpdir, progname);
34d340d7
A
339 if ((tfd = mkstemp(tfn)) == -1)
340 err(1, "%s", tfn);
341 attrs = (CFMutableDictionaryRef)edit(tfn, attrs_orig);
342 (void)unlink(tfn);
343#else
344 /*
345 * We don't really need pw_*() here, but pw_edit() (used
346 * by edit()) is just too useful...
347 */
348 if (pw_init(NULL, NULL))
349 err(1, "pw_init()");
350 if ((tfd = pw_tmp(-1)) == -1) {
351 pw_fini();
352 err(1, "pw_tmp()");
353 }
354 free(pw);
355 pw = edit(pw_tempname(), old_pw);
356 pw_fini();
357 if (pw == NULL)
358 err(1, "edit()");
cf37c299 359 /*
34d340d7
A
360 * pw_equal does not check for crypted passwords, so we
361 * should do it explicitly
362 */
cf37c299 363 if (pw_equal(old_pw, pw) &&
34d340d7
A
364 strcmp(old_pw->pw_passwd, pw->pw_passwd) == 0)
365 errx(0, "user information unchanged");
366#endif /* OPEN_DIRECTORY */
c3a08f59 367 }
1815bff5 368
34d340d7
A
369#ifndef OPEN_DIRECTORY
370 if (old_pw && !master_mode) {
371 password = getpass("Password: ");
372 if (strcmp(crypt(password, old_pw->pw_passwd),
373 old_pw->pw_passwd) != 0)
374 baduser();
375 } else {
376 password = "";
2fc1e207 377 }
34d340d7
A
378#endif
379
380#ifdef OPEN_DIRECTORY
381 odUpdateUser(rec, attrs_orig, attrs);
cf37c299 382
34d340d7 383 if (rec) CFRelease(rec);
2fc1e207 384
34d340d7 385 exit(0);
2fc1e207 386 return 0;
34d340d7
A
387#else /* OPEN_DIRECTORY */
388 exit(0);
389 if (old_pw != NULL)
390 pw->pw_fields |= (old_pw->pw_fields & _PWF_SOURCE);
391 switch (pw->pw_fields & _PWF_SOURCE) {
392#ifdef YP
393 case _PWF_NIS:
394 ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
395 if (ypclnt == NULL ||
396 ypclnt_connect(ypclnt) == -1 ||
397 ypclnt_passwd(ypclnt, pw, password) == -1) {
398 warnx("%s", ypclnt->error);
399 ypclnt_free(ypclnt);
400 exit(1);
401 }
402 ypclnt_free(ypclnt);
403 errx(0, "NIS user information updated");
404#endif /* YP */
405 case 0:
406 case _PWF_FILES:
407 if (pw_init(NULL, NULL))
408 err(1, "pw_init()");
409 if ((pfd = pw_lock()) == -1) {
410 pw_fini();
411 err(1, "pw_lock()");
412 }
413 if ((tfd = pw_tmp(-1)) == -1) {
414 pw_fini();
415 err(1, "pw_tmp()");
416 }
417 if (pw_copy(pfd, tfd, pw, old_pw) == -1) {
418 pw_fini();
419 err(1, "pw_copy");
420 }
421 if (pw_mkdb(pw->pw_name) == -1) {
422 pw_fini();
423 err(1, "pw_mkdb()");
424 }
425 pw_fini();
426 errx(0, "user information updated");
427 break;
428 default:
429 errx(1, "unsupported passwd source");
430 }
431#endif /* OPEN_DIRECTORY */
2fc1e207 432}
2fc1e207 433
34d340d7
A
434static void
435baduser(void)
1815bff5 436{
1815bff5
A
437 errx(1, "%s", strerror(EACCES));
438}
439
34d340d7
A
440static void
441usage(void)
1815bff5 442{
34d340d7
A
443 (void)fprintf(stderr,
444 "usage: chpass%s %s [user]\n",
445#ifdef OPEN_DIRECTORY
446 "",
447 "[-l location] [-u authname] [-s shell]");
448#else /* OPEN_DIRECTORY */
449#ifdef YP
450 " [-d domain] [-h host]",
451#else
452 "",
453#endif
454 "[-a list] [-p encpass] [-s shell] [-e mmm dd yy]");
455#endif /* OPEN_DIRECTORY */
1815bff5
A
456 exit(1);
457}