summaryrefslogtreecommitdiffstats
path: root/system_cmds/chpass.tproj
diff options
context:
space:
mode:
Diffstat (limited to 'system_cmds/chpass.tproj')
-rw-r--r--system_cmds/chpass.tproj/IMPORT_NOTES1
-rw-r--r--system_cmds/chpass.tproj/chpass.1315
-rw-r--r--system_cmds/chpass.tproj/chpass.c457
-rw-r--r--system_cmds/chpass.tproj/chpass.h132
-rw-r--r--system_cmds/chpass.tproj/edit.c415
-rw-r--r--system_cmds/chpass.tproj/field.c345
-rw-r--r--system_cmds/chpass.tproj/open_directory.c205
-rw-r--r--system_cmds/chpass.tproj/open_directory.h13
-rw-r--r--system_cmds/chpass.tproj/pw_copy.c127
-rw-r--r--system_cmds/chpass.tproj/pw_copy.h56
-rw-r--r--system_cmds/chpass.tproj/table.c117
-rw-r--r--system_cmds/chpass.tproj/util.c335
12 files changed, 2518 insertions, 0 deletions
diff --git a/system_cmds/chpass.tproj/IMPORT_NOTES b/system_cmds/chpass.tproj/IMPORT_NOTES
new file mode 100644
index 0000000..457907b
--- /dev/null
+++ b/system_cmds/chpass.tproj/IMPORT_NOTES
@@ -0,0 +1 @@
+chpass.1 - FreeBSD file with references to yp items deleted
diff --git a/system_cmds/chpass.tproj/chpass.1 b/system_cmds/chpass.tproj/chpass.1
new file mode 100644
index 0000000..7ee45ae
--- /dev/null
+++ b/system_cmds/chpass.tproj/chpass.1
@@ -0,0 +1,315 @@
+.\" Copyright (c) 1988, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)chpass.1 8.2 (Berkeley) 12/30/93
+.\" $FreeBSD: src/usr.bin/chpass/chpass.1,v 1.38.2.1 2005/09/24 01:59:39 keramida Exp $
+.\"
+.Dd December 30, 1993
+.Dt CHPASS 1
+.Os
+.Sh NAME
+.Nm chpass ,
+.Nm chfn ,
+.Nm chsh
+.\".Nm ypchpass ,
+.\".Nm ypchfn ,
+.\".Nm ypchsh
+.Nd add or change user database information
+.Sh SYNOPSIS
+.Nm
+.\".Op Fl a Ar list
+.\".Op Fl p Ar encpass
+.\".Op Fl e Ar expiretime
+.Op Fl l Ar location
+.Op Fl u Ar authname
+.Op Fl s Ar newshell
+.Op user
+.Sh DESCRIPTION
+The
+.Nm
+utility
+allows editing of the user database information associated
+with
+.Ar user
+or, by default, the current user.
+.Pp
+The
+.Nm
+utility
+.Em cannot
+change the user's password on Open Directory
+systems. Use the
+.Xr passwd 1
+utility instead.
+.Pp
+The
+.Nm chfn ,
+and
+.Nm chsh
+.\".Nm ypchpass ,
+.\".Nm ypchfn
+.\"and
+.\".Nm ypchsh
+utilities behave identically to
+.Nm .
+(There is only one program.)
+.Pp
+The information is formatted and supplied to an editor for changes.
+.Pp
+Only the information that the user is allowed to change is displayed.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.\".It Fl a
+.\"The super-user is allowed to directly supply a user database
+.\"entry, in the format specified by
+.\".Xr passwd 5 ,
+.\"as an argument.
+.\"This argument must be a colon
+.\".Pq Dq \&:
+.\"separated list of all the
+.\"user database fields, although they may be empty.
+.\".It Fl p
+.\"The super-user is allowed to directly supply an encrypted password field,
+.\"in the format used by
+.\".Xr crypt 3 ,
+.\"as an argument.
+.\".It Fl e Ar expiretime
+.\"Change the account expire time.
+.\"This option is used to set the expire time
+.\"from a script as if it were done in the interactive editor.
+.It Fl l Ar location
+If not specified,
+.Nm
+will perform a search for the user record on all available
+Open Directory nodes.
+When specified,
+.Nm
+will edit the user record on the directory node at the given
+.Ar location .
+.It Fl u Ar authname
+The user name to use when authenticating to the directory node containing the
+user.
+.It Fl s Ar newshell
+Attempt to change the user's shell to
+.Ar newshell .
+.El
+.Pp
+Possible display items are as follows:
+.Pp
+.Bl -tag -width "Other Information:" -compact -offset indent
+.It Login:
+user's login name
+.\".It Password:
+.\"user's encrypted password
+.It Uid:
+user's login
+.It Gid:
+user's login group
+.It Generated uid:
+user's UUID
+.\".It Class:
+.\"user's general classification
+.\".It Change:
+.\"password change time
+.\".It Expire:
+.\"account expiration time
+.It Full Name:
+user's real name
+.It Office Location:
+user's office location
+.It Office Phone:
+user's office phone
+.It Home Phone:
+user's home phone
+.\".It Other Information:
+.\"any locally defined parameters for user
+.It Home Directory:
+user's home directory
+.It Shell:
+user's login shell
+.Pp
+.\".It NOTE(1) -
+.\"In the actual master.passwd file, these fields are comma-delimited
+.\"fields embedded in the FullName field.
+.El
+.Pp
+The
+.Ar login
+field is the user name used to access the computer account.
+.\".Pp
+.\"The
+.\".Ar password
+.\"field contains the encrypted form of the user's password.
+.Pp
+The
+.Ar uid
+field is the number associated with the
+.Ar login
+field.
+Both of these fields should be unique across the system (and often
+across a group of systems) as they control file access.
+.Pp
+While it is possible to have multiple entries with identical login names
+and/or identical user id's, it is usually a mistake to do so.
+Routines
+that manipulate these files will often return only one of the multiple
+entries, and that one by random selection.
+.Pp
+The
+.Ar group
+field is the group that the user will be placed in at login.
+Since
+.Bx
+supports multiple groups (see
+.Xr groups 1 )
+this field currently has little special meaning.
+This field may be filled in with either a number or a group name (see
+.Xr group 5 ) .
+.Pp
+The
+.Ar generated uid
+field is the globally unique identifier (UUID) for the user.
+.\".Pp
+.\"The
+.\".Ar class
+.\"field references class descriptions in
+.\".Pa /etc/login.conf
+.\"and is typically used to initialize the user's system resource limits
+.\"when they login.
+.\".Pp
+.\"The
+.\".Ar change
+.\"field is the date by which the password must be changed.
+.\".Pp
+.\"The
+.\".Ar expire
+.\"field is the date on which the account expires.
+.\".Pp
+.\"Both the
+.\".Ar change
+.\"and
+.\".Ar expire
+.\"fields should be entered in the form
+.\".Dq month day year
+.\"where
+.\".Ar month
+.\"is the month name (the first three characters are sufficient),
+.\".Ar day
+.\"is the day of the month, and
+.\".Ar year
+.\"is the year.
+.\".Pp
+.\"Five fields are available for storing the user's
+.\".Ar full name , office location ,
+.\".Ar work
+.\"and
+.\".Ar home telephone
+.\"numbers and finally
+.\".Ar other information
+.\"which is a single comma delimited string to represent any additional
+.\"gcos fields (typically used for site specific user information).
+.\"Note that
+.\".Xr finger 1
+.\"will display the office location and office phone together under the
+.\"heading
+.\".Ar Office: .
+The
+.Ar full name
+field contains the full name of the user.
+.Pp
+The user's
+.Ar home directory
+is the full
+.Ux
+path name where the user
+will be placed at login.
+.Pp
+The
+.Ar shell
+field is the command interpreter the user prefers.
+If the
+.Ar shell
+field is empty, the Bourne shell,
+.Pa /bin/sh ,
+is assumed.
+When altering a login shell, and not the super-user, the user
+may not change from a non-standard shell or to a non-standard
+shell.
+Non-standard is defined as a shell not found in
+.Pa /etc/shells .
+.Pp
+The
+.Ar picture
+field is the path to a picture to be displayed for the user.
+.Sh OPEN DIRECTORY
+User database entries are under the control of
+.Xr DirectoryService 8
+and may be physically located in many different places,
+including the local Directory Service node,
+and remote LDAP servers.
+This version of
+.Nm
+uses Open Directory to change user database information.
+It does not interact with the historic flat file
+database
+.Pa /etc/master.passwd
+.
+.Sh ENVIRONMENT
+The
+.Xr vi 1
+editor will be used unless the environment variable
+.Ev EDITOR
+is set to
+an alternate editor.
+When the editor terminates, the information is re-read and used to
+update the user database itself.
+Only the user, or the super-user, may edit the information associated
+with the user.
+.Sh FILES
+.Bl -tag -width /etc/chpass.XXXXXX -compact
+.It Pa /etc/chpass.XXXXXX
+temporary copy of the data to edit
+.It Pa /etc/shells
+the list of approved shells
+.El
+.Sh SEE ALSO
+.\".Xr finger 1 ,
+.Xr login 1 ,
+.Xr passwd 1 ,
+.Xr getusershell 3 ,
+.Xr passwd 5
+.Rs
+.%A Robert Morris
+.%A Ken Thompson
+.%T "UNIX Password security"
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 Reno .
diff --git a/system_cmds/chpass.tproj/chpass.c b/system_cmds/chpass.tproj/chpass.c
new file mode 100644
index 0000000..6b265c0
--- /dev/null
+++ b/system_cmds/chpass.tproj/chpass.c
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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, 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) 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * 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.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#if 0
+#ifndef lint
+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 <err.h>
+#include <errno.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 <libutil.h>
+#endif
+
+#include "chpass.h"
+
+int master_mode;
+
+#ifdef OPEN_DIRECTORY
+#include "open_directory.h"
+char *progname = NULL;
+CFStringRef DSPath = NULL;
+#endif /* OPEN_DIRECTORY */
+
+static void baduser(void);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op;
+#ifndef OPEN_DIRECTORY
+ struct passwd lpw, *old_pw, *pw;
+ int ch, pfd, tfd;
+ const char *password;
+#else
+ struct passwd *old_pw, *pw;
+ int ch, tfd;
+ char tfn[MAXPATHLEN];
+ char *tmpdir;
+#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;
+#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;
+ break;
+ case 's':
+ 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 || 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 OPEN_DIRECTORY
+ if (uid != 0 && uid != pw->pw_uid)
+ baduser();
+#endif
+ }
+
+#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
+ }
+
+#if OPEN_DIRECTORY
+ master_mode = (uid == 0);
+
+ /*
+ * 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);
+ }
+
+ /*
+ * 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) == -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, kODAttributeTypeUserShell, 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) {
+ if (uid)
+ baduser();
+#ifdef OPEN_DIRECTORY
+ warnx("-a is not supported for Open Directory.");
+ exit(1);
+#else
+ pw = &lpw;
+ old_pw = NULL;
+ if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER))
+ exit(1);
+#endif /* OPEN_DIRECTORY */
+ }
+
+#ifndef OPEN_DIRECTORY
+ if (op == NEWPW) {
+ if (uid)
+ baduser();
+
+ if (strchr(arg, ':'))
+ errx(1, "invalid format for password");
+ pw->pw_passwd = arg;
+ }
+#endif /* OPEN_DIRECTORY */
+
+ if (op == EDITENTRY) {
+#ifdef OPEN_DIRECTORY
+ setrestricted(attrs_orig);
+ tmpdir = getenv("TMPDIR");
+ if (!tmpdir)
+ tmpdir = P_tmpdir; // defined in the system headers, defaults to /tmp
+ snprintf(tfn, sizeof(tfn), "%s/%s.XXXXXX", tmpdir, 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 */
+ }
+
+#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);
+
+ 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 */
+}
+
+static void
+baduser(void)
+{
+ errx(1, "%s", strerror(EACCES));
+}
+
+static void
+usage(void)
+{
+ (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);
+}
diff --git a/system_cmds/chpass.tproj/chpass.h b/system_cmds/chpass.tproj/chpass.h
new file mode 100644
index 0000000..739601f
--- /dev/null
+++ b/system_cmds/chpass.tproj/chpass.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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, 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) 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * 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.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)chpass.h 8.4 (Berkeley) 4/2/94
+ * $FreeBSD: src/usr.bin/chpass/chpass.h,v 1.7 2004/01/18 21:46:39 charnier Exp $
+ */
+
+#ifdef OPEN_DIRECTORY
+#include "open_directory.h"
+
+extern char* progname;
+extern CFStringRef DSPath;
+#endif /* OPEN_DIRECTORY */
+
+struct passwd;
+
+typedef struct _entry {
+ const char *prompt;
+#ifdef OPEN_DIRECTORY
+ void (*display)();
+#endif
+ int (*func)(char *, struct passwd *, struct _entry *);
+ int restricted;
+ size_t len;
+#if OPEN_DIRECTORY
+ char *except;
+ const CFStringRef *attrName;
+#else /* OPEN_DIRECTORY */
+ char *except, *save;
+#endif /* OPEN_DIRECTORY */
+} ENTRY;
+
+/* Field numbers. */
+#define E_BPHONE 8
+#define E_HPHONE 9
+#define E_LOCATE 10
+#define E_NAME 7
+#define E_OTHER 11
+#define E_SHELL 13
+
+extern ENTRY list[];
+extern int master_mode;
+
+#ifdef OPEN_DIRECTORY
+/* edit.c */
+void display_time(CFDictionaryRef, CFStringRef, const char*, FILE *);
+void display_string(CFDictionaryRef, CFStringRef, const char*, FILE *);
+CFDictionaryRef edit(const char *tfn, CFDictionaryRef pw);
+
+/* util.c */
+int cfprintf(FILE* file, const char* format, ...);
+int editfile(const char* tfn);
+#endif /* OPEN_DIRECTORY */
+
+int atot(char *, time_t *);
+#ifndef OPEN_DIRECTORY
+struct passwd *edit(const char *, struct passwd *);
+#endif /* OPEN_DIRECTORY */
+int ok_shell(char *);
+char *dup_shell(char *);
+int p_change(char *, struct passwd *, ENTRY *);
+int p_class(char *, struct passwd *, ENTRY *);
+int p_expire(char *, struct passwd *, ENTRY *);
+int p_gecos(char *, struct passwd *, ENTRY *);
+int p_gid(char *, struct passwd *, ENTRY *);
+int p_hdir(char *, struct passwd *, ENTRY *);
+int p_login(char *, struct passwd *, ENTRY *);
+int p_passwd(char *, struct passwd *, ENTRY *);
+int p_shell(char *, struct passwd *, ENTRY *);
+int p_uid(char *, struct passwd *, ENTRY *);
+#ifdef OPEN_DIRECTORY
+int p_uuid(char *, struct passwd *, ENTRY *);
+#endif /* OPEN_DIRECTORY */
+char *ttoa(time_t);
diff --git a/system_cmds/chpass.tproj/edit.c b/system_cmds/chpass.tproj/edit.c
new file mode 100644
index 0000000..7c87050
--- /dev/null
+++ b/system_cmds/chpass.tproj/edit.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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, 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) 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * 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.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)edit.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/chpass/edit.c,v 1.23 2003/04/09 18:18:42 des Exp $");
+#endif
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef OPEN_DIRECTORY
+#include <pw_scan.h>
+#include <libutil.h>
+#endif
+
+#include "chpass.h"
+
+#ifdef OPEN_DIRECTORY
+static int display(const char *tfn, CFDictionaryRef attrs);
+static CFDictionaryRef verify(const char *tfn, CFDictionaryRef attrs);
+#else
+static int display(const char *tfn, struct passwd *pw);
+static struct passwd *verify(const char *tfn, struct passwd *pw);
+#endif
+
+#ifdef OPEN_DIRECTORY
+CFDictionaryRef
+edit(const char *tfn, CFDictionaryRef pw)
+#else
+struct passwd *
+edit(const char *tfn, struct passwd *pw)
+#endif
+{
+#ifdef OPEN_DIRECTORY
+ CFDictionaryRef npw;
+#else
+ struct passwd *npw;
+#endif
+ char *line;
+ size_t len;
+
+ if (display(tfn, pw) == -1)
+ return (NULL);
+ for (;;) {
+#ifdef OPEN_DIRECTORY
+ switch (editfile(tfn)) {
+#else
+ switch (pw_edit(1)) {
+#endif
+ case -1:
+ return (NULL);
+ case 0:
+#ifdef OPEN_DIRECTORY
+ return (NULL);
+#else
+ return (pw_dup(pw));
+#endif
+ default:
+ break;
+ }
+ if ((npw = verify(tfn, pw)) != NULL)
+ return (npw);
+#ifndef OPEN_DIRECTORY
+ free(npw);
+#endif
+ printf("re-edit the password file? ");
+ fflush(stdout);
+ if ((line = fgetln(stdin, &len)) == NULL) {
+ warn("fgetln()");
+ return (NULL);
+ }
+ if (len > 0 && (*line == 'N' || *line == 'n'))
+ return (NULL);
+ }
+}
+
+/*
+ * display --
+ * print out the file for the user to edit; strange side-effect:
+ * set conditional flag if the user gets to edit the shell.
+ */
+#if OPEN_DIRECTORY
+static int
+display(const char *tfn, CFDictionaryRef attrs)
+#else
+static int
+display(const char *tfn, struct passwd *pw)
+#endif
+{
+ FILE *fp;
+#ifndef OPEN_DIRECTORY
+ char *bp, *gecos, *p;
+#endif
+
+ if ((fp = fopen(tfn, "w")) == NULL) {
+ warn("%s", tfn);
+ return (-1);
+ }
+
+#ifdef OPEN_DIRECTORY
+ CFArrayRef values = CFDictionaryGetValue(attrs, kODAttributeTypeRecordName);
+ CFStringRef username = (values && CFArrayGetCount(values)) > 0 ? CFArrayGetValueAtIndex(values, 0) : NULL;
+
+ (void)cfprintf(fp,
+ "# Changing user information for %@.\n"
+ "# Use \"passwd\" to change the password.\n"
+ "##\n"
+ "# Open Directory%s%@\n"
+ "##\n",
+ username,
+ DSPath ? ": " : "",
+ DSPath ? DSPath : CFSTR(""));
+
+ int ndisplayed = 0;
+ ENTRY* ep;
+ for (ep = list; ep->prompt; ep++)
+ if (!ep->restricted) {
+ ep->display(attrs, *ep->attrName, ep->prompt, fp);
+ ndisplayed++;
+ }
+ if(!ndisplayed) {
+ (void)fprintf(fp, "###################################\n");
+ (void)fprintf(fp, "# No fields are available to change\n");
+ (void)fprintf(fp, "###################################\n");
+ }
+#else /* OPEN_DIRECTORY */
+ (void)fprintf(fp,
+ "#Changing user information for %s.\n", pw->pw_name);
+ if (master_mode) {
+ (void)fprintf(fp, "Login: %s\n", pw->pw_name);
+ (void)fprintf(fp, "Password: %s\n", pw->pw_passwd);
+ (void)fprintf(fp, "Uid [#]: %lu\n", (unsigned long)pw->pw_uid);
+ (void)fprintf(fp, "Gid [# or name]: %lu\n",
+ (unsigned long)pw->pw_gid);
+ (void)fprintf(fp, "Change [month day year]: %s\n",
+ ttoa(pw->pw_change));
+ (void)fprintf(fp, "Expire [month day year]: %s\n",
+ ttoa(pw->pw_expire));
+ (void)fprintf(fp, "Class: %s\n", pw->pw_class);
+ (void)fprintf(fp, "Home directory: %s\n", pw->pw_dir);
+ (void)fprintf(fp, "Shell: %s\n",
+ *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL);
+ }
+ /* Only admin can change "restricted" shells. */
+#if 0
+ else if (ok_shell(pw->pw_shell))
+ /*
+ * Make shell a restricted field. Ugly with a
+ * necklace, but there's not much else to do.
+ */
+#else
+ else if ((!list[E_SHELL].restricted && ok_shell(pw->pw_shell)) ||
+ master_mode)
+ /*
+ * If change not restrict (table.c) and standard shell
+ * OR if root, then allow editing of shell.
+ */
+#endif
+ (void)fprintf(fp, "Shell: %s\n",
+ *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL);
+ else
+ list[E_SHELL].restricted = 1;
+
+ if ((bp = gecos = strdup(pw->pw_gecos)) == NULL) {
+ warn(NULL);
+ fclose(fp);
+ return (-1);
+ }
+
+ p = strsep(&bp, ",");
+ p = strdup(p ? p : "");
+ list[E_NAME].save = p;
+ if (!list[E_NAME].restricted || master_mode)
+ (void)fprintf(fp, "Full Name: %s\n", p);
+
+ p = strsep(&bp, ",");
+ p = strdup(p ? p : "");
+ list[E_LOCATE].save = p;
+ if (!list[E_LOCATE].restricted || master_mode)
+ (void)fprintf(fp, "Office Location: %s\n", p);
+
+ p = strsep(&bp, ",");
+ p = strdup(p ? p : "");
+ list[E_BPHONE].save = p;
+ if (!list[E_BPHONE].restricted || master_mode)
+ (void)fprintf(fp, "Office Phone: %s\n", p);
+
+ p = strsep(&bp, ",");
+ p = strdup(p ? p : "");
+ list[E_HPHONE].save = p;
+ if (!list[E_HPHONE].restricted || master_mode)
+ (void)fprintf(fp, "Home Phone: %s\n", p);
+
+ bp = strdup(bp ? bp : "");
+ list[E_OTHER].save = bp;
+ if (!list[E_OTHER].restricted || master_mode)
+ (void)fprintf(fp, "Other information: %s\n", bp);
+
+ free(gecos);
+#endif /* OPEN_DIRECTORY */
+
+ (void)fchown(fileno(fp), getuid(), getgid());
+ (void)fclose(fp);
+ return (0);
+}
+
+#ifdef OPEN_DIRECTORY
+static CFDictionaryRef
+verify(const char* tfn, CFDictionaryRef pw)
+#else
+static struct passwd *
+verify(const char *tfn, struct passwd *pw)
+#endif
+{
+#ifdef OPEN_DIRECTORY
+ CFMutableDictionaryRef npw;
+#else
+ struct passwd *npw;
+#endif
+ ENTRY *ep;
+ char *buf, *p, *val;
+ struct stat sb;
+ FILE *fp;
+ int line;
+ size_t len;
+
+#ifdef OPEN_DIRECTORY
+ if ((npw = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL)
+ return (NULL);
+#else
+ if ((pw = pw_dup(pw)) == NULL)
+ return (NULL);
+#endif
+ if ((fp = fopen(tfn, "r")) == NULL ||
+ fstat(fileno(fp), &sb) == -1) {
+ warn("%s", tfn);
+#ifndef OPEN_DIRECTORY
+ free(pw);
+#endif
+ return (NULL);
+ }
+ if (sb.st_size == 0) {
+ warnx("corrupted temporary file");
+ fclose(fp);
+#ifndef OPEN_DIRECTORY
+ free(pw);
+#endif
+ return (NULL);
+ }
+ val = NULL;
+ for (line = 1; (buf = fgetln(fp, &len)) != NULL; ++line) {
+ if (*buf == '\0' || *buf == '#')
+ continue;
+ while (len > 0 && isspace(buf[len - 1]))
+ --len;
+ for (ep = list;; ++ep) {
+ if (!ep->prompt) {
+ warnx("%s: unrecognized field on line %d",
+ tfn, line);
+ goto bad;
+ }
+ if (ep->len > len)
+ continue;
+ if (strncasecmp(buf, ep->prompt, ep->len) != 0)
+ continue;
+ if (ep->restricted && !master_mode) {
+ warnx("%s: you may not change the %s field",
+ tfn, ep->prompt);
+ goto bad;
+ }
+ for (p = buf; p < buf + len && *p != ':'; ++p)
+ /* nothing */ ;
+ if (*p != ':') {
+ warnx("%s: line %d corrupted", tfn, line);
+ goto bad;
+ }
+ while (++p < buf + len && isspace(*p))
+ /* nothing */ ;
+ free(val);
+ asprintf(&val, "%.*s", (int)(buf + len - p), p);
+ if (val == NULL)
+ goto bad;
+ if (ep->except && strpbrk(val, ep->except)) {
+ warnx("%s: invalid character in \"%s\" field '%s'",
+ tfn, ep->prompt, val);
+ goto bad;
+ }
+#ifdef OPEN_DIRECTORY
+ if ((ep->func)(val, NULL, NULL))
+ goto bad;
+ {
+ CFStringRef str = CFStringCreateWithCString(NULL, val, kCFStringEncodingUTF8);
+ if (str) {
+ CFDictionarySetValue(npw, *ep->attrName, str);
+ CFRelease(str);
+ }
+ }
+#else
+ if ((ep->func)(val, pw, ep))
+ goto bad;
+#endif
+ break;
+ }
+ }
+ free(val);
+ fclose(fp);
+
+#ifndef OPEN_DIRECTORY
+ /* Build the gecos field. */
+ len = asprintf(&p, "%s,%s,%s,%s,%s", list[E_NAME].save,
+ list[E_LOCATE].save, list[E_BPHONE].save,
+ list[E_HPHONE].save, list[E_OTHER].save);
+ if (p == NULL) {
+ warn("asprintf()");
+ free(pw);
+ return (NULL);
+ }
+ while (len > 0 && p[len - 1] == ',')
+ p[--len] = '\0';
+ pw->pw_gecos = p;
+ buf = pw_make(pw);
+ free(pw);
+ free(p);
+ if (buf == NULL) {
+ warn("pw_make()");
+ return (NULL);
+ }
+ npw = pw_scan(buf, PWSCAN_WARN|PWSCAN_MASTER);
+#endif /* !OPEN_DIRECTORY */
+ free(buf);
+ return (npw);
+bad:
+#ifndef OPEN_DIRECTORY
+ free(pw);
+#endif
+ free(val);
+ fclose(fp);
+ return (NULL);
+}
diff --git a/system_cmds/chpass.tproj/field.c b/system_cmds/chpass.tproj/field.c
new file mode 100644
index 0000000..5ae326a
--- /dev/null
+++ b/system_cmds/chpass.tproj/field.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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, 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) 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * 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.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)field.c 8.4 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/chpass/field.c,v 1.9 2004/01/18 21:46:39 charnier Exp $");
+#endif
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "chpass.h"
+
+/* ARGSUSED */
+int
+p_login(char *p, struct passwd *pw, ENTRY *ep __unused)
+{
+ if (!*p) {
+ warnx("empty login field");
+ return (-1);
+ }
+ if (*p == '-') {
+ warnx("login names may not begin with a hyphen");
+ return (-1);
+ }
+#ifndef OPEN_DIRECTORY
+ if (!(pw->pw_name = strdup(p))) {
+ warnx("can't save entry");
+ return (-1);
+ }
+#endif
+ if (strchr(p, '.'))
+ warnx("\'.\' is dangerous in a login name");
+ for (; *p; ++p)
+ if (isupper(*p)) {
+ warnx("upper-case letters are dangerous in a login name");
+ break;
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_passwd(char *p, struct passwd *pw, ENTRY *ep __unused)
+{
+#ifndef OPEN_DIRECTORY
+ if (!(pw->pw_passwd = strdup(p))) {
+ warnx("can't save password entry");
+ return (-1);
+ }
+#endif
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_uid(char *p, struct passwd *pw, ENTRY *ep __unused)
+{
+ uid_t id;
+ char *np;
+
+ if (!*p) {
+ warnx("empty uid field");
+ return (-1);
+ }
+ if (!isdigit(*p)) {
+ warnx("illegal uid");
+ return (-1);
+ }
+ errno = 0;
+ id = (uid_t)strtoul(p, &np, 10);
+ if (*np || (id == (uid_t)ULONG_MAX && errno == ERANGE)) {
+ warnx("illegal uid");
+ return (-1);
+ }
+#ifndef OPEN_DIRECTORY
+ pw->pw_uid = id;
+#endif
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_gid(char *p, struct passwd *pw, ENTRY *ep __unused)
+{
+ struct group *gr;
+ gid_t id;
+ char *np;
+
+ if (!*p) {
+ warnx("empty gid field");
+ return (-1);
+ }
+ if (!isdigit(*p)) {
+ if (!(gr = getgrnam(p))) {
+ warnx("unknown group %s", p);
+ return (-1);
+ }
+#ifndef OPEN_DIRECTORY
+ pw->pw_gid = gr->gr_gid;
+#endif
+ return (0);
+ }
+ errno = 0;
+ id = (gid_t)strtoul(p, &np, 10);
+ if (*np || (id == (uid_t)ULONG_MAX && errno == ERANGE)) {
+ warnx("illegal gid");
+ return (-1);
+ }
+#ifndef OPEN_DIRECTORY
+ pw->pw_gid = id;
+#endif
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_class(char *p, struct passwd *pw, ENTRY *ep __unused)
+{
+#ifndef OPEN_DIRECTORY
+ if (!(pw->pw_class = strdup(p))) {
+ warnx("can't save entry");
+ return (-1);
+ }
+#endif
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_change(char *p, struct passwd *pw, ENTRY *ep __unused)
+{
+#ifndef OPEN_DIRECTORY
+ if (!atot(p, &pw->pw_change))
+ return (0);
+ warnx("illegal date for change field");
+#endif
+ return (-1);
+}
+
+/* ARGSUSED */
+int
+p_expire(char *p, struct passwd *pw, ENTRY *ep __unused)
+{
+#ifndef OPEN_DIRECTORY
+ if (!atot(p, &pw->pw_expire))
+ return (0);
+ warnx("illegal date for expire field");
+#endif
+ return (-1);
+}
+
+/* ARGSUSED */
+int
+p_gecos(char *p, struct passwd *pw __unused, ENTRY *ep)
+{
+#ifndef OPEN_DIRECTORY
+ if (!(ep->save = strdup(p))) {
+ warnx("can't save entry");
+ return (-1);
+ }
+#endif
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_hdir(char *p, struct passwd *pw, ENTRY *ep __unused)
+{
+ if (!*p) {
+ warnx("empty home directory field");
+ return (-1);
+ }
+#ifndef OPEN_DIRECTORY
+ if (!(pw->pw_dir = strdup(p))) {
+ warnx("can't save entry");
+ return (-1);
+ }
+#endif
+ return (0);
+}
+
+
+/* ARGSUSED */
+int
+p_shell(char *p, struct passwd *pw, ENTRY *ep __unused)
+{
+ struct stat sbuf;
+#ifdef OPEN_DIRECTORY
+ struct passwd lpw;
+ pw = &lpw;
+ memset(pw, 0, sizeof(lpw));
+ pw->pw_shell = p;
+#endif
+
+#ifndef OPEN_DIRECTORY
+ if (!*p) {
+ pw->pw_shell = strdup(_PATH_BSHELL);
+ return (0);
+ }
+ /* only admin can change from or to "restricted" shells */
+ if (!master_mode && pw->pw_shell && !ok_shell(pw->pw_shell)) {
+ warnx("%s: current shell non-standard", pw->pw_shell);
+ return (-1);
+ }
+#endif /* !OPEN_DIRECTORY */
+ if (!ok_shell(p)) {
+ if (!master_mode) {
+ warnx("%s: non-standard shell", p);
+ return (-1);
+ }
+#ifndef OPEN_DIRECTORY
+ pw->pw_shell = strdup(p);
+#endif
+ }
+#ifndef OPEN_DIRECTORY
+ else
+ pw->pw_shell = dup_shell(p);
+ if (!pw->pw_shell) {
+ warnx("can't save entry");
+ return (-1);
+ }
+#endif
+ if (stat(pw->pw_shell, &sbuf) < 0) {
+ if (errno == ENOENT)
+ warnx("WARNING: shell '%s' does not exist",
+ pw->pw_shell);
+ else
+ warn("WARNING: can't stat shell '%s'", pw->pw_shell);
+ return (0);
+ }
+ if (!S_ISREG(sbuf.st_mode)) {
+ warnx("WARNING: shell '%s' is not a regular file",
+ pw->pw_shell);
+ return (0);
+ }
+ if ((sbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) == 0) {
+ warnx("WARNING: shell '%s' is not executable", pw->pw_shell);
+ return (0);
+ }
+ return (0);
+}
+
+#ifdef OPEN_DIRECTORY
+#include <uuid/uuid.h>
+/* ARGSUSED */
+int
+p_uuid(char *p, struct passwd *pw __unused, ENTRY *ep)
+{
+ uuid_t uu;
+ if (uuid_parse(p, uu) != 0) {
+ warnx("invalid UUID");
+ return (-1);
+ }
+ return (0);
+}
+
+void
+display_string(CFDictionaryRef attrs, CFStringRef attrName, const char* prompt, FILE *fp)
+{
+ CFTypeRef value = CFSTR("");
+ CFArrayRef values = CFDictionaryGetValue(attrs, attrName);
+ if (values) {
+ value = CFArrayGetCount(values) > 0 ? CFArrayGetValueAtIndex(values, 0) : NULL;
+ if (value && CFGetTypeID(value) != CFStringGetTypeID()) value = NULL;
+ }
+ cfprintf(fp, "%s: %@\n", prompt, value);
+}
+#endif /* OPEN_DIRECTORY */
diff --git a/system_cmds/chpass.tproj/open_directory.c b/system_cmds/chpass.tproj/open_directory.c
new file mode 100644
index 0000000..5949eac
--- /dev/null
+++ b/system_cmds/chpass.tproj/open_directory.c
@@ -0,0 +1,205 @@
+#ifdef OPEN_DIRECTORY
+#include "open_directory.h"
+#include "chpass.h"
+#include <err.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+#include <OpenDirectory/OpenDirectory.h>
+#include <OpenDirectory/OpenDirectoryPriv.h>
+
+/*---------------------------------------------------------------------------
+ * PUBLIC setrestricted - sets the restricted flag
+ *---------------------------------------------------------------------------*/
+void
+setrestricted(CFDictionaryRef attrs)
+{
+ const char* user_allowed[] = { "shell", "full name", "office location", "office phone", "home phone", "picture", NULL };
+ const char* root_restricted[] = { "password", "change", "expire", "class", NULL };
+ ENTRY* ep;
+ const char** pp;
+ int restrict_by_default = !master_mode;
+
+ // for ordinary users, everything is restricted except for the values
+ // expressly permitted above
+ // for root, everything is permitted except for the values expressly
+ // restricted above
+
+ for (ep = list; ep->prompt; ep++) {
+ ep->restricted = restrict_by_default;
+ pp = restrict_by_default ? user_allowed : root_restricted;
+ for (; *pp; pp++) {
+ if (strncasecmp(ep->prompt, *pp, ep->len) == 0) {
+ ep->restricted = !restrict_by_default;
+ break;
+ }
+ }
+
+ // If not root, then it is only permitted to change the shell
+ // when the original value is one of the approved shells.
+ // Otherwise, the assumption is that root has given this user
+ // a restricted shell which they must not change away from.
+ if (restrict_by_default && strcmp(ep->prompt, "shell") == 0) {
+ ep->restricted = 1;
+ CFArrayRef values = CFDictionaryGetValue(attrs, kODAttributeTypeUserShell);
+ CFTypeRef value = values && CFArrayGetCount(values) > 0 ? CFArrayGetValueAtIndex(values, 0) : NULL;
+ if (value && CFGetTypeID(value) == CFStringGetTypeID()) {
+ size_t size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), kCFStringEncodingUTF8)+1;
+ char* shell = malloc(size);
+ if (CFStringGetCString(value, shell, size, kCFStringEncodingUTF8)) {
+ if (ok_shell(shell)) {
+ ep->restricted = 0;
+ }
+ }
+ }
+ }
+ }
+}
+
+static CFStringRef
+prompt_passwd(CFStringRef user)
+{
+ CFStringRef result = NULL;
+ CFStringRef prompt = CFStringCreateWithFormat(NULL, NULL, CFSTR("Password for %@: "), user);
+ size_t prompt_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(prompt), kCFStringEncodingUTF8);
+ char* buf = malloc(prompt_size);
+ CFStringGetCString(prompt, buf, prompt_size, kCFStringEncodingUTF8);
+ char* pass = getpass(buf);
+ result = CFStringCreateWithCString(NULL, pass, kCFStringEncodingUTF8);
+ memset(pass, 0, strlen(pass));
+ free(buf);
+ CFRelease(prompt);
+ return result;
+}
+
+static void
+show_error(CFErrorRef error) {
+ if (error) {
+ CFStringRef desc = CFErrorCopyDescription(error);
+ if (desc) {
+ cfprintf(stderr, "%s: %@", progname, desc);
+ CFRelease(desc);
+ }
+ desc = CFErrorCopyFailureReason(error);
+ if (desc) cfprintf(stderr, " %@", desc);
+
+ desc = CFErrorCopyRecoverySuggestion(error);
+ if (desc) cfprintf(stderr, " %@", desc);
+
+ fprintf(stderr, "\n");
+ }
+}
+
+ODRecordRef
+odGetUser(CFStringRef location, CFStringRef authname, CFStringRef user, CFDictionaryRef* attrs)
+{
+ ODNodeRef node = NULL;
+ ODRecordRef rec = NULL;
+ CFErrorRef error = NULL;
+
+ assert(attrs);
+
+ /*
+ * Open the specified node, or perform a search.
+ * Copy the record and put the record's location into DSPath.
+ */
+ if (location) {
+ node = ODNodeCreateWithName(NULL, kODSessionDefault, location, &error);
+ } else {
+ node = ODNodeCreateWithNodeType(NULL, kODSessionDefault, kODNodeTypeAuthentication, &error);
+ }
+ if (node) {
+ CFTypeRef vals[] = { kODAttributeTypeStandardOnly };
+ CFArrayRef desiredAttrs = CFArrayCreate(NULL, vals, 1, &kCFTypeArrayCallBacks);
+ rec = ODNodeCopyRecord(node, kODRecordTypeUsers, user, desiredAttrs, &error);
+ if (desiredAttrs) CFRelease(desiredAttrs);
+ CFRelease(node);
+ }
+ if (rec) {
+ *attrs = ODRecordCopyDetails(rec, NULL, &error);
+ if (*attrs) {
+ CFArrayRef values = CFDictionaryGetValue(*attrs, kODAttributeTypeMetaNodeLocation);
+ DSPath = (values && CFArrayGetCount(values) > 0) ? CFArrayGetValueAtIndex(values, 0) : NULL;
+ }
+
+ /*
+ * Prompt for a password if -u was specified,
+ * or if we are not root,
+ * or if we are updating something not on the
+ * local node.
+ */
+ if (authname || !master_mode ||
+ (DSPath && CFStringCompareWithOptions(DSPath, CFSTR("/Local/"), CFRangeMake(0, 7), 0) != kCFCompareEqualTo)) {
+
+ CFStringRef password = NULL;
+
+ if (!authname) authname = user;
+
+ password = prompt_passwd(authname);
+ if (!ODRecordSetNodeCredentials(rec, authname, password, &error)) {
+ CFRelease(rec);
+ rec = NULL;
+ }
+ }
+ }
+
+ if (error) show_error(error);
+ return rec;
+}
+
+void
+odUpdateUser(ODRecordRef rec, CFDictionaryRef attrs_orig, CFDictionaryRef attrs)
+{
+ CFErrorRef error = NULL;
+ int updated = 0;
+ ENTRY* ep;
+
+ for (ep = list; ep->prompt; ep++) {
+
+ // Nothing to update
+ if (!rec || !attrs_orig || !attrs) break;
+
+ // No need to update if entry is restricted
+ if (ep->restricted) continue;
+
+ CFArrayRef values_orig = CFDictionaryGetValue(attrs_orig, *ep->attrName);
+ CFTypeRef value_orig = values_orig && CFArrayGetCount(values_orig) ? CFArrayGetValueAtIndex(values_orig, 0) : NULL;
+ CFTypeRef value = CFDictionaryGetValue(attrs, *ep->attrName);
+
+ // No need to update if both values are the same
+ if (value == value_orig) continue;
+
+ // No need to update if strings are equal
+ if (value && value_orig) {
+ if (CFGetTypeID(value_orig) == CFStringGetTypeID() &&
+ CFStringCompare(value_orig, value, 0) == kCFCompareEqualTo) continue;
+ }
+
+ // No need to update if empty string replaces NULL
+ if (!value_orig && value) {
+ if (CFStringGetLength(value) == 0) continue;
+ }
+
+ // Needs update
+ if (value) {
+ // if new value is an empty string, send an empty dictionary which will delete the property.
+ CFIndex count = CFEqual(value, CFSTR("")) ? 0 : 1;
+ CFTypeRef vals[] = { value };
+ CFArrayRef values = CFArrayCreate(NULL, vals, count, &kCFTypeArrayCallBacks);
+ if (values && ODRecordSetValue(rec, *ep->attrName, values, &error)) {
+ updated = 1;
+ }
+ if (values) CFRelease(values);
+ if (error) show_error(error);
+ }
+ }
+
+ if (updated) {
+ updated = ODRecordSynchronize(rec, &error);
+ if (error) show_error(error);
+ }
+ if (!updated) {
+ fprintf(stderr, "%s: no changes made\n", progname);
+ }
+}
+#endif /* OPEN_DIRECTORY */
diff --git a/system_cmds/chpass.tproj/open_directory.h b/system_cmds/chpass.tproj/open_directory.h
new file mode 100644
index 0000000..e6011b7
--- /dev/null
+++ b/system_cmds/chpass.tproj/open_directory.h
@@ -0,0 +1,13 @@
+#ifndef _OPEN_DIRECTORY_H_
+#define _OPEN_DIRECTORY_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <OpenDirectory/OpenDirectory.h>
+
+extern void setrestricted(CFDictionaryRef attrs);
+
+ODRecordRef odGetUser(CFStringRef location, CFStringRef authname, CFStringRef user, CFDictionaryRef* attrs);
+
+void odUpdateUser(ODRecordRef rec, CFDictionaryRef attrs_orig, CFDictionaryRef attrs);
+
+#endif /* _OPEN_DIRECTORY_H_ */
diff --git a/system_cmds/chpass.tproj/pw_copy.c b/system_cmds/chpass.tproj/pw_copy.c
new file mode 100644
index 0000000..8aa74a6
--- /dev/null
+++ b/system_cmds/chpass.tproj/pw_copy.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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, 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.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * 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.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This module is used to copy the master password file, replacing a single
+ * record, by chpass(1) and passwd(1).
+ */
+
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pw_util.h"
+#include "pw_copy.h"
+
+extern char *tempname;
+
+void
+pw_copy(int ffd, int tfd, struct passwd *pw)
+{
+ FILE *from, *to;
+ int done;
+ char *p, buf[8192];
+
+ if (!(from = fdopen(ffd, "r")))
+ pw_error(_PATH_MASTERPASSWD, 1, 1);
+ if (!(to = fdopen(tfd, "w")))
+ pw_error(tempname, 1, 1);
+
+ for (done = 0; fgets(buf, sizeof(buf), from);) {
+ if (!strchr(buf, '\n')) {
+ warnx("%s: line too long", _PATH_MASTERPASSWD);
+ pw_error(NULL, 0, 1);
+ }
+#if defined(__APPLE__)
+ if (done || (buf[0] == '#')) {
+#else
+ if (done) {
+#endif
+ (void)fprintf(to, "%s", buf);
+ if (ferror(to))
+ goto err;
+ continue;
+ }
+ if (!(p = strchr(buf, ':'))) {
+ warnx("%s: corrupted entry", _PATH_MASTERPASSWD);
+ pw_error(NULL, 0, 1);
+ }
+ *p = '\0';
+ if (strcmp(buf, pw->pw_name)) {
+ *p = ':';
+ (void)fprintf(to, "%s", buf);
+ if (ferror(to))
+ goto err;
+ continue;
+ }
+ (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
+ pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
+ pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
+ pw->pw_dir, pw->pw_shell);
+ done = 1;
+ if (ferror(to))
+ goto err;
+ }
+ if (!done)
+ (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
+ pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
+ pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
+ pw->pw_dir, pw->pw_shell);
+
+ if (ferror(to))
+err: pw_error(NULL, 1, 1);
+ (void)fclose(to);
+}
diff --git a/system_cmds/chpass.tproj/pw_copy.h b/system_cmds/chpass.tproj/pw_copy.h
new file mode 100644
index 0000000..7c7af0a
--- /dev/null
+++ b/system_cmds/chpass.tproj/pw_copy.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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, 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.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * 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.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+void pw_copy __P((int, int, struct passwd *));
diff --git a/system_cmds/chpass.tproj/table.c b/system_cmds/chpass.tproj/table.c
new file mode 100644
index 0000000..ba1523d
--- /dev/null
+++ b/system_cmds/chpass.tproj/table.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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, 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.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * 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.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#if 0
+#ifndef lint
+static const char sccsid[] = "@(#)table.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/chpass/table.c,v 1.10 2003/05/03 19:44:45 obrien Exp $");
+#endif
+
+#include <sys/types.h>
+#include <stddef.h>
+#include "chpass.h"
+
+char e1[] = ": ";
+char e2[] = ":,";
+
+#ifdef OPEN_DIRECTORY
+#include "open_directory.h"
+
+ENTRY list[] = {
+ { "Login", display_string, p_login, 1, 5, e1, &kODAttributeTypeRecordName, },
+ { "Password", display_string, p_passwd, 1, 8, e1, &kODAttributeTypePassword, },
+ { "Uid [#]", display_string, p_uid, 1, 3, e1, &kODAttributeTypeUniqueID, },
+ { "Gid [# or name]", display_string, p_gid, 1, 3, e1, &kODAttributeTypePrimaryGroupID, },
+ { "Generated uid", display_string, p_uuid, 1, 13, NULL, &kODAttributeTypeGUID, },
+#if 0
+ { "Change [month day year]", display_time, p_change, 1, 6, NULL, CFSTR(kDS1AttrChange), },
+ { "Expire [month day year]", display_time, p_expire, 1, 6, NULL, kODAttributeTypeExpire, },
+ { "Class", display_string, p_class, 0, 5, e1, CFSTR(""), "Class" },
+#endif
+ { "Home directory", display_string, p_hdir, 1, 14, e1, &kODAttributeTypeNFSHomeDirectory, },
+ { "Shell", display_string, p_shell, 1, 5, e1, &kODAttributeTypeUserShell, },
+ { "Full Name", display_string, p_gecos, 1, 9, e2, &kODAttributeTypeFullName, },
+ { "Office Location", display_string, p_gecos, 1, 8, e2, &kODAttributeTypeBuilding, },
+ { "Office Phone", display_string, p_gecos, 1, 12, e2, &kODAttributeTypePhoneNumber, },
+ { "Home Phone", display_string, p_gecos, 1, 10, e2, &kODAttributeTypeHomePhoneNumber, },
+ { NULL, NULL, NULL, 0, 0, NULL, NULL,},
+};
+#else /* OPEN_DIRECTORY */
+ENTRY list[] = {
+ { "login", p_login, 1, 5, e1, NULL },
+ { "password", p_passwd, 1, 8, e1, NULL },
+ { "uid", p_uid, 1, 3, e1, NULL },
+ { "gid", p_gid, 1, 3, e1, NULL },
+ { "class", p_class, 1, 5, e1, NULL },
+ { "change", p_change, 1, 6, NULL, NULL },
+ { "expire", p_expire, 1, 6, NULL, NULL },
+#ifdef RESTRICT_FULLNAME_CHANGE /* do not allow fullname changes */
+ { "full name", p_gecos, 1, 9, e2, NULL },
+#else
+ { "full name", p_gecos, 0, 9, e2, NULL },
+#endif
+ { "office phone", p_gecos, 0, 12, e2, NULL },
+ { "home phone", p_gecos, 0, 10, e2, NULL },
+ { "office location", p_gecos, 0, 15, e2, NULL },
+ { "other information", p_gecos, 0, 11, e1, NULL },
+ { "home directory", p_hdir, 1, 14, e1, NULL },
+ { "shell", p_shell, 0, 5, e1, NULL },
+ { NULL, NULL, 0, 0, NULL, NULL },
+};
+#endif /* OPEN_DIRECTORY */
diff --git a/system_cmds/chpass.tproj/util.c b/system_cmds/chpass.tproj/util.c
new file mode 100644
index 0000000..d3ab03f
--- /dev/null
+++ b/system_cmds/chpass.tproj/util.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 1999-2016 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * 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, 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) 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
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * 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.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)util.c 8.4 (Berkeley) 4/2/94";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/chpass/util.c,v 1.13 2004/01/18 21:46:39 charnier Exp $");
+#endif
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <tzfile.h>
+#include <unistd.h>
+
+#include "chpass.h"
+
+#if OPEN_DIRECTORY
+#include <err.h>
+#include <paths.h>
+#include <sys/stat.h>
+#include "open_directory.h"
+
+char* tempname;
+#endif /* OPEN_DIRECTORY */
+
+static const char *months[] =
+ { "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November",
+ "December", NULL };
+
+char *
+ttoa(time_t tval)
+{
+ struct tm *tp;
+ static char tbuf[50];
+
+ if (tval) {
+ tp = localtime(&tval);
+ (void)sprintf(tbuf, "%s %d, %d", months[tp->tm_mon],
+ tp->tm_mday, tp->tm_year + TM_YEAR_BASE);
+ }
+ else
+ *tbuf = '\0';
+ return (tbuf);
+}
+
+int
+atot(char *p, time_t *store)
+{
+ static struct tm *lt;
+ char *t;
+ const char **mp;
+ time_t tval;
+ int day, month, year;
+
+ if (!*p) {
+ *store = 0;
+ return (0);
+ }
+ if (!lt) {
+ unsetenv("TZ");
+ (void)time(&tval);
+ lt = localtime(&tval);
+ }
+ if (!(t = strtok(p, " \t")))
+ goto bad;
+ if (isdigit(*t)) {
+ month = atoi(t);
+ } else {
+ for (mp = months;; ++mp) {
+ if (!*mp)
+ goto bad;
+ if (!strncasecmp(*mp, t, 3)) {
+ month = (int)(mp - months + 1);
+ break;
+ }
+ }
+ }
+ if (!(t = strtok((char *)NULL, " \t,")) || !isdigit(*t))
+ goto bad;
+ day = atoi(t);
+ if (!(t = strtok((char *)NULL, " \t,")) || !isdigit(*t))
+ goto bad;
+ year = atoi(t);
+ if (day < 1 || day > 31 || month < 1 || month > 12)
+ goto bad;
+ /* Allow two digit years 1969-2068 */
+ if (year < 69)
+ year += 2000;
+ else if (year < 100)
+ year += TM_YEAR_BASE;
+ if (year < EPOCH_YEAR)
+bad: return (1);
+ lt->tm_year = year - TM_YEAR_BASE;
+ lt->tm_mon = month - 1;
+ lt->tm_mday = day;
+ lt->tm_hour = 0;
+ lt->tm_min = 0;
+ lt->tm_sec = 0;
+ lt->tm_isdst = -1;
+ if ((tval = mktime(lt)) < 0)
+ return (1);
+ *store = tval;
+ return (0);
+}
+
+int
+ok_shell(char *name)
+{
+#ifdef __APPLE__
+ char *sh;
+#else
+ char *p, *sh;
+#endif
+
+ setusershell();
+ while ((sh = getusershell())) {
+ if (!strcmp(name, sh)) {
+ endusershell();
+ return (1);
+ }
+#ifndef __APPLE__
+ /* allow just shell name, but use "real" path */
+ if ((p = strrchr(sh, '/')) && strcmp(name, p + 1) == 0) {
+ endusershell();
+ return (1);
+ }
+#endif
+ }
+ endusershell();
+ return (0);
+}
+
+char *
+dup_shell(char *name)
+{
+ char *p, *sh, *ret;
+
+ setusershell();
+ while ((sh = getusershell())) {
+ if (!strcmp(name, sh)) {
+ endusershell();
+ return (strdup(name));
+ }
+ /* allow just shell name, but use "real" path */
+ if ((p = strrchr(sh, '/')) && strcmp(name, p + 1) == 0) {
+ ret = strdup(sh);
+ endusershell();
+ return (ret);
+ }
+ }
+ endusershell();
+ return (NULL);
+}
+
+#if OPEN_DIRECTORY
+int
+cfprintf(FILE* file, const char* format, ...)
+{
+ char* cstr;
+ int result = 0;
+ va_list args;
+ va_start(args, format);
+ CFStringRef formatStr = CFStringCreateWithCStringNoCopy(NULL, format, kCFStringEncodingUTF8, kCFAllocatorNull);
+ if (formatStr) {
+ CFStringRef str = CFStringCreateWithFormatAndArguments(NULL, NULL, formatStr, args);
+ if (str) {
+ size_t size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1;
+ va_end(args);
+ cstr = malloc(size);
+ if (cstr && CFStringGetCString(str, cstr, size, kCFStringEncodingUTF8)) {
+ result = fprintf(file, "%s", cstr);
+ free(cstr);
+ }
+ CFRelease(str);
+ }
+ CFRelease(formatStr);
+ }
+ return result;
+}
+
+/*
+ * Edit the temp file. Return -1 on error, >0 if the file was modified, 0
+ * if it was not.
+ */
+int
+editfile(const char* tfn)
+{
+ struct sigaction sa, sa_int, sa_quit;
+ sigset_t oldsigset, sigset;
+ struct stat st1, st2;
+ const char *p, *editor;
+ int pstat;
+ pid_t editpid;
+
+ if ((editor = getenv("EDITOR")) == NULL)
+ editor = _PATH_VI;
+ if ((p = strrchr(editor, '/')))
+ ++p;
+ else
+ p = editor;
+
+ if (stat(tfn, &st1) == -1)
+ return (-1);
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGINT, &sa, &sa_int);
+ sigaction(SIGQUIT, &sa, &sa_quit);
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &sigset, &oldsigset);
+ switch ((editpid = fork())) {
+ case -1:
+ return (-1);
+ case 0:
+ sigaction(SIGINT, &sa_int, NULL);
+ sigaction(SIGQUIT, &sa_quit, NULL);
+ sigprocmask(SIG_SETMASK, &oldsigset, NULL);
+ errno = 0;
+ if (!master_mode) {
+ (void)setgid(getgid());
+ (void)setuid(getuid());
+ }
+ execlp(editor, p, tfn, (char *)NULL);
+ _exit(errno);
+ default:
+ /* parent */
+ break;
+ }
+ for (;;) {
+ if (waitpid(editpid, &pstat, WUNTRACED) == -1) {
+ if (errno == EINTR)
+ continue;
+ unlink(tfn);
+ editpid = -1;
+ break;
+ } else if (WIFSTOPPED(pstat)) {
+ raise(WSTOPSIG(pstat));
+ } else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) {
+ editpid = -1;
+ break;
+ } else {
+ unlink(tfn);
+ editpid = -1;
+ break;
+ }
+ }
+ sigaction(SIGINT, &sa_int, NULL);
+ sigaction(SIGQUIT, &sa_quit, NULL);
+ sigprocmask(SIG_SETMASK, &oldsigset, NULL);
+ if (stat(tfn, &st2) == -1)
+ return (-1);
+ return (st1.st_mtime != st2.st_mtime);
+}
+
+#if 0
+static void
+pw_error(char *name, int err, int eval)
+{
+ if (err)
+ warn("%s", name);
+ exit(eval);
+}
+#endif
+
+#endif /* OPEN_DIRECTORY */