summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2014-04-28 07:50:45 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2014-04-28 07:50:45 +0000
commit2d2d383dd1e01034ed139c7d891c8c835d712e34 (patch)
tree202333b14f641a3f8a162ab015482fd8d6f3840b
parent458a332b6487c6e80893dbd053ecd9e0391b76cf (diff)
parentada4ded218239677fd4d3378d71bc8f378e000f1 (diff)
downloadpw-darwin-2d2d383dd1e01034ed139c7d891c8c835d712e34.tar.gz
pw-darwin-2d2d383dd1e01034ed139c7d891c8c835d712e34.tar.zst
pw-darwin-2d2d383dd1e01034ed139c7d891c8c835d712e34.zip
Merge head
-rw-r--r--chpass/chpass.1485
-rw-r--r--pw/pw.h1
-rw-r--r--pw/pw_conf.c503
-rw-r--r--pw/pw_group.c19
-rw-r--r--pw/pw_user.c44
5 files changed, 1031 insertions, 21 deletions
diff --git a/chpass/chpass.1 b/chpass/chpass.1
new file mode 100644
index 0000000..a97531e
--- /dev/null
+++ b/chpass/chpass.1
@@ -0,0 +1,485 @@
+.\" 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.
+.\" 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.1 8.2 (Berkeley) 12/30/93
+.\" $FreeBSD$
+.\"
+.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 s Ar newshell
+.Op user
+.Nm
+.Op Fl oly
+.Op Fl a Ar list
+.Op Fl p Ar encpass
+.Op Fl e Ar expiretime
+.Op Fl s Ar newshell
+.Op Fl d Ar domain
+.Op Fl h Ar host
+.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 chfn ,
+.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 was done in the interactive editor.
+.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 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 (1)
+.It Office Phone:
+user's office phone (1)
+.It Home Phone:
+user's home phone (1)
+.It Other Information:
+any locally defined parameters for user (1)
+.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 gid
+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 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
+gecos 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: .
+.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
+Once the information has been verified,
+.Nm
+uses
+.Xr pwd_mkdb 8
+to update the user database.
+.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.
+.Pp
+See
+.Xr pwd_mkdb 8
+for an explanation of the impact of setting the
+.Ev PW_SCAN_BIG_IDS
+environment variable.
+.Sh NIS INTERACTION
+The
+.Nm
+utility can also be used in conjunction with NIS, however some restrictions
+apply.
+Currently,
+.Nm
+can only make changes to the NIS passwd maps through
+.Xr rpc.yppasswdd 8 ,
+which normally only permits changes to a user's password, shell and GECOS
+fields.
+Except when invoked by the super-user on the NIS master server,
+.Nm
+(and, similarly,
+.Xr passwd 1 )
+cannot use the
+.Xr rpc.yppasswdd 8
+server to change other user information or
+add new records to the NIS passwd maps.
+Furthermore,
+.Xr rpc.yppasswdd 8
+requires password authentication before it will make any
+changes.
+The only user allowed to submit changes without supplying
+a password is the super-user on the NIS master server; all other users,
+including those with root privileges on NIS clients (and NIS slave
+servers) must enter a password.
+(The super-user on the NIS master is allowed to bypass these restrictions
+largely for convenience: a user with root access
+to the NIS master server already has the privileges required to make
+updates to the NIS maps, but editing the map source files by hand can
+be cumbersome.
+.Pp
+Note: these exceptions only apply when the NIS master server is a
+.Fx
+system).
+.Pp
+Consequently, except where noted, the following restrictions apply when
+.Nm
+is used with NIS:
+.Bl -enum -offset indent
+.It
+.Em "Only the shell and GECOS information may be changed" .
+All other
+fields are restricted, even when
+.Nm
+is invoked by the super-user.
+While support for
+changing other fields could be added, this would lead to
+compatibility problems with other NIS-capable systems.
+Even though the super-user may supply data for other fields
+while editing an entry, the extra information (other than the
+password -- see below) will be silently discarded.
+.Pp
+Exception: the super-user on the NIS master server is permitted to
+change any field.
+.It
+.Em "Password authentication is required" .
+The
+.Nm
+utility will prompt for the user's NIS password before effecting
+any changes.
+If the password is invalid, all changes will be
+discarded.
+.Pp
+Exception: the super-user on the NIS master server is allowed to
+submit changes without supplying a password.
+(The super-user may
+choose to turn off this feature using the
+.Fl o
+flag, described below.)
+.It
+.Em "Adding new records to the local password database is discouraged" .
+The
+.Nm
+utility will allow the administrator to add new records to the
+local password database while NIS is enabled, but this can lead to
+some confusion since the new records are appended to the end of
+the master password file, usually after the special NIS '+' entries.
+The administrator should use
+.Xr vipw 8
+to modify the local password
+file when NIS is running.
+.Pp
+The super-user on the NIS master server is permitted to add new records
+to the NIS password maps, provided the
+.Xr rpc.yppasswdd 8
+server has been started with the
+.Fl a
+flag to permitted additions (it refuses them by default).
+The
+.Nm
+utility tries to update the local password database by default; to update the
+NIS maps instead, invoke chpass with the
+.Fl y
+flag.
+.It
+.Em "Password changes are not permitted".
+Users should use
+.Xr passwd 1
+or
+.Xr yppasswd 1
+to change their NIS passwords.
+The super-user is allowed to specify
+a new password (even though the
+.Dq Password:
+field does not show
+up in the editor template, the super-user may add it back by hand),
+but even the super-user must supply the user's original password
+otherwise
+.Xr rpc.yppasswdd 8
+will refuse to update the NIS maps.
+.Pp
+Exception: the super-user on the NIS master server is permitted to
+change a user's NIS password with
+.Nm .
+.El
+.Pp
+There are also a few extra option flags that are available when
+.Nm
+is compiled with NIS support:
+.Bl -tag -width indent
+.It Fl l
+Force
+.Nm
+to modify the local copy of a user's password
+information in the event that a user exists in both
+the local and NIS databases.
+.It Fl y
+Opposite effect of
+.Fl l .
+This flag is largely redundant since
+.Nm
+operates on NIS entries by default if NIS is enabled.
+.It Fl d Ar domain
+Specify a particular NIS domain.
+The
+.Nm
+utility uses the system domain name by default, as set by the
+.Xr domainname 1
+utility.
+The
+.Fl d
+option can be used to override a default, or to specify a domain
+when the system domain name is not set.
+.It Fl h Ar host
+Specify the name or address of an NIS server to query.
+Normally,
+.Nm
+will communicate with the NIS master host specified in the
+.Pa master.passwd
+or
+.Pa passwd
+maps.
+On hosts that have not been configured as NIS clients, there is
+no way for the program to determine this information unless the user
+provides the hostname of a server.
+Note that the specified hostname need
+not be that of the NIS master server; the name of any server, master or
+slave, in a given NIS domain will do.
+.Pp
+When using the
+.Fl d
+option, the hostname defaults to
+.Dq localhost .
+The
+.Fl h
+option can be used in conjunction with the
+.Fl d
+option, in which case the user-specified hostname will override
+the default.
+.It Fl o
+Force the use of RPC-based updates when communicating with
+.Xr rpc.yppasswdd 8
+.Pq Dq old-mode .
+When invoked by the super-user on the NIS master server,
+.Nm
+allows unrestricted changes to the NIS passwd maps using dedicated,
+non-RPC-based mechanism (in this case, a
+.Ux
+domain socket).
+The
+.Fl o
+flag can be used to force
+.Nm
+to use the standard update mechanism instead.
+This option is provided
+mainly for testing purposes.
+.El
+.Sh FILES
+.Bl -tag -width /etc/master.passwd -compact
+.It Pa /etc/master.passwd
+the user database
+.It Pa /etc/passwd
+a Version 7 format password file
+.It Pa /etc/chpass.XXXXXX
+temporary copy of the password file
+.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 login.conf 5 ,
+.Xr passwd 5 ,
+.Xr pw 8 ,
+.Xr pwd_mkdb 8 ,
+.Xr vipw 8
+.Rs
+.%A Robert Morris
+.%A Ken Thompson
+.%T "UNIX Password security"
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 Reno .
+.Sh BUGS
+User information should (and eventually will) be stored elsewhere.
diff --git a/pw/pw.h b/pw/pw.h
index 1ff69a6..a1ed0c4 100644
--- a/pw/pw.h
+++ b/pw/pw.h
@@ -26,6 +26,7 @@
* $FreeBSD$
*/
+#define _WITH_GETLINE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/pw/pw_conf.c b/pw/pw_conf.c
new file mode 100644
index 0000000..1289b3e
--- /dev/null
+++ b/pw/pw_conf.c
@@ -0,0 +1,503 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT 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 DAVID L. NUGENT 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include "pw.h"
+
+#define debugging 0
+
+enum {
+ _UC_NONE,
+ _UC_DEFAULTPWD,
+ _UC_REUSEUID,
+ _UC_REUSEGID,
+ _UC_NISPASSWD,
+ _UC_DOTDIR,
+ _UC_NEWMAIL,
+ _UC_LOGFILE,
+ _UC_HOMEROOT,
+ _UC_HOMEMODE,
+ _UC_SHELLPATH,
+ _UC_SHELLS,
+ _UC_DEFAULTSHELL,
+ _UC_DEFAULTGROUP,
+ _UC_EXTRAGROUPS,
+ _UC_DEFAULTCLASS,
+ _UC_MINUID,
+ _UC_MAXUID,
+ _UC_MINGID,
+ _UC_MAXGID,
+ _UC_EXPIRE,
+ _UC_PASSWORD,
+ _UC_FIELDS
+};
+
+static char bourne_shell[] = "sh";
+
+static char *system_shells[_UC_MAXSHELLS] =
+{
+ bourne_shell,
+ "csh",
+ "tcsh"
+};
+
+static char const *booltrue[] =
+{
+ "yes", "true", "1", "on", NULL
+};
+static char const *boolfalse[] =
+{
+ "no", "false", "0", "off", NULL
+};
+
+static struct userconf config =
+{
+ 0, /* Default password for new users? (nologin) */
+ 0, /* Reuse uids? */
+ 0, /* Reuse gids? */
+ NULL, /* NIS version of the passwd file */
+ "/usr/share/skel", /* Where to obtain skeleton files */
+ NULL, /* Mail to send to new accounts */
+ "/var/log/userlog", /* Where to log changes */
+ "/home", /* Where to create home directory */
+ _DEF_DIRMODE, /* Home directory perms, modified by umask */
+ "/bin", /* Where shells are located */
+ system_shells, /* List of shells (first is default) */
+ bourne_shell, /* Default shell */
+ NULL, /* Default group name */
+ NULL, /* Default (additional) groups */
+ NULL, /* Default login class */
+ 1000, 32000, /* Allowed range of uids */
+ 1000, 32000, /* Allowed range of gids */
+ 0, /* Days until account expires */
+ 0, /* Days until password expires */
+ 0 /* size of default_group array */
+};
+
+static char const *comments[_UC_FIELDS] =
+{
+ "#\n# pw.conf - user/group configuration defaults\n#\n",
+ "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
+ "\n# Reuse gaps in uid sequence? (yes or no)\n",
+ "\n# Reuse gaps in gid sequence? (yes or no)\n",
+ "\n# Path to the NIS passwd file (blank or 'no' for none)\n",
+ "\n# Obtain default dotfiles from this directory\n",
+ "\n# Mail this file to new user (/etc/newuser.msg or no)\n",
+ "\n# Log add/change/remove information in this file\n",
+ "\n# Root directory in which $HOME directory is created\n",
+ "\n# Mode for the new $HOME directory, will be modified by umask\n",
+ "\n# Colon separated list of directories containing valid shells\n",
+ "\n# Comma separated list of available shells (without paths)\n",
+ "\n# Default shell (without path)\n",
+ "\n# Default group (leave blank for new group per user)\n",
+ "\n# Extra groups for new users\n",
+ "\n# Default login class for new users\n",
+ "\n# Range of valid default user ids\n",
+ NULL,
+ "\n# Range of valid default group ids\n",
+ NULL,
+ "\n# Days after which account expires (0=disabled)\n",
+ "\n# Days after which password expires (0=disabled)\n"
+};
+
+static char const *kwds[] =
+{
+ "",
+ "defaultpasswd",
+ "reuseuids",
+ "reusegids",
+ "nispasswd",
+ "skeleton",
+ "newmail",
+ "logfile",
+ "home",
+ "homemode",
+ "shellpath",
+ "shells",
+ "defaultshell",
+ "defaultgroup",
+ "extragroups",
+ "defaultclass",
+ "minuid",
+ "maxuid",
+ "mingid",
+ "maxgid",
+ "expire_days",
+ "password_days",
+ NULL
+};
+
+static char *
+unquote(char const * str)
+{
+ if (str && (*str == '"' || *str == '\'')) {
+ char *p = strchr(str + 1, *str);
+
+ if (p != NULL)
+ *p = '\0';
+ return (char *) (*++str ? str : NULL);
+ }
+ return (char *) str;
+}
+
+int
+boolean_val(char const * str, int dflt)
+{
+ if ((str = unquote(str)) != NULL) {
+ int i;
+
+ for (i = 0; booltrue[i]; i++)
+ if (strcmp(str, booltrue[i]) == 0)
+ return 1;
+ for (i = 0; boolfalse[i]; i++)
+ if (strcmp(str, boolfalse[i]) == 0)
+ return 0;
+
+ /*
+ * Special cases for defaultpassword
+ */
+ if (strcmp(str, "random") == 0)
+ return -1;
+ if (strcmp(str, "none") == 0)
+ return -2;
+ }
+ return dflt;
+}
+
+char const *
+boolean_str(int val)
+{
+ if (val == -1)
+ return "random";
+ else if (val == -2)
+ return "none";
+ else
+ return val ? booltrue[0] : boolfalse[0];
+}
+
+char *
+newstr(char const * p)
+{
+ char *q = NULL;
+
+ if ((p = unquote(p)) != NULL) {
+ int l = strlen(p) + 1;
+
+ if ((q = malloc(l)) != NULL)
+ memcpy(q, p, l);
+ }
+ return q;
+}
+
+#define LNBUFSZ 1024
+
+
+struct userconf *
+read_userconfig(char const * file)
+{
+ FILE *fp;
+ char *buf, *p;
+ size_t linecap;
+ ssize_t linelen;
+
+ buf = NULL;
+ linecap = 0;
+
+ extendarray(&config.groups, &config.numgroups, 200);
+ memset(config.groups, 0, config.numgroups * sizeof(char *));
+ if (file == NULL)
+ file = _PATH_PW_CONF;
+
+ if ((fp = fopen(file, "r")) != NULL) {
+ while ((linelen = getline(&buf, &linecap, fp)) > 0) {
+ if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
+ static char const toks[] = " \t\r\n,=";
+ char *q = strtok(NULL, toks);
+ int i = 0;
+ mode_t *modeset;
+
+ while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
+ ++i;
+#if debugging
+ if (i == _UC_FIELDS)
+ printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
+ else
+ printf("Got kwd[%s]=%s\n", p, q);
+#endif
+ switch (i) {
+ case _UC_DEFAULTPWD:
+ config.default_password = boolean_val(q, 1);
+ break;
+ case _UC_REUSEUID:
+ config.reuse_uids = boolean_val(q, 0);
+ break;
+ case _UC_REUSEGID:
+ config.reuse_gids = boolean_val(q, 0);
+ break;
+ case _UC_NISPASSWD:
+ config.nispasswd = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_DOTDIR:
+ config.dotdir = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_NEWMAIL:
+ config.newmail = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_LOGFILE:
+ config.logfile = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_HOMEROOT:
+ config.home = (q == NULL || !boolean_val(q, 1))
+ ? "/home" : newstr(q);
+ break;
+ case _UC_HOMEMODE:
+ modeset = setmode(q);
+ config.homemode = (q == NULL || !boolean_val(q, 1))
+ ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
+ free(modeset);
+ break;
+ case _UC_SHELLPATH:
+ config.shelldir = (q == NULL || !boolean_val(q, 1))
+ ? "/bin" : newstr(q);
+ break;
+ case _UC_SHELLS:
+ for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
+ system_shells[i] = newstr(q);
+ if (i > 0)
+ while (i < _UC_MAXSHELLS)
+ system_shells[i++] = NULL;
+ break;
+ case _UC_DEFAULTSHELL:
+ config.shell_default = (q == NULL || !boolean_val(q, 1))
+ ? (char *) bourne_shell : newstr(q);
+ break;
+ case _UC_DEFAULTGROUP:
+ q = unquote(q);
+ config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
+ ? NULL : newstr(q);
+ break;
+ case _UC_EXTRAGROUPS:
+ for (i = 0; q != NULL; q = strtok(NULL, toks)) {
+ if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
+ config.groups[i++] = newstr(q);
+ }
+ if (i > 0)
+ while (i < config.numgroups)
+ config.groups[i++] = NULL;
+ break;
+ case _UC_DEFAULTCLASS:
+ config.default_class = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_MINUID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.min_uid = (uid_t) atol(q);
+ break;
+ case _UC_MAXUID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.max_uid = (uid_t) atol(q);
+ break;
+ case _UC_MINGID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.min_gid = (gid_t) atol(q);
+ break;
+ case _UC_MAXGID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.max_gid = (gid_t) atol(q);
+ break;
+ case _UC_EXPIRE:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.expire_days = atoi(q);
+ break;
+ case _UC_PASSWORD:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.password_days = atoi(q);
+ break;
+ case _UC_FIELDS:
+ case _UC_NONE:
+ break;
+ }
+ }
+ }
+ if (linecap > 0)
+ free(buf);
+ fclose(fp);
+ }
+ return &config;
+}
+
+
+int
+write_userconfig(char const * file)
+{
+ int fd;
+
+ if (file == NULL)
+ file = _PATH_PW_CONF;
+
+ if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
+ FILE *fp;
+
+ if ((fp = fdopen(fd, "w")) == NULL)
+ close(fd);
+ else {
+ int i, j, k;
+ int len = LNBUFSZ;
+ char *buf = malloc(len);
+
+ for (i = _UC_NONE; i < _UC_FIELDS; i++) {
+ int quote = 1;
+ char const *val = buf;
+
+ *buf = '\0';
+ switch (i) {
+ case _UC_DEFAULTPWD:
+ val = boolean_str(config.default_password);
+ break;
+ case _UC_REUSEUID:
+ val = boolean_str(config.reuse_uids);
+ break;
+ case _UC_REUSEGID:
+ val = boolean_str(config.reuse_gids);
+ break;
+ case _UC_NISPASSWD:
+ val = config.nispasswd ? config.nispasswd : "";
+ quote = 0;
+ break;
+ case _UC_DOTDIR:
+ val = config.dotdir ? config.dotdir : boolean_str(0);
+ break;
+ case _UC_NEWMAIL:
+ val = config.newmail ? config.newmail : boolean_str(0);
+ break;
+ case _UC_LOGFILE:
+ val = config.logfile ? config.logfile : boolean_str(0);
+ break;
+ case _UC_HOMEROOT:
+ val = config.home;
+ break;
+ case _UC_HOMEMODE:
+ sprintf(buf, "%04o", config.homemode);
+ quote = 0;
+ break;
+ case _UC_SHELLPATH:
+ val = config.shelldir;
+ break;
+ case _UC_SHELLS:
+ for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
+ char lbuf[64];
+ int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
+ if (l < 0)
+ l = 0;
+ if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
+ strcpy(buf + k, lbuf);
+ k += l;
+ }
+ }
+ quote = 0;
+ break;
+ case _UC_DEFAULTSHELL:
+ val = config.shell_default ? config.shell_default : bourne_shell;
+ break;
+ case _UC_DEFAULTGROUP:
+ val = config.default_group ? config.default_group : "";
+ break;
+ case _UC_EXTRAGROUPS:
+ extendarray(&config.groups, &config.numgroups, 200);
+ for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
+ char lbuf[64];
+ int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
+ if (l < 0)
+ l = 0;
+ if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
+ strcpy(buf + k, lbuf);
+ k += l;
+ }
+ }
+ quote = 0;
+ break;
+ case _UC_DEFAULTCLASS:
+ val = config.default_class ? config.default_class : "";
+ break;
+ case _UC_MINUID:
+ sprintf(buf, "%lu", (unsigned long) config.min_uid);
+ quote = 0;
+ break;
+ case _UC_MAXUID:
+ sprintf(buf, "%lu", (unsigned long) config.max_uid);
+ quote = 0;
+ break;
+ case _UC_MINGID:
+ sprintf(buf, "%lu", (unsigned long) config.min_gid);
+ quote = 0;
+ break;
+ case _UC_MAXGID:
+ sprintf(buf, "%lu", (unsigned long) config.max_gid);
+ quote = 0;
+ break;
+ case _UC_EXPIRE:
+ sprintf(buf, "%d", config.expire_days);
+ quote = 0;
+ break;
+ case _UC_PASSWORD:
+ sprintf(buf, "%d", config.password_days);
+ quote = 0;
+ break;
+ case _UC_NONE:
+ break;
+ }
+
+ if (comments[i])
+ fputs(comments[i], fp);
+
+ if (*kwds[i]) {
+ if (quote)
+ fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
+ else
+ fprintf(fp, "%s = %s\n", kwds[i], val);
+#if debugging
+ printf("WROTE: %s = %s\n", kwds[i], val);
+#endif
+ }
+ }
+ free(buf);
+ return fclose(fp) != EOF;
+ }
+ }
+ return 0;
+}
diff --git a/pw/pw_group.c b/pw/pw_group.c
index 3259412..391e477 100644
--- a/pw/pw_group.c
+++ b/pw/pw_group.c
@@ -227,10 +227,12 @@ pw_group(struct userconf * cnf, int mode, struct cargs * args)
else if (arg->ch == 'm') {
int k = 0;
- while (grp->gr_mem[k] != NULL) {
- if (extendarray(&members, &grmembers, i + 2) != -1)
- members[i++] = grp->gr_mem[k];
- k++;
+ if (grp->gr_mem != NULL) {
+ while (grp->gr_mem[k] != NULL) {
+ if (extendarray(&members, &grmembers, i + 2) != -1)
+ members[i++] = grp->gr_mem[k];
+ k++;
+ }
}
}
@@ -311,6 +313,9 @@ delete_members(char ***members, int *grmembers, int *i, struct carg *arg,
int k;
struct passwd *pwd;
+ if (grp->gr_mem == NULL)
+ return;
+
k = 0;
while (grp->gr_mem[k] != NULL) {
matchFound = false;
@@ -415,8 +420,10 @@ print_group(struct group * grp, int pretty)
printf("Group Name: %-15s #%lu\n"
" Members: ",
grp->gr_name, (long) grp->gr_gid);
- for (i = 0; grp->gr_mem[i]; i++)
- printf("%s%s", i ? "," : "", grp->gr_mem[i]);
+ if (grp->gr_mem != NULL) {
+ for (i = 0; grp->gr_mem[i]; i++)
+ printf("%s%s", i ? "," : "", grp->gr_mem[i]);
+ }
fputs("\n\n", stdout);
}
return EXIT_SUCCESS;
diff --git a/pw/pw_user.c b/pw/pw_user.c
index def238c..4b3f550 100644
--- a/pw/pw_user.c
+++ b/pw/pw_user.c
@@ -380,6 +380,8 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
char file[MAXPATHLEN];
char home[MAXPATHLEN];
uid_t uid = pwd->pw_uid;
+ struct group *gr;
+ char grname[LOGNAMESIZE];
if (strcmp(pwd->pw_name, "root") == 0)
errx(EX_DATAERR, "cannot remove user 'root'");
@@ -406,6 +408,11 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
*/
sprintf(file, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
strlcpy(home, pwd->pw_dir, sizeof(home));
+ gr = GETGRGID(pwd->pw_gid);
+ if (gr != NULL)
+ strlcpy(grname, gr->gr_name, LOGNAMESIZE);
+ else
+ grname[0] = '\0';
rc = delpwent(pwd);
if (rc == -1)
@@ -425,19 +432,23 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
}
grp = GETGRNAM(a_name->val);
- if (grp != NULL && *grp->gr_mem == NULL)
+ if (grp != NULL &&
+ (grp->gr_mem == NULL || *grp->gr_mem == NULL) &&
+ strcmp(a_name->val, grname) == 0)
delgrent(GETGRNAM(a_name->val));
SETGRENT();
while ((grp = GETGRENT()) != NULL) {
int i;
char group[MAXLOGNAME];
- for (i = 0; grp->gr_mem[i] != NULL; i++) {
- if (!strcmp(grp->gr_mem[i], a_name->val)) {
- while (grp->gr_mem[i] != NULL) {
- grp->gr_mem[i] = grp->gr_mem[i+1];
- }
- strlcpy(group, grp->gr_name, MAXLOGNAME);
- chggrent(group, grp);
+ if (grp->gr_mem != NULL) {
+ for (i = 0; grp->gr_mem[i] != NULL; i++) {
+ if (!strcmp(grp->gr_mem[i], a_name->val)) {
+ while (grp->gr_mem[i] != NULL) {
+ grp->gr_mem[i] = grp->gr_mem[i+1];
+ }
+ strlcpy(group, grp->gr_name, MAXLOGNAME);
+ chggrent(group, grp);
+ }
}
}
}
@@ -908,7 +919,8 @@ pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer
errx(EX_NOUSER, "group `%s' is not defined", a_gid->val);
}
gid = grp->gr_gid;
- } else if ((grp = GETGRNAM(nam)) != NULL && grp->gr_mem[0] == NULL) {
+ } else if ((grp = GETGRNAM(nam)) != NULL &&
+ (grp->gr_mem == NULL || grp->gr_mem[0] == NULL)) {
gid = grp->gr_gid; /* Already created? Use it anyway... */
} else {
struct cargs grpargs;
@@ -1182,14 +1194,16 @@ print_user(struct passwd * pwd, int pretty, int v7)
while ((grp=GETGRENT()) != NULL)
{
int i = 0;
- while (grp->gr_mem[i] != NULL)
- {
- if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
+ if (grp->gr_mem != NULL) {
+ while (grp->gr_mem[i] != NULL)
{
- printf(j++ == 0 ? " Groups: %s" : ",%s", grp->gr_name);
- break;
+ if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
+ {
+ printf(j++ == 0 ? " Groups: %s" : ",%s", grp->gr_name);
+ break;
+ }
+ ++i;
}
- ++i;
}
}
ENDGRENT();