diff options
author | 2014-04-28 07:50:45 +0000 | |
---|---|---|
committer | 2014-04-28 07:50:45 +0000 | |
commit | 2d2d383dd1e01034ed139c7d891c8c835d712e34 (patch) | |
tree | 202333b14f641a3f8a162ab015482fd8d6f3840b | |
parent | 458a332b6487c6e80893dbd053ecd9e0391b76cf (diff) | |
parent | ada4ded218239677fd4d3378d71bc8f378e000f1 (diff) | |
download | pw-darwin-2d2d383dd1e01034ed139c7d891c8c835d712e34.tar.gz pw-darwin-2d2d383dd1e01034ed139c7d891c8c835d712e34.tar.zst pw-darwin-2d2d383dd1e01034ed139c7d891c8c835d712e34.zip |
Merge head
-rw-r--r-- | chpass/chpass.1 | 485 | ||||
-rw-r--r-- | pw/pw.h | 1 | ||||
-rw-r--r-- | pw/pw_conf.c | 503 | ||||
-rw-r--r-- | pw/pw_group.c | 19 | ||||
-rw-r--r-- | pw/pw_user.c | 44 |
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. @@ -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(); |