/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2006 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
- * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
- * Reserved. This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License'). You may not use this file
- * except in compliance with the License. Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License."
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*-
- * Copyright (c) 1990, 1993, 1994
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* SUCH DAMAGE.
*/
+#if 0
+#if 0
#ifndef lint
-static char copyright[] =
+static const char copyright[] =
"@(#) Copyright (c) 1988, 1993, 1994\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
+#ifndef lint
+static char sccsid[] = "@(#)chpass.c 8.4 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/chpass/chpass.c,v 1.27.8.1 2006/09/29 06:13:20 marck Exp $");
+#endif
+
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <sys/time.h>
#include <sys/resource.h>
-#include <ctype.h>
#include <err.h>
#include <errno.h>
-#include <fcntl.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#ifdef YP
+#include <ypclnt.h>
+#endif
+#ifndef OPEN_DIRECTORY
#include <pw_scan.h>
-#include <pw_util.h>
-#include "pw_copy.h"
+#include <libutil.h>
+#endif
#include "chpass.h"
-#include "pathnames.h"
-#ifdef DIRECTORY_SERVICE
-#include "directory_service.h"
-#define PWSETFIELD(field, in, out) if(in->field) out.field = strdup(in->field)
-#endif /* DIRECTORY_SERVICE */
+int master_mode;
-char *progname = "chpass";
-char *tempname;
-uid_t uid;
-#ifdef DIRECTORY_SERVICE
-int dswhere;
-#endif /* DIRECTORY_SERVICE */
+#ifdef OPEN_DIRECTORY
+#include "open_directory.h"
+char *progname = NULL;
+CFStringRef DSPath = NULL;
+#endif /* OPEN_DIRECTORY */
-void baduser __P((void));
-void usage __P((void));
+static void baduser(void);
+static void usage(void);
int
-main(argc, argv)
- int argc;
- char **argv;
+main(int argc, char *argv[])
{
- enum { NEWSH, LOADENTRY, EDITENTRY } op;
- struct passwd *pw, lpw;
+ enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op;
+#ifndef OPEN_DIRECTORY
+ struct passwd lpw, *old_pw, *pw;
int ch, pfd, tfd;
- char *arg;
-#ifdef DIRECTORY_SERVICE
- struct passwd pworig;
- char *task_argv[3] = { NULL };
-#endif /* DIRECTORY_SERVICE */
+ const char *password;
+#else
+ struct passwd *old_pw, *pw;
+ int ch, tfd;
+ char tfn[MAXPATHLEN];
+#endif
+ char *arg = NULL;
+ uid_t uid;
+#ifdef YP
+ struct ypclnt *ypclnt;
+ const char *yp_domain = NULL, *yp_host = NULL;
+#endif
+#ifdef OPEN_DIRECTORY
+ CFStringRef username = NULL;
+ CFStringRef authname = NULL;
+ CFStringRef location = NULL;
+
+ progname = strrchr(argv[0], '/');
+ if (progname) progname++;
+ else progname = argv[0];
+#endif /* OPEN_DIRECTORY */
+ pw = old_pw = NULL;
op = EDITENTRY;
- while ((ch = getopt(argc, argv, "a:s:")) != EOF)
- switch(ch) {
+#ifdef OPEN_DIRECTORY
+ while ((ch = getopt(argc, argv, "a:s:l:u:")) != -1)
+#else /* OPEN_DIRECTORY */
+#ifdef YP
+ while ((ch = getopt(argc, argv, "a:p:s:e:d:h:loy")) != -1)
+#else
+ while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1)
+#endif
+#endif /* OPEN_DIRECTORY */
+ switch (ch) {
case 'a':
op = LOADENTRY;
arg = optarg;
op = NEWSH;
arg = optarg;
break;
+#ifndef OPEN_DIRECTORY
+ case 'p':
+ op = NEWPW;
+ arg = optarg;
+ break;
+ case 'e':
+ op = NEWEXP;
+ arg = optarg;
+ break;
+#ifdef YP
+ case 'd':
+ yp_domain = optarg;
+ break;
+ case 'h':
+ yp_host = optarg;
+ break;
+ case 'l':
+ case 'o':
+ case 'y':
+ /* compatibility */
+ break;
+#endif
+#else /* OPEN_DIRECTORY */
+ case 'l':
+ location = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
+ break;
+ case 'u':
+ authname = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
+ break;
+#endif
case '?':
default:
usage();
}
+
argc -= optind;
argv += optind;
+ if (argc > 1)
+ usage();
+
uid = getuid();
- if (op == EDITENTRY || op == NEWSH)
-#ifdef DIRECTORY_SERVICE
- {
-#endif /* DIRECTORY_SERVICE */
- switch(argc) {
- case 0:
- if (!(pw = getpwuid(uid)))
- errx(1, "unknown user: uid %u", uid);
- break;
- case 1:
- if (!(pw = getpwnam(*argv)))
+ if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) {
+ if (argc == 0) {
+ if ((pw = getpwuid(uid)) == NULL)
+ errx(1, "unknown user: uid %lu",
+ (unsigned long)uid);
+ } else {
+ if ((pw = getpwnam(*argv)) == NULL)
errx(1, "unknown user: %s", *argv);
-#ifndef DIRECTORY_SERVICE
- if (uid && uid != pw->pw_uid)
+#ifndef OPEN_DIRECTORY
+ if (uid != 0 && uid != pw->pw_uid)
baduser();
-#endif /* DIRECTORY_SERVICE */
- break;
- default:
- usage();
+#endif
}
- /* getpwnam(3) returns a pointer to local storage */
- lpw = *pw;
- PWSETFIELD(pw_name, pw, lpw);
- PWSETFIELD(pw_passwd, pw, lpw);
- PWSETFIELD(pw_class, pw, lpw);
- PWSETFIELD(pw_gecos, pw, lpw);
- PWSETFIELD(pw_dir, pw, lpw);
- PWSETFIELD(pw_shell, pw, lpw);
+#ifndef OPEN_DIRECTORY
+ /* Make a copy for later verification */
+ if ((pw = pw_dup(pw)) == NULL ||
+ (old_pw = pw_dup(pw)) == NULL)
+ err(1, "pw_dup");
+#endif
+ }
- pw = &lpw;
+#if OPEN_DIRECTORY
+ master_mode = (uid == 0);
-#ifdef DIRECTORY_SERVICE
- if ((dswhere = wherepwent(pw->pw_name)) < 0) {
- if(dswhere > E_NOTFOUND)
- errc(1, dswhere, "wherepwent");
- else
- errx(1, "wherepwent returned %d", dswhere);
- }
- switch(dswhere) {
- case WHERE_REMOTENI:
- errx(1,
-"Can't change info for user \"%s\", which resides in the\n"
-"netinfo domain \"%s\"",
- pw->pw_name, DSPath);
- case WHERE_DS:
- errx(1,
-"Can't change info for user \"%s\", which resides in the\n"
-"Directory Service path \"%s\"",
- pw->pw_name, DSPath);
- case WHERE_NIS:
- errx(1,
-"Can't change info for user \"%s\", which resides in NIS",
- pw->pw_name);
- case WHERE_LOCALNI:
- pworig = *pw;
- PWSETFIELD(pw_name, pw, pworig);
- PWSETFIELD(pw_passwd, pw, pworig);
- PWSETFIELD(pw_class, pw, pworig);
- PWSETFIELD(pw_gecos, pw, pworig);
- PWSETFIELD(pw_dir, pw, pworig);
- PWSETFIELD(pw_shell, pw, pworig);
- /* drop through */
- default:
- if (uid && uid != pw->pw_uid)
- baduser();
- }
+ /*
+ * Find the user record and copy its details.
+ */
+ username = CFStringCreateWithCString(NULL, pw->pw_name, kCFStringEncodingUTF8);
+
+ if (strcmp(progname, "chsh") == 0 || op == NEWSH) {
+ cfprintf(stderr, "Changing shell for %@.\n", username);
+ } else if (strcmp(progname, "chfn") == 0) {
+ cfprintf(stderr, "Changing finger information for %@.\n", username);
+ } else if (strcmp(progname, "chpass") == 0) {
+ cfprintf(stderr, "Changing account information for %@.\n", username);
}
-#endif /* DIRECTORY_SERVICE */
+
+ /*
+ * odGetUser updates DSPath global variable, performs authentication
+ * if necessary, and extracts the attributes.
+ */
+ CFDictionaryRef attrs_orig = NULL;
+ CFDictionaryRef attrs = NULL;
+ ODRecordRef rec = odGetUser(location, authname, username, &attrs_orig);
+
+ if (!rec || !attrs_orig) exit(1);
+#endif /* OPEN_DIRECTORY */
+
+#ifdef YP
+ if (pw != NULL && (pw->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
+ ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
+ master_mode = (ypclnt != NULL &&
+ ypclnt_connect(ypclnt) != -1 &&
+ ypclnt_havepasswdd(ypclnt) == 1);
+ ypclnt_free(ypclnt);
+ } else
+#endif
+ master_mode = (uid == 0);
if (op == NEWSH) {
/* protect p_shell -- it thinks NULL is /bin/sh */
if (!arg[0])
usage();
- if (p_shell(arg, pw, (ENTRY *)NULL))
- pw_error((char *)NULL, 0, 1);
+ if (p_shell(arg, pw, (ENTRY *)NULL) == -1)
+ exit(1);
+#ifdef OPEN_DIRECTORY
+ else {
+ ENTRY* ep;
+
+ setrestricted(attrs_orig);
+
+ for (ep = list; ep->prompt; ep++) {
+ if (strncasecmp(ep->prompt, "shell", ep->len) == 0) {
+ if (!ep->restricted) {
+ CFStringRef shell = CFStringCreateWithCString(NULL, arg, kCFStringEncodingUTF8);
+ if (shell) {
+ attrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ if (attrs) CFDictionarySetValue((CFMutableDictionaryRef)attrs, CFSTR(kDS1AttrUserShell), shell);
+ CFRelease(shell);
+ }
+ } else {
+ warnx("shell is restricted");
+ exit(1);
+ }
+ }
+ }
+ }
+#endif
}
+#ifndef OPEN_DIRECTORY
+ if (op == NEWEXP) {
+ if (uid) /* only root can change expire */
+ baduser();
+ if (p_expire(arg, pw, (ENTRY *)NULL) == -1)
+ exit(1);
+ }
+#endif
+
if (op == LOADENTRY) {
-#ifdef DIRECTORY_SERVICE
- warnx("-a is only supported for %s", MasterPasswd);
- dswhere = WHERE_FILES;
-#endif /* DIRECTORY_SERVICE */
if (uid)
baduser();
+#ifdef OPEN_DIRECTORY
+ warnx("-a is not supported for Open Directory.");
+ exit(1);
+#else
pw = &lpw;
- if (!pw_scan(arg, pw, NULL))
+ old_pw = NULL;
+ if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER))
exit(1);
+#endif /* OPEN_DIRECTORY */
}
- /*
- * The temporary file/file descriptor usage is a little tricky here.
- * 1: We start off with two fd's, one for the master password
- * file (used to lock everything), and one for a temporary file.
- * 2: Display() gets an fp for the temporary file, and copies the
- * user's information into it. It then gives the temporary file
- * to the user and closes the fp, closing the underlying fd.
- * 3: The user edits the temporary file some number of times.
- * 4: Verify() gets an fp for the temporary file, and verifies the
- * contents. It can't use an fp derived from the step #2 fd,
- * because the user's editor may have created a new instance of
- * the file. Once the file is verified, its contents are stored
- * in a password structure. The verify routine closes the fp,
- * closing the underlying fd.
- * 5: Delete the temporary file.
- * 6: Get a new temporary file/fd. Pw_copy() gets an fp for it
- * file and copies the master password file into it, replacing
- * the user record with a new one. We can't use the first
- * temporary file for this because it was owned by the user.
- * Pw_copy() closes its fp, flushing the data and closing the
- * underlying file descriptor. We can't close the master
- * password fp, or we'd lose the lock.
- * 7: Call pw_mkdb() (which renames the temporary file) and exit.
- * The exit closes the master passwd fp/fd.
- */
- pw_init();
-#ifdef DIRECTORY_SERVICE
- if (dswhere == WHERE_FILES)
-#endif /* DIRECTORY_SERVICE */
- pfd = pw_lock();
- tfd = pw_tmp();
+#ifndef OPEN_DIRECTORY
+ if (op == NEWPW) {
+ if (uid)
+ baduser();
- if (op == EDITENTRY) {
-#ifdef DIRECTORY_SERVICE
- setrestricted(dswhere, pw);
-#endif /* DIRECTORY_SERVICE */
- display(tfd, pw);
- edit(pw);
- (void)unlink(tempname);
-#ifdef DIRECTORY_SERVICE
- if (dswhere == WHERE_FILES)
-#endif /* DIRECTORY_SERVICE */
- tfd = pw_tmp();
+ if (strchr(arg, ':'))
+ errx(1, "invalid format for password");
+ pw->pw_passwd = arg;
}
-
-#ifdef DIRECTORY_SERVICE
- switch (dswhere) {
- case WHERE_LOCALNI:
- update_local_ni(&pworig, pw);
- break;
- case WHERE_FILES:
-#endif /* DIRECTORY_SERVICE */
- pw_copy(pfd, tfd, pw);
+#endif /* OPEN_DIRECTORY */
- if (pw_mkdb() != 0)
- pw_error((char *)NULL, 0, 1);
-#ifdef DIRECTORY_SERVICE
+ if (op == EDITENTRY) {
+#ifdef OPEN_DIRECTORY
+ setrestricted(attrs_orig);
+ snprintf(tfn, sizeof(tfn), "/tmp/%s.XXXXXX", progname);
+ if ((tfd = mkstemp(tfn)) == -1)
+ err(1, "%s", tfn);
+ attrs = (CFMutableDictionaryRef)edit(tfn, attrs_orig);
+ (void)unlink(tfn);
+#else
+ /*
+ * We don't really need pw_*() here, but pw_edit() (used
+ * by edit()) is just too useful...
+ */
+ if (pw_init(NULL, NULL))
+ err(1, "pw_init()");
+ if ((tfd = pw_tmp(-1)) == -1) {
+ pw_fini();
+ err(1, "pw_tmp()");
+ }
+ free(pw);
+ pw = edit(pw_tempname(), old_pw);
+ pw_fini();
+ if (pw == NULL)
+ err(1, "edit()");
+ /*
+ * pw_equal does not check for crypted passwords, so we
+ * should do it explicitly
+ */
+ if (pw_equal(old_pw, pw) &&
+ strcmp(old_pw->pw_passwd, pw->pw_passwd) == 0)
+ errx(0, "user information unchanged");
+#endif /* OPEN_DIRECTORY */
}
- task_argv[0] = "/usr/sbin/lookupd";
- task_argv[1] = "-flushcache";
- task_argv[2] = NULL;
- LaunchTaskWithPipes( task_argv[0], task_argv, NULL, NULL );
-#endif /* DIRECTORY_SERVICE */
- exit(0);
-}
-#ifdef DIRECTORY_SERVICE
-// read from 0
-int LaunchTaskWithPipes(const char *path, char *const argv[], int *outPipe0, int *outPipe1)
-{
- int outputPipe[2];
- pid_t pid;
-
- if (outPipe0 != NULL)
- pipe(outputPipe);
-
- pid = fork();
- if (pid == -1)
- return -1;
-
- /* Handle the child */
- if (pid == 0)
- {
- int result = -1;
-
- if (outPipe0 != NULL)
- dup2(outputPipe[1], fileno(stdout));
-
- result = execv(path, argv);
- if (result == -1) {
- _exit(1);
- }
-
- /* This should never be reached */
- _exit(1);
+#ifndef OPEN_DIRECTORY
+ if (old_pw && !master_mode) {
+ password = getpass("Password: ");
+ if (strcmp(crypt(password, old_pw->pw_passwd),
+ old_pw->pw_passwd) != 0)
+ baduser();
+ } else {
+ password = "";
}
+#endif
+
+#ifdef OPEN_DIRECTORY
+ odUpdateUser(rec, attrs_orig, attrs);
- /* Now the parent */
- if ( outPipe0 != NULL )
- *outPipe0 = outputPipe[0];
- if ( outPipe1 != NULL )
- *outPipe1 = outputPipe[1];
+ if (rec) CFRelease(rec);
+ exit(0);
return 0;
+#else /* OPEN_DIRECTORY */
+ exit(0);
+ if (old_pw != NULL)
+ pw->pw_fields |= (old_pw->pw_fields & _PWF_SOURCE);
+ switch (pw->pw_fields & _PWF_SOURCE) {
+#ifdef YP
+ case _PWF_NIS:
+ ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
+ if (ypclnt == NULL ||
+ ypclnt_connect(ypclnt) == -1 ||
+ ypclnt_passwd(ypclnt, pw, password) == -1) {
+ warnx("%s", ypclnt->error);
+ ypclnt_free(ypclnt);
+ exit(1);
+ }
+ ypclnt_free(ypclnt);
+ errx(0, "NIS user information updated");
+#endif /* YP */
+ case 0:
+ case _PWF_FILES:
+ if (pw_init(NULL, NULL))
+ err(1, "pw_init()");
+ if ((pfd = pw_lock()) == -1) {
+ pw_fini();
+ err(1, "pw_lock()");
+ }
+ if ((tfd = pw_tmp(-1)) == -1) {
+ pw_fini();
+ err(1, "pw_tmp()");
+ }
+ if (pw_copy(pfd, tfd, pw, old_pw) == -1) {
+ pw_fini();
+ err(1, "pw_copy");
+ }
+ if (pw_mkdb(pw->pw_name) == -1) {
+ pw_fini();
+ err(1, "pw_mkdb()");
+ }
+ pw_fini();
+ errx(0, "user information updated");
+ break;
+ default:
+ errx(1, "unsupported passwd source");
+ }
+#endif /* OPEN_DIRECTORY */
}
-#endif /* DIRECTORY_SERVICE */
-void
-baduser()
+static void
+baduser(void)
{
errx(1, "%s", strerror(EACCES));
}
-void
-usage()
+static void
+usage(void)
{
- (void)fprintf(stderr, "usage: chpass [-a list] [-s shell] [user]\n");
+ (void)fprintf(stderr,
+ "usage: chpass%s %s [user]\n",
+#ifdef OPEN_DIRECTORY
+ "",
+ "[-l location] [-u authname] [-s shell]");
+#else /* OPEN_DIRECTORY */
+#ifdef YP
+ " [-d domain] [-h host]",
+#else
+ "",
+#endif
+ "[-a list] [-p encpass] [-s shell] [-e mmm dd yy]");
+#endif /* OPEN_DIRECTORY */
exit(1);
}