freebsd-skq/usr.sbin/pw/pw_conf.c
Pedro F. Giffuni 1de7b4b805 various: general adoption of SPDX licensing ID tags.
Mainly focus on files that use BSD 2-Clause license, however the tool I
was using misidentified many licenses so this was mostly a manual - error
prone - task.

The Software Package Data Exchange (SPDX) group provides a specification
to make it easier for automated tools to detect and summarize well known
opensource licenses. We are gradually adopting the specification, noting
that the tags are considered only advisory and do not, in any way,
superceed or replace the license texts.

No functional change intended.
2017-11-27 15:37:16 +00:00

545 lines
13 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* 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 <sys/types.h>
#include <sys/sbuf.h>
#include <err.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.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 */
};
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;
}
return dflt;
}
int
passwd_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;
errx(1, "Invalid value for default password");
}
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;
if ((p = unquote(p)) == NULL)
return (NULL);
if ((q = strdup(p)) == NULL)
err(1, "strdup()");
return (q);
}
struct userconf *
read_userconfig(char const * file)
{
FILE *fp;
char *buf, *p;
const char *errstr;
size_t linecap;
ssize_t linelen;
buf = NULL;
linecap = 0;
if (file == NULL)
file = _PATH_PW_CONF;
if ((fp = fopen(file, "r")) == NULL)
return (&config);
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 = passwd_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:
while ((q = strtok(NULL, toks)) != NULL) {
if (config.groups == NULL)
config.groups = sl_init();
sl_add(config.groups, newstr(q));
}
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) {
config.min_uid = strtounum(q, 0,
UID_MAX, &errstr);
if (errstr)
warnx("Invalid min_uid: '%s';"
" ignoring", q);
}
break;
case _UC_MAXUID:
if ((q = unquote(q)) != NULL) {
config.max_uid = strtounum(q, 0,
UID_MAX, &errstr);
if (errstr)
warnx("Invalid max_uid: '%s';"
" ignoring", q);
}
break;
case _UC_MINGID:
if ((q = unquote(q)) != NULL) {
config.min_gid = strtounum(q, 0,
GID_MAX, &errstr);
if (errstr)
warnx("Invalid min_gid: '%s';"
" ignoring", q);
}
break;
case _UC_MAXGID:
if ((q = unquote(q)) != NULL) {
config.max_gid = strtounum(q, 0,
GID_MAX, &errstr);
if (errstr)
warnx("Invalid max_gid: '%s';"
" ignoring", q);
}
break;
case _UC_EXPIRE:
if ((q = unquote(q)) != NULL) {
config.expire_days = strtonum(q, 0,
INT_MAX, &errstr);
if (errstr)
warnx("Invalid expire days:"
" '%s'; ignoring", q);
}
break;
case _UC_PASSWORD:
if ((q = unquote(q)) != NULL) {
config.password_days = strtonum(q, 0,
INT_MAX, &errstr);
if (errstr)
warnx("Invalid password days:"
" '%s'; ignoring", q);
}
break;
case _UC_FIELDS:
case _UC_NONE:
break;
}
}
}
free(buf);
fclose(fp);
return (&config);
}
int
write_userconfig(struct userconf *cnf, const char *file)
{
int fd;
int i, j;
struct sbuf *buf;
FILE *fp;
if (file == NULL)
file = _PATH_PW_CONF;
if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
return (0);
if ((fp = fdopen(fd, "w")) == NULL) {
close(fd);
return (0);
}
buf = sbuf_new_auto();
for (i = _UC_NONE; i < _UC_FIELDS; i++) {
int quote = 1;
sbuf_clear(buf);
switch (i) {
case _UC_DEFAULTPWD:
sbuf_cat(buf, boolean_str(cnf->default_password));
break;
case _UC_REUSEUID:
sbuf_cat(buf, boolean_str(cnf->reuse_uids));
break;
case _UC_REUSEGID:
sbuf_cat(buf, boolean_str(cnf->reuse_gids));
break;
case _UC_NISPASSWD:
sbuf_cat(buf, cnf->nispasswd ? cnf->nispasswd : "");
quote = 0;
break;
case _UC_DOTDIR:
sbuf_cat(buf, cnf->dotdir ? cnf->dotdir :
boolean_str(0));
break;
case _UC_NEWMAIL:
sbuf_cat(buf, cnf->newmail ? cnf->newmail :
boolean_str(0));
break;
case _UC_LOGFILE:
sbuf_cat(buf, cnf->logfile ? cnf->logfile :
boolean_str(0));
break;
case _UC_HOMEROOT:
sbuf_cat(buf, cnf->home);
break;
case _UC_HOMEMODE:
sbuf_printf(buf, "%04o", cnf->homemode);
quote = 0;
break;
case _UC_SHELLPATH:
sbuf_cat(buf, cnf->shelldir);
break;
case _UC_SHELLS:
for (j = 0; j < _UC_MAXSHELLS &&
system_shells[j] != NULL; j++)
sbuf_printf(buf, "%s\"%s\"", j ?
"," : "", system_shells[j]);
quote = 0;
break;
case _UC_DEFAULTSHELL:
sbuf_cat(buf, cnf->shell_default ?
cnf->shell_default : bourne_shell);
break;
case _UC_DEFAULTGROUP:
sbuf_cat(buf, cnf->default_group ?
cnf->default_group : "");
break;
case _UC_EXTRAGROUPS:
for (j = 0; cnf->groups != NULL &&
j < (int)cnf->groups->sl_cur; j++)
sbuf_printf(buf, "%s\"%s\"", j ?
"," : "", cnf->groups->sl_str[j]);
quote = 0;
break;
case _UC_DEFAULTCLASS:
sbuf_cat(buf, cnf->default_class ?
cnf->default_class : "");
break;
case _UC_MINUID:
sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_uid);
quote = 0;
break;
case _UC_MAXUID:
sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_uid);
quote = 0;
break;
case _UC_MINGID:
sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_gid);
quote = 0;
break;
case _UC_MAXGID:
sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_gid);
quote = 0;
break;
case _UC_EXPIRE:
sbuf_printf(buf, "%jd", (intmax_t)cnf->expire_days);
quote = 0;
break;
case _UC_PASSWORD:
sbuf_printf(buf, "%jd", (intmax_t)cnf->password_days);
quote = 0;
break;
case _UC_NONE:
break;
}
sbuf_finish(buf);
if (comments[i])
fputs(comments[i], fp);
if (*kwds[i]) {
if (quote)
fprintf(fp, "%s = \"%s\"\n", kwds[i],
sbuf_data(buf));
else
fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
#if debugging
printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
#endif
}
}
sbuf_delete(buf);
return (fclose(fp) != EOF);
}