]> git.saurik.com Git - apple/system_cmds.git/blob - chpass.tproj/chpass.c
system_cmds-735.20.1.tar.gz
[apple/system_cmds.git] / chpass.tproj / chpass.c
1 /*
2 * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
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,
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*-
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.
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:
44 * This product includes software developed by the University of
45 * California, Berkeley and its contributors.
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
63 #if 0
64 #if 0
65 #ifndef lint
66 static const char copyright[] =
67 "@(#) Copyright (c) 1988, 1993, 1994\n\
68 The Regents of the University of California. All rights reserved.\n";
69 #endif /* not lint */
70
71 #ifndef lint
72 static 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
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
85 #include <err.h>
86 #include <errno.h>
87 #include <pwd.h>
88 #include <stdio.h>
89 #include <stdlib.h>
90 #include <string.h>
91 #include <unistd.h>
92 #ifdef YP
93 #include <ypclnt.h>
94 #endif
95
96 #ifndef OPEN_DIRECTORY
97 #include <pw_scan.h>
98 #include <libutil.h>
99 #endif
100
101 #include "chpass.h"
102
103 int master_mode;
104
105 #ifdef OPEN_DIRECTORY
106 #include "open_directory.h"
107 char *progname = NULL;
108 CFStringRef DSPath = NULL;
109 #endif /* OPEN_DIRECTORY */
110
111 static void baduser(void);
112 static void usage(void);
113
114 int
115 main(int argc, char *argv[])
116 {
117 enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op;
118 #ifndef OPEN_DIRECTORY
119 struct passwd lpw, *old_pw, *pw;
120 int ch, pfd, tfd;
121 const char *password;
122 #else
123 struct passwd *old_pw, *pw;
124 int ch, tfd;
125 char tfn[MAXPATHLEN];
126 char *tmpdir;
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;
138
139 progname = strrchr(argv[0], '/');
140 if (progname) progname++;
141 else progname = argv[0];
142 #endif /* OPEN_DIRECTORY */
143
144 pw = old_pw = NULL;
145 op = EDITENTRY;
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) {
156 case 'a':
157 op = LOADENTRY;
158 arg = optarg;
159 break;
160 case 's':
161 op = NEWSH;
162 arg = optarg;
163 break;
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
194 case '?':
195 default:
196 usage();
197 }
198
199 argc -= optind;
200 argv += optind;
201
202 if (argc > 1)
203 usage();
204
205 uid = getuid();
206
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)
214 errx(1, "unknown user: %s", *argv);
215 #ifndef OPEN_DIRECTORY
216 if (uid != 0 && uid != pw->pw_uid)
217 baduser();
218 #endif
219 }
220
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 }
228
229 #if OPEN_DIRECTORY
230 master_mode = (uid == 0);
231
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);
243 }
244
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);
252
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);
266
267 if (op == NEWSH) {
268 /* protect p_shell -- it thinks NULL is /bin/sh */
269 if (!arg[0])
270 usage();
271 if (p_shell(arg, pw, (ENTRY *)NULL) == -1)
272 exit(1);
273 #ifdef OPEN_DIRECTORY
274 else {
275 ENTRY* ep;
276
277 setrestricted(attrs_orig);
278
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);
285 if (attrs) CFDictionarySetValue((CFMutableDictionaryRef)attrs, kODAttributeTypeUserShell, shell);
286 CFRelease(shell);
287 }
288 } else {
289 warnx("shell is restricted");
290 exit(1);
291 }
292 }
293 }
294 }
295 #endif
296 }
297
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
307 if (op == LOADENTRY) {
308 if (uid)
309 baduser();
310 #ifdef OPEN_DIRECTORY
311 warnx("-a is not supported for Open Directory.");
312 exit(1);
313 #else
314 pw = &lpw;
315 old_pw = NULL;
316 if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER))
317 exit(1);
318 #endif /* OPEN_DIRECTORY */
319 }
320
321 #ifndef OPEN_DIRECTORY
322 if (op == NEWPW) {
323 if (uid)
324 baduser();
325
326 if (strchr(arg, ':'))
327 errx(1, "invalid format for password");
328 pw->pw_passwd = arg;
329 }
330 #endif /* OPEN_DIRECTORY */
331
332 if (op == EDITENTRY) {
333 #ifdef OPEN_DIRECTORY
334 setrestricted(attrs_orig);
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);
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()");
359 /*
360 * pw_equal does not check for crypted passwords, so we
361 * should do it explicitly
362 */
363 if (pw_equal(old_pw, pw) &&
364 strcmp(old_pw->pw_passwd, pw->pw_passwd) == 0)
365 errx(0, "user information unchanged");
366 #endif /* OPEN_DIRECTORY */
367 }
368
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 = "";
377 }
378 #endif
379
380 #ifdef OPEN_DIRECTORY
381 odUpdateUser(rec, attrs_orig, attrs);
382
383 if (rec) CFRelease(rec);
384
385 exit(0);
386 return 0;
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 */
432 }
433
434 static void
435 baduser(void)
436 {
437 errx(1, "%s", strerror(EACCES));
438 }
439
440 static void
441 usage(void)
442 {
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 */
456 exit(1);
457 }