]> git.saurik.com Git - apple/system_cmds.git/blame - chpass.tproj/chpass.c
system_cmds-496.tar.gz
[apple/system_cmds.git] / chpass.tproj / chpass.c
CommitLineData
1815bff5 1/*
34d340d7 2 * Copyright (c) 1999-2006 Apple Computer, Inc. All rights reserved.
1815bff5
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
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.
1815bff5
A
12 *
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.
1815bff5
A
20 *
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];
126#endif
127 char *arg = NULL;
128 uid_t uid;
129#ifdef YP
130 struct ypclnt *ypclnt;
131 const char *yp_domain = NULL, *yp_host = NULL;
132#endif
133#ifdef OPEN_DIRECTORY
134 CFStringRef username = NULL;
135 CFStringRef authname = NULL;
136 CFStringRef location = NULL;
137
138 progname = strrchr(argv[0], '/');
139 if (progname) progname++;
140 else progname = argv[0];
141#endif /* OPEN_DIRECTORY */
1815bff5 142
34d340d7 143 pw = old_pw = NULL;
1815bff5 144 op = EDITENTRY;
34d340d7
A
145#ifdef OPEN_DIRECTORY
146 while ((ch = getopt(argc, argv, "a:s:l:u:")) != -1)
147#else /* OPEN_DIRECTORY */
148#ifdef YP
149 while ((ch = getopt(argc, argv, "a:p:s:e:d:h:loy")) != -1)
150#else
151 while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1)
152#endif
153#endif /* OPEN_DIRECTORY */
154 switch (ch) {
1815bff5
A
155 case 'a':
156 op = LOADENTRY;
157 arg = optarg;
158 break;
159 case 's':
160 op = NEWSH;
161 arg = optarg;
162 break;
34d340d7
A
163#ifndef OPEN_DIRECTORY
164 case 'p':
165 op = NEWPW;
166 arg = optarg;
167 break;
168 case 'e':
169 op = NEWEXP;
170 arg = optarg;
171 break;
172#ifdef YP
173 case 'd':
174 yp_domain = optarg;
175 break;
176 case 'h':
177 yp_host = optarg;
178 break;
179 case 'l':
180 case 'o':
181 case 'y':
182 /* compatibility */
183 break;
184#endif
185#else /* OPEN_DIRECTORY */
186 case 'l':
187 location = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
188 break;
189 case 'u':
190 authname = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
191 break;
192#endif
1815bff5
A
193 case '?':
194 default:
195 usage();
196 }
34d340d7 197
1815bff5
A
198 argc -= optind;
199 argv += optind;
200
34d340d7
A
201 if (argc > 1)
202 usage();
203
1815bff5
A
204 uid = getuid();
205
34d340d7
A
206 if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) {
207 if (argc == 0) {
208 if ((pw = getpwuid(uid)) == NULL)
209 errx(1, "unknown user: uid %lu",
210 (unsigned long)uid);
211 } else {
212 if ((pw = getpwnam(*argv)) == NULL)
1815bff5 213 errx(1, "unknown user: %s", *argv);
34d340d7
A
214#ifndef OPEN_DIRECTORY
215 if (uid != 0 && uid != pw->pw_uid)
1815bff5 216 baduser();
34d340d7 217#endif
1815bff5
A
218 }
219
34d340d7
A
220#ifndef OPEN_DIRECTORY
221 /* Make a copy for later verification */
222 if ((pw = pw_dup(pw)) == NULL ||
223 (old_pw = pw_dup(pw)) == NULL)
224 err(1, "pw_dup");
225#endif
226 }
09fd88e4 227
34d340d7
A
228#if OPEN_DIRECTORY
229 master_mode = (uid == 0);
09fd88e4 230
34d340d7
A
231 /*
232 * Find the user record and copy its details.
233 */
234 username = CFStringCreateWithCString(NULL, pw->pw_name, kCFStringEncodingUTF8);
235
236 if (strcmp(progname, "chsh") == 0 || op == NEWSH) {
237 cfprintf(stderr, "Changing shell for %@.\n", username);
238 } else if (strcmp(progname, "chfn") == 0) {
239 cfprintf(stderr, "Changing finger information for %@.\n", username);
240 } else if (strcmp(progname, "chpass") == 0) {
241 cfprintf(stderr, "Changing account information for %@.\n", username);
c3a08f59 242 }
34d340d7
A
243
244 /*
245 * odGetUser updates DSPath global variable, performs authentication
246 * if necessary, and extracts the attributes.
247 */
248 CFDictionaryRef attrs_orig = NULL;
249 CFDictionaryRef attrs = NULL;
250 ODRecordRef rec = odGetUser(location, authname, username, &attrs_orig);
251
252 if (!rec || !attrs_orig) exit(1);
253#endif /* OPEN_DIRECTORY */
254
255#ifdef YP
256 if (pw != NULL && (pw->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
257 ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
258 master_mode = (ypclnt != NULL &&
259 ypclnt_connect(ypclnt) != -1 &&
260 ypclnt_havepasswdd(ypclnt) == 1);
261 ypclnt_free(ypclnt);
262 } else
263#endif
264 master_mode = (uid == 0);
c3a08f59 265
1815bff5
A
266 if (op == NEWSH) {
267 /* protect p_shell -- it thinks NULL is /bin/sh */
268 if (!arg[0])
269 usage();
34d340d7
A
270 if (p_shell(arg, pw, (ENTRY *)NULL) == -1)
271 exit(1);
272#ifdef OPEN_DIRECTORY
273 else {
274 ENTRY* ep;
275
276 setrestricted(attrs_orig);
277
278 for (ep = list; ep->prompt; ep++) {
279 if (strncasecmp(ep->prompt, "shell", ep->len) == 0) {
280 if (!ep->restricted) {
281 CFStringRef shell = CFStringCreateWithCString(NULL, arg, kCFStringEncodingUTF8);
282 if (shell) {
283 attrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
ef8ad44b 284 if (attrs) CFDictionarySetValue((CFMutableDictionaryRef)attrs, kODAttributeTypeUserShell, shell);
34d340d7
A
285 CFRelease(shell);
286 }
287 } else {
288 warnx("shell is restricted");
289 exit(1);
290 }
291 }
292 }
293 }
294#endif
1815bff5
A
295 }
296
34d340d7
A
297#ifndef OPEN_DIRECTORY
298 if (op == NEWEXP) {
299 if (uid) /* only root can change expire */
300 baduser();
301 if (p_expire(arg, pw, (ENTRY *)NULL) == -1)
302 exit(1);
303 }
304#endif
305
1815bff5
A
306 if (op == LOADENTRY) {
307 if (uid)
308 baduser();
34d340d7
A
309#ifdef OPEN_DIRECTORY
310 warnx("-a is not supported for Open Directory.");
311 exit(1);
312#else
1815bff5 313 pw = &lpw;
34d340d7
A
314 old_pw = NULL;
315 if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER))
1815bff5 316 exit(1);
34d340d7 317#endif /* OPEN_DIRECTORY */
1815bff5
A
318 }
319
34d340d7
A
320#ifndef OPEN_DIRECTORY
321 if (op == NEWPW) {
322 if (uid)
323 baduser();
1815bff5 324
34d340d7
A
325 if (strchr(arg, ':'))
326 errx(1, "invalid format for password");
327 pw->pw_passwd = arg;
1815bff5 328 }
34d340d7 329#endif /* OPEN_DIRECTORY */
1815bff5 330
34d340d7
A
331 if (op == EDITENTRY) {
332#ifdef OPEN_DIRECTORY
333 setrestricted(attrs_orig);
a8daac8f 334 snprintf(tfn, sizeof(tfn), "/etc/%s.XXXXXX", progname);
34d340d7
A
335 if ((tfd = mkstemp(tfn)) == -1)
336 err(1, "%s", tfn);
337 attrs = (CFMutableDictionaryRef)edit(tfn, attrs_orig);
338 (void)unlink(tfn);
339#else
340 /*
341 * We don't really need pw_*() here, but pw_edit() (used
342 * by edit()) is just too useful...
343 */
344 if (pw_init(NULL, NULL))
345 err(1, "pw_init()");
346 if ((tfd = pw_tmp(-1)) == -1) {
347 pw_fini();
348 err(1, "pw_tmp()");
349 }
350 free(pw);
351 pw = edit(pw_tempname(), old_pw);
352 pw_fini();
353 if (pw == NULL)
354 err(1, "edit()");
355 /*
356 * pw_equal does not check for crypted passwords, so we
357 * should do it explicitly
358 */
359 if (pw_equal(old_pw, pw) &&
360 strcmp(old_pw->pw_passwd, pw->pw_passwd) == 0)
361 errx(0, "user information unchanged");
362#endif /* OPEN_DIRECTORY */
c3a08f59 363 }
1815bff5 364
34d340d7
A
365#ifndef OPEN_DIRECTORY
366 if (old_pw && !master_mode) {
367 password = getpass("Password: ");
368 if (strcmp(crypt(password, old_pw->pw_passwd),
369 old_pw->pw_passwd) != 0)
370 baduser();
371 } else {
372 password = "";
2fc1e207 373 }
34d340d7
A
374#endif
375
376#ifdef OPEN_DIRECTORY
377 odUpdateUser(rec, attrs_orig, attrs);
2fc1e207 378
34d340d7 379 if (rec) CFRelease(rec);
2fc1e207 380
34d340d7 381 exit(0);
2fc1e207 382 return 0;
34d340d7
A
383#else /* OPEN_DIRECTORY */
384 exit(0);
385 if (old_pw != NULL)
386 pw->pw_fields |= (old_pw->pw_fields & _PWF_SOURCE);
387 switch (pw->pw_fields & _PWF_SOURCE) {
388#ifdef YP
389 case _PWF_NIS:
390 ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
391 if (ypclnt == NULL ||
392 ypclnt_connect(ypclnt) == -1 ||
393 ypclnt_passwd(ypclnt, pw, password) == -1) {
394 warnx("%s", ypclnt->error);
395 ypclnt_free(ypclnt);
396 exit(1);
397 }
398 ypclnt_free(ypclnt);
399 errx(0, "NIS user information updated");
400#endif /* YP */
401 case 0:
402 case _PWF_FILES:
403 if (pw_init(NULL, NULL))
404 err(1, "pw_init()");
405 if ((pfd = pw_lock()) == -1) {
406 pw_fini();
407 err(1, "pw_lock()");
408 }
409 if ((tfd = pw_tmp(-1)) == -1) {
410 pw_fini();
411 err(1, "pw_tmp()");
412 }
413 if (pw_copy(pfd, tfd, pw, old_pw) == -1) {
414 pw_fini();
415 err(1, "pw_copy");
416 }
417 if (pw_mkdb(pw->pw_name) == -1) {
418 pw_fini();
419 err(1, "pw_mkdb()");
420 }
421 pw_fini();
422 errx(0, "user information updated");
423 break;
424 default:
425 errx(1, "unsupported passwd source");
426 }
427#endif /* OPEN_DIRECTORY */
2fc1e207 428}
2fc1e207 429
34d340d7
A
430static void
431baduser(void)
1815bff5
A
432{
433
434 errx(1, "%s", strerror(EACCES));
435}
436
34d340d7
A
437static void
438usage(void)
1815bff5
A
439{
440
34d340d7
A
441 (void)fprintf(stderr,
442 "usage: chpass%s %s [user]\n",
443#ifdef OPEN_DIRECTORY
444 "",
445 "[-l location] [-u authname] [-s shell]");
446#else /* OPEN_DIRECTORY */
447#ifdef YP
448 " [-d domain] [-h host]",
449#else
450 "",
451#endif
452 "[-a list] [-p encpass] [-s shell] [-e mmm dd yy]");
453#endif /* OPEN_DIRECTORY */
1815bff5
A
454 exit(1);
455}