pw_conf.c revision 20302
120253Sjoerg/*-
220302Sjoerg * Copyright (C) 1996
320302Sjoerg *	David L. Nugent.  All rights reserved.
420253Sjoerg *
520253Sjoerg * Redistribution and use in source and binary forms, with or without
620253Sjoerg * modification, are permitted provided that the following conditions
720253Sjoerg * are met:
820253Sjoerg * 1. Redistributions of source code must retain the above copyright
920302Sjoerg *    notice, this list of conditions and the following disclaimer.
1020253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1120253Sjoerg *    notice, this list of conditions and the following disclaimer in the
1220253Sjoerg *    documentation and/or other materials provided with the distribution.
1320253Sjoerg *
1420302Sjoerg * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
1520253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1620253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1720302Sjoerg * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
1820253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1920253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2020253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2120253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2220253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2320253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2420253Sjoerg * SUCH DAMAGE.
2520253Sjoerg *
2620302Sjoerg *	$Id: pw_conf.c,v 1.1.1.1 1996/12/09 14:05:35 joerg Exp $
2720253Sjoerg */
2820253Sjoerg
2920253Sjoerg#include <string.h>
3020253Sjoerg#include <ctype.h>
3120253Sjoerg#include <fcntl.h>
3220253Sjoerg
3320253Sjoerg#include "pw.h"
3420253Sjoerg
3520253Sjoerg#define debugging 0
3620253Sjoerg
3720253Sjoergenum {
3820253Sjoerg	_UC_NONE,
3920253Sjoerg	_UC_DEFAULTPWD,
4020253Sjoerg	_UC_REUSEUID,
4120253Sjoerg	_UC_REUSEGID,
4220253Sjoerg	_UC_DOTDIR,
4320253Sjoerg	_UC_NEWMAIL,
4420253Sjoerg	_UC_LOGFILE,
4520253Sjoerg	_UC_HOMEROOT,
4620253Sjoerg	_UC_SHELLPATH,
4720253Sjoerg	_UC_SHELLS,
4820253Sjoerg	_UC_DEFAULTSHELL,
4920253Sjoerg	_UC_DEFAULTGROUP,
5020253Sjoerg	_UC_EXTRAGROUPS,
5120253Sjoerg	_UC_DEFAULTCLASS,
5220253Sjoerg	_UC_MINUID,
5320253Sjoerg	_UC_MAXUID,
5420253Sjoerg	_UC_MINGID,
5520253Sjoerg	_UC_MAXGID,
5620253Sjoerg	_UC_EXPIRE,
5720253Sjoerg	_UC_PASSWORD,
5820253Sjoerg	_UC_FIELDS
5920253Sjoerg};
6020253Sjoerg
6120253Sjoergstatic char     bourne_shell[] = "sh";
6220253Sjoerg
6320253Sjoergstatic char    *system_shells[_UC_MAXSHELLS] =
6420253Sjoerg{
6520253Sjoerg	bourne_shell,
6620253Sjoerg	"csh"
6720253Sjoerg};
6820253Sjoerg
6920253Sjoergstatic char    *default_groups[_UC_MAXGROUPS] =
7020253Sjoerg{
7120253Sjoerg	NULL
7220253Sjoerg};
7320253Sjoerg
7420253Sjoergstatic char const *booltrue[] =
7520253Sjoerg{
7620253Sjoerg	"yes", "true", "1", "on", NULL
7720253Sjoerg};
7820253Sjoergstatic char const *boolfalse[] =
7920253Sjoerg{
8020253Sjoerg	"no", "false", "0", "off", NULL
8120253Sjoerg};
8220253Sjoerg
8320253Sjoergstatic struct userconf config =
8420253Sjoerg{
8520253Sjoerg	0,			/* Default password for new users? (nologin) */
8620253Sjoerg	0,			/* Reuse uids? */
8720253Sjoerg	0,			/* Reuse gids? */
8820253Sjoerg	"/usr/share/skel",	/* Where to obtain skeleton files */
8920253Sjoerg	NULL,			/* Mail to send to new accounts */
9020253Sjoerg	"/var/log/userlog",	/* Where to log changes */
9120253Sjoerg	"/home",		/* Where to create home directory */
9220253Sjoerg	"/bin",			/* Where shells are located */
9320253Sjoerg	system_shells,		/* List of shells (first is default) */
9420253Sjoerg	bourne_shell,		/* Default shell */
9520253Sjoerg	NULL,			/* Default group name */
9620253Sjoerg	default_groups,		/* Default (additional) groups */
9720253Sjoerg	NULL,			/* Default login class */
9820253Sjoerg	1000, 32000,		/* Allowed range of uids */
9920253Sjoerg	1000, 32000,		/* Allowed range of gids */
10020253Sjoerg	0,			/* Days until account expires */
10120253Sjoerg	0			/* Days until password expires */
10220253Sjoerg};
10320253Sjoerg
10420253Sjoergstatic char const *comments[_UC_FIELDS] =
10520253Sjoerg{
10620253Sjoerg	"#\n# pw.conf - user/group configuration defaults\n#\n",
10720253Sjoerg	"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
10820253Sjoerg	"\n# Reuse gaps in uid sequence? (yes or no)\n",
10920253Sjoerg	"\n# Reuse gaps in gid sequence? (yes or no)\n",
11020253Sjoerg	"\n# Obtain default dotfiles from this directory\n",
11120253Sjoerg	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
11220253Sjoerg	"\n# Log add/change/remove information in this file\n",
11320253Sjoerg	"\n# Root directory in which $HOME directory is created\n",
11420253Sjoerg	"\n# Colon separated list of directories containing valid shells\n",
11520253Sjoerg	"\n# Space separated list of available shells (without paths)\n",
11620253Sjoerg	"\n# Default shell (without path)\n",
11720253Sjoerg	"\n# Default group (leave blank for new group per user)\n",
11820253Sjoerg	"\n# Extra groups for new users\n",
11920253Sjoerg	"\n# Default login class for new users\n",
12020253Sjoerg	"\n# Range of valid default user ids\n",
12120253Sjoerg	NULL,
12220253Sjoerg	"\n# Range of valid default group ids\n",
12320253Sjoerg	NULL,
12420253Sjoerg	"\n# Days after which account expires (0=disabled)\n",
12520253Sjoerg	"\n# Days after which password expires (0=disabled)\n"
12620253Sjoerg};
12720253Sjoerg
12820253Sjoergstatic char const *kwds[] =
12920253Sjoerg{
13020253Sjoerg	"",
13120253Sjoerg	"defaultpasswd",
13220253Sjoerg	"reuseuids",
13320253Sjoerg	"reusegids",
13420253Sjoerg	"skeleton",
13520253Sjoerg	"newmail",
13620253Sjoerg	"logfile",
13720253Sjoerg	"home",
13820253Sjoerg	"shellpath",
13920253Sjoerg	"shells",
14020253Sjoerg	"defaultshell",
14120253Sjoerg	"defaultgroup",
14220253Sjoerg	"extragroups",
14320253Sjoerg	"defaultclass",
14420253Sjoerg	"minuid",
14520253Sjoerg	"maxuid",
14620253Sjoerg	"mingid",
14720253Sjoerg	"maxgid",
14820253Sjoerg	"expire_days",
14920253Sjoerg	"password_days",
15020253Sjoerg	NULL
15120253Sjoerg};
15220253Sjoerg
15320253Sjoergstatic char    *
15420253Sjoergunquote(char const * str)
15520253Sjoerg{
15620253Sjoerg	if (str && (*str == '"' || *str == '\'')) {
15720253Sjoerg		char           *p = strchr(str + 1, *str);
15820253Sjoerg
15920253Sjoerg		if (p != NULL)
16020253Sjoerg			*p = '\0';
16120253Sjoerg		return (char *) (*++str ? str : NULL);
16220253Sjoerg	}
16320253Sjoerg	return (char *) str;
16420253Sjoerg}
16520253Sjoerg
16620253Sjoergint
16720253Sjoergboolean_val(char const * str, int dflt)
16820253Sjoerg{
16920253Sjoerg	if ((str = unquote(str)) != NULL) {
17020253Sjoerg		int             i;
17120253Sjoerg
17220253Sjoerg		for (i = 0; booltrue[i]; i++)
17320253Sjoerg			if (strcmp(str, booltrue[i]) == 0)
17420253Sjoerg				return 1;
17520253Sjoerg		for (i = 0; boolfalse[i]; i++)
17620253Sjoerg			if (strcmp(str, boolfalse[i]) == 0)
17720253Sjoerg				return 0;
17820253Sjoerg
17920253Sjoerg		/*
18020253Sjoerg		 * Special cases for defaultpassword
18120253Sjoerg		 */
18220253Sjoerg		if (strcmp(str, "random") == 0)
18320253Sjoerg			return -1;
18420253Sjoerg		if (strcmp(str, "none") == 0)
18520253Sjoerg			return -2;
18620253Sjoerg	}
18720253Sjoerg	return dflt;
18820253Sjoerg}
18920253Sjoerg
19020253Sjoergchar const     *
19120253Sjoergboolean_str(int val)
19220253Sjoerg{
19320253Sjoerg	if (val == -1)
19420253Sjoerg		return "random";
19520253Sjoerg	else if (val == -2)
19620253Sjoerg		return "none";
19720253Sjoerg	else
19820253Sjoerg		return val ? booltrue[0] : boolfalse[0];
19920253Sjoerg}
20020253Sjoerg
20120253Sjoergchar           *
20220253Sjoergnewstr(char const * p)
20320253Sjoerg{
20420253Sjoerg	char           *q = NULL;
20520253Sjoerg
20620253Sjoerg	if ((p = unquote(p)) != NULL) {
20720253Sjoerg		int             l = strlen(p) + 1;
20820253Sjoerg
20920253Sjoerg		if ((q = malloc(l)) != NULL)
21020253Sjoerg			memcpy(q, p, l);
21120253Sjoerg	}
21220253Sjoerg	return q;
21320253Sjoerg}
21420253Sjoerg
21520253Sjoerg
21620253Sjoergstruct userconf *
21720253Sjoergread_userconfig(char const * file)
21820253Sjoerg{
21920253Sjoerg	FILE           *fp;
22020253Sjoerg
22120253Sjoerg	if (file == NULL)
22220253Sjoerg		file = _PATH_PW_CONF;
22320253Sjoerg	if ((fp = fopen(file, "r")) != NULL) {
22420253Sjoerg		char            buf[_UC_MAXLINE];
22520253Sjoerg
22620253Sjoerg		while (fgets(buf, sizeof buf, fp) != NULL) {
22720253Sjoerg			char           *p = strchr(buf, '\n');
22820253Sjoerg
22920253Sjoerg			if (p == NULL) {	/* Line too long */
23020253Sjoerg				int             ch;
23120253Sjoerg
23220253Sjoerg				while ((ch = fgetc(fp)) != '\n' && ch != EOF);
23320253Sjoerg			} else {
23420253Sjoerg				*p = '\0';
23520253Sjoerg				if (*buf && *buf != '\n' && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
23620253Sjoerg					static char const toks[] = " \t\r\n,=";
23720253Sjoerg					char           *q = strtok(NULL, toks);
23820253Sjoerg					int             i = 0;
23920253Sjoerg
24020253Sjoerg					while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
24120253Sjoerg						++i;
24220253Sjoerg#if debugging
24320253Sjoerg					if (i == _UC_FIELDS)
24420253Sjoerg						printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
24520253Sjoerg					else
24620253Sjoerg						printf("Got kwd[%s]=%s\n", p, q);
24720253Sjoerg#endif
24820253Sjoerg					switch (i) {
24920253Sjoerg					case _UC_DEFAULTPWD:
25020253Sjoerg						config.default_password = boolean_val(q, 1);
25120253Sjoerg						break;
25220253Sjoerg					case _UC_REUSEUID:
25320253Sjoerg						config.reuse_uids = boolean_val(q, 0);
25420253Sjoerg						break;
25520253Sjoerg					case _UC_REUSEGID:
25620253Sjoerg						config.reuse_gids = boolean_val(q, 0);
25720253Sjoerg						break;
25820253Sjoerg					case _UC_DOTDIR:
25920253Sjoerg						config.dotdir = (q == NULL || !boolean_val(q, 1))
26020253Sjoerg							? NULL : newstr(q);
26120253Sjoerg						break;
26220253Sjoerg					case _UC_NEWMAIL:
26320253Sjoerg						config.newmail = (q == NULL || !boolean_val(q, 1))
26420253Sjoerg							? NULL : newstr(q);
26520253Sjoerg						break;
26620253Sjoerg					case _UC_LOGFILE:
26720253Sjoerg						config.logfile = (q == NULL || !boolean_val(q, 1))
26820253Sjoerg							? NULL : newstr(q);
26920253Sjoerg						break;
27020253Sjoerg					case _UC_HOMEROOT:
27120253Sjoerg						config.home = (q == NULL || !boolean_val(q, 1))
27220253Sjoerg							? "/home" : newstr(q);
27320253Sjoerg						break;
27420253Sjoerg					case _UC_SHELLPATH:
27520253Sjoerg						config.shelldir = (q == NULL || !boolean_val(q, 1))
27620253Sjoerg							? "/bin" : newstr(q);
27720253Sjoerg						break;
27820253Sjoerg					case _UC_SHELLS:
27920253Sjoerg						for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
28020253Sjoerg							system_shells[i] = newstr(q);
28120253Sjoerg						if (i > 0)
28220253Sjoerg							while (i < _UC_MAXSHELLS)
28320253Sjoerg								system_shells[i++] = NULL;
28420253Sjoerg						break;
28520253Sjoerg					case _UC_DEFAULTSHELL:
28620253Sjoerg						config.shell_default = (q == NULL || !boolean_val(q, 1))
28720253Sjoerg							? (char *) bourne_shell : newstr(q);
28820253Sjoerg						break;
28920253Sjoerg					case _UC_DEFAULTGROUP:
29020253Sjoerg						config.default_group = (q == NULL || !boolean_val(q, 1) || getgrnam(q) == NULL)
29120253Sjoerg							? NULL : newstr(q);
29220253Sjoerg						break;
29320253Sjoerg					case _UC_EXTRAGROUPS:
29420253Sjoerg						for (i = 0; i < _UC_MAXGROUPS && q != NULL; i++, q = strtok(NULL, toks))
29520253Sjoerg							default_groups[i] = newstr(q);
29620253Sjoerg						if (i > 0)
29720253Sjoerg							while (i < _UC_MAXGROUPS)
29820253Sjoerg								default_groups[i++] = NULL;
29920253Sjoerg						break;
30020253Sjoerg					case _UC_DEFAULTCLASS:
30120253Sjoerg						config.default_class = (q == NULL || !boolean_val(q, 1))
30220253Sjoerg							? NULL : newstr(q);
30320253Sjoerg						break;
30420253Sjoerg					case _UC_MINUID:
30520253Sjoerg						if ((q = unquote(q)) != NULL && isdigit(*q))
30620253Sjoerg							config.min_uid = (uid_t) atol(q);
30720253Sjoerg						break;
30820253Sjoerg					case _UC_MAXUID:
30920253Sjoerg						if ((q = unquote(q)) != NULL && isdigit(*q))
31020253Sjoerg							config.max_uid = (uid_t) atol(q);
31120253Sjoerg						break;
31220253Sjoerg					case _UC_MINGID:
31320253Sjoerg						if ((q = unquote(q)) != NULL && isdigit(*q))
31420253Sjoerg							config.min_gid = (gid_t) atol(q);
31520253Sjoerg						break;
31620253Sjoerg					case _UC_MAXGID:
31720253Sjoerg						if ((q = unquote(q)) != NULL && isdigit(*q))
31820253Sjoerg							config.max_gid = (gid_t) atol(q);
31920253Sjoerg						break;
32020253Sjoerg					case _UC_EXPIRE:
32120253Sjoerg						if ((q = unquote(q)) != NULL && isdigit(*q))
32220253Sjoerg							config.expire_days = atoi(q);
32320253Sjoerg						break;
32420253Sjoerg					case _UC_PASSWORD:
32520253Sjoerg						if ((q = unquote(q)) != NULL && isdigit(*q))
32620253Sjoerg							config.password_days = atoi(q);
32720253Sjoerg						break;
32820253Sjoerg					case _UC_FIELDS:
32920253Sjoerg					case _UC_NONE:
33020253Sjoerg						break;
33120253Sjoerg					}
33220253Sjoerg				}
33320253Sjoerg			}
33420253Sjoerg		}
33520253Sjoerg		fclose(fp);
33620253Sjoerg	}
33720253Sjoerg	return &config;
33820253Sjoerg}
33920253Sjoerg
34020253Sjoerg
34120253Sjoergint
34220253Sjoergwrite_userconfig(char const * file)
34320253Sjoerg{
34420253Sjoerg	int             fd;
34520253Sjoerg
34620253Sjoerg	if (file == NULL)
34720253Sjoerg		file = _PATH_PW_CONF;
34820253Sjoerg
34920253Sjoerg	if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
35020253Sjoerg		FILE           *fp;
35120253Sjoerg
35220253Sjoerg		if ((fp = fdopen(fd, "w")) == NULL)
35320253Sjoerg			close(fd);
35420253Sjoerg		else {
35520253Sjoerg			int             i, j, k;
35620253Sjoerg			char            buf[_UC_MAXLINE];
35720253Sjoerg
35820253Sjoerg			for (i = _UC_NONE; i < _UC_FIELDS; i++) {
35920253Sjoerg				int             quote = 1;
36020253Sjoerg				char const     *val = buf;
36120253Sjoerg
36220253Sjoerg				*buf = '\0';
36320253Sjoerg				switch (i) {
36420253Sjoerg				case _UC_DEFAULTPWD:
36520253Sjoerg					val = boolean_str(config.default_password);
36620253Sjoerg					break;
36720253Sjoerg				case _UC_REUSEUID:
36820253Sjoerg					val = boolean_str(config.reuse_uids);
36920253Sjoerg					break;
37020253Sjoerg				case _UC_REUSEGID:
37120253Sjoerg					val = boolean_str(config.reuse_gids);
37220253Sjoerg					break;
37320253Sjoerg				case _UC_DOTDIR:
37420253Sjoerg					val = config.dotdir ? config.dotdir : boolean_str(0);
37520253Sjoerg					break;
37620253Sjoerg				case _UC_NEWMAIL:
37720253Sjoerg					val = config.newmail ? config.newmail : boolean_str(0);
37820253Sjoerg					break;
37920253Sjoerg				case _UC_LOGFILE:
38020253Sjoerg					val = config.logfile ? config.logfile : boolean_str(0);
38120253Sjoerg					break;
38220253Sjoerg				case _UC_HOMEROOT:
38320253Sjoerg					val = config.home;
38420253Sjoerg					break;
38520253Sjoerg				case _UC_SHELLPATH:
38620253Sjoerg					val = config.shelldir;
38720253Sjoerg					break;
38820253Sjoerg				case _UC_SHELLS:
38920253Sjoerg					for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++)
39020253Sjoerg						k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", system_shells[j]);
39120253Sjoerg					quote = 0;
39220253Sjoerg					break;
39320253Sjoerg				case _UC_DEFAULTSHELL:
39420253Sjoerg					val = config.shell_default ? config.shell_default : bourne_shell;
39520253Sjoerg					break;
39620253Sjoerg				case _UC_DEFAULTGROUP:
39720253Sjoerg					val = config.default_group ? config.default_group : "";
39820253Sjoerg					break;
39920253Sjoerg				case _UC_EXTRAGROUPS:
40020253Sjoerg					for (j = k = 0; j < _UC_MAXGROUPS && default_groups[j] != NULL; j++)
40120253Sjoerg						k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", default_groups[j]);
40220253Sjoerg					quote = 0;
40320253Sjoerg					break;
40420253Sjoerg				case _UC_DEFAULTCLASS:
40520253Sjoerg					val = config.default_class ? config.default_class : "";
40620253Sjoerg					break;
40720253Sjoerg				case _UC_MINUID:
40820253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.min_uid);
40920253Sjoerg					quote = 0;
41020253Sjoerg					break;
41120253Sjoerg				case _UC_MAXUID:
41220253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.max_uid);
41320253Sjoerg					quote = 0;
41420253Sjoerg					break;
41520253Sjoerg				case _UC_MINGID:
41620253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.min_gid);
41720253Sjoerg					quote = 0;
41820253Sjoerg					break;
41920253Sjoerg				case _UC_MAXGID:
42020253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.max_gid);
42120253Sjoerg					quote = 0;
42220253Sjoerg					break;
42320253Sjoerg				case _UC_EXPIRE:
42420253Sjoerg					sprintf(buf, "%d", config.expire_days);
42520253Sjoerg					quote = 0;
42620253Sjoerg					break;
42720253Sjoerg				case _UC_PASSWORD:
42820253Sjoerg					sprintf(buf, "%d", config.password_days);
42920253Sjoerg					quote = 0;
43020253Sjoerg					break;
43120253Sjoerg				case _UC_NONE:
43220253Sjoerg					break;
43320253Sjoerg				}
43420253Sjoerg
43520253Sjoerg				if (comments[i])
43620253Sjoerg					fputs(comments[i], fp);
43720253Sjoerg
43820253Sjoerg				if (*kwds[i]) {
43920253Sjoerg					if (quote)
44020253Sjoerg						fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
44120253Sjoerg					else
44220253Sjoerg						fprintf(fp, "%s = %s\n", kwds[i], val);
44320253Sjoerg#if debugging
44420253Sjoerg					printf("WROTE: %s = %s\n", kwds[i], val);
44520253Sjoerg#endif
44620253Sjoerg				}
44720253Sjoerg			}
44820253Sjoerg			return fclose(fp) != EOF;
44920253Sjoerg		}
45020253Sjoerg	}
45120253Sjoerg	return 0;
45220253Sjoerg}
453