pw_conf.c revision 30259
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 */
2620253Sjoerg
2730259Scharnier#ifndef lint
2830259Scharnierstatic const char rcsid[] =
2930259Scharnier	"$Id$";
3030259Scharnier#endif /* not lint */
3130259Scharnier
3220253Sjoerg#include <string.h>
3320253Sjoerg#include <ctype.h>
3420253Sjoerg#include <fcntl.h>
3520253Sjoerg
3620253Sjoerg#include "pw.h"
3720747Sdavidn#include "pwupd.h"
3820253Sjoerg
3920253Sjoerg#define debugging 0
4020253Sjoerg
4120253Sjoergenum {
4220253Sjoerg	_UC_NONE,
4320253Sjoerg	_UC_DEFAULTPWD,
4420253Sjoerg	_UC_REUSEUID,
4520253Sjoerg	_UC_REUSEGID,
4621330Sdavidn	_UC_NISPASSWD,
4720253Sjoerg	_UC_DOTDIR,
4820253Sjoerg	_UC_NEWMAIL,
4920253Sjoerg	_UC_LOGFILE,
5020253Sjoerg	_UC_HOMEROOT,
5120253Sjoerg	_UC_SHELLPATH,
5220253Sjoerg	_UC_SHELLS,
5320253Sjoerg	_UC_DEFAULTSHELL,
5420253Sjoerg	_UC_DEFAULTGROUP,
5520253Sjoerg	_UC_EXTRAGROUPS,
5620253Sjoerg	_UC_DEFAULTCLASS,
5720253Sjoerg	_UC_MINUID,
5820253Sjoerg	_UC_MAXUID,
5920253Sjoerg	_UC_MINGID,
6020253Sjoerg	_UC_MAXGID,
6120253Sjoerg	_UC_EXPIRE,
6220253Sjoerg	_UC_PASSWORD,
6320253Sjoerg	_UC_FIELDS
6420253Sjoerg};
6520253Sjoerg
6620253Sjoergstatic char     bourne_shell[] = "sh";
6720253Sjoerg
6820253Sjoergstatic char    *system_shells[_UC_MAXSHELLS] =
6920253Sjoerg{
7020253Sjoerg	bourne_shell,
7120253Sjoerg	"csh"
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? */
8821330Sdavidn	NULL,			/* NIS version of the passwd file */
8920253Sjoerg	"/usr/share/skel",	/* Where to obtain skeleton files */
9020253Sjoerg	NULL,			/* Mail to send to new accounts */
9120253Sjoerg	"/var/log/userlog",	/* Where to log changes */
9220253Sjoerg	"/home",		/* Where to create home directory */
9320253Sjoerg	"/bin",			/* Where shells are located */
9420253Sjoerg	system_shells,		/* List of shells (first is default) */
9520253Sjoerg	bourne_shell,		/* Default shell */
9620253Sjoerg	NULL,			/* Default group name */
9720747Sdavidn	NULL,			/* Default (additional) groups */
9820253Sjoerg	NULL,			/* Default login class */
9920253Sjoerg	1000, 32000,		/* Allowed range of uids */
10020253Sjoerg	1000, 32000,		/* Allowed range of gids */
10120253Sjoerg	0,			/* Days until account expires */
10220253Sjoerg	0			/* Days until password expires */
10320253Sjoerg};
10420253Sjoerg
10520253Sjoergstatic char const *comments[_UC_FIELDS] =
10620253Sjoerg{
10720253Sjoerg	"#\n# pw.conf - user/group configuration defaults\n#\n",
10820253Sjoerg	"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
10920253Sjoerg	"\n# Reuse gaps in uid sequence? (yes or no)\n",
11020253Sjoerg	"\n# Reuse gaps in gid sequence? (yes or no)\n",
11121330Sdavidn	"\n# Path to the NIS passwd file (blank or 'no' for none)\n",
11220253Sjoerg	"\n# Obtain default dotfiles from this directory\n",
11320253Sjoerg	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
11420253Sjoerg	"\n# Log add/change/remove information in this file\n",
11520253Sjoerg	"\n# Root directory in which $HOME directory is created\n",
11620253Sjoerg	"\n# Colon separated list of directories containing valid shells\n",
11720253Sjoerg	"\n# Space separated list of available shells (without paths)\n",
11820253Sjoerg	"\n# Default shell (without path)\n",
11920253Sjoerg	"\n# Default group (leave blank for new group per user)\n",
12020253Sjoerg	"\n# Extra groups for new users\n",
12120253Sjoerg	"\n# Default login class for new users\n",
12220253Sjoerg	"\n# Range of valid default user ids\n",
12320253Sjoerg	NULL,
12420253Sjoerg	"\n# Range of valid default group ids\n",
12520253Sjoerg	NULL,
12620253Sjoerg	"\n# Days after which account expires (0=disabled)\n",
12720253Sjoerg	"\n# Days after which password expires (0=disabled)\n"
12820253Sjoerg};
12920253Sjoerg
13020253Sjoergstatic char const *kwds[] =
13120253Sjoerg{
13220253Sjoerg	"",
13320253Sjoerg	"defaultpasswd",
13420253Sjoerg	"reuseuids",
13520253Sjoerg	"reusegids",
13621330Sdavidn	"nispasswd",
13720253Sjoerg	"skeleton",
13820253Sjoerg	"newmail",
13920253Sjoerg	"logfile",
14020253Sjoerg	"home",
14120253Sjoerg	"shellpath",
14220253Sjoerg	"shells",
14320253Sjoerg	"defaultshell",
14420253Sjoerg	"defaultgroup",
14520253Sjoerg	"extragroups",
14620253Sjoerg	"defaultclass",
14720253Sjoerg	"minuid",
14820253Sjoerg	"maxuid",
14920253Sjoerg	"mingid",
15020253Sjoerg	"maxgid",
15120253Sjoerg	"expire_days",
15220253Sjoerg	"password_days",
15320253Sjoerg	NULL
15420253Sjoerg};
15520253Sjoerg
15620253Sjoergstatic char    *
15720253Sjoergunquote(char const * str)
15820253Sjoerg{
15920253Sjoerg	if (str && (*str == '"' || *str == '\'')) {
16020253Sjoerg		char           *p = strchr(str + 1, *str);
16120253Sjoerg
16220253Sjoerg		if (p != NULL)
16320253Sjoerg			*p = '\0';
16420253Sjoerg		return (char *) (*++str ? str : NULL);
16520253Sjoerg	}
16620253Sjoerg	return (char *) str;
16720253Sjoerg}
16820253Sjoerg
16920253Sjoergint
17020253Sjoergboolean_val(char const * str, int dflt)
17120253Sjoerg{
17220253Sjoerg	if ((str = unquote(str)) != NULL) {
17320253Sjoerg		int             i;
17420253Sjoerg
17520253Sjoerg		for (i = 0; booltrue[i]; i++)
17620253Sjoerg			if (strcmp(str, booltrue[i]) == 0)
17720253Sjoerg				return 1;
17820253Sjoerg		for (i = 0; boolfalse[i]; i++)
17920253Sjoerg			if (strcmp(str, boolfalse[i]) == 0)
18020253Sjoerg				return 0;
18120253Sjoerg
18220253Sjoerg		/*
18320253Sjoerg		 * Special cases for defaultpassword
18420253Sjoerg		 */
18520253Sjoerg		if (strcmp(str, "random") == 0)
18620253Sjoerg			return -1;
18720253Sjoerg		if (strcmp(str, "none") == 0)
18820253Sjoerg			return -2;
18920253Sjoerg	}
19020253Sjoerg	return dflt;
19120253Sjoerg}
19220253Sjoerg
19320253Sjoergchar const     *
19420253Sjoergboolean_str(int val)
19520253Sjoerg{
19620253Sjoerg	if (val == -1)
19720253Sjoerg		return "random";
19820253Sjoerg	else if (val == -2)
19920253Sjoerg		return "none";
20020253Sjoerg	else
20120253Sjoerg		return val ? booltrue[0] : boolfalse[0];
20220253Sjoerg}
20320253Sjoerg
20420253Sjoergchar           *
20520253Sjoergnewstr(char const * p)
20620253Sjoerg{
20720253Sjoerg	char           *q = NULL;
20820253Sjoerg
20920253Sjoerg	if ((p = unquote(p)) != NULL) {
21020253Sjoerg		int             l = strlen(p) + 1;
21120253Sjoerg
21220253Sjoerg		if ((q = malloc(l)) != NULL)
21320253Sjoerg			memcpy(q, p, l);
21420253Sjoerg	}
21520253Sjoerg	return q;
21620253Sjoerg}
21720253Sjoerg
21820747Sdavidn#define LNBUFSZ 1024
21920253Sjoerg
22020747Sdavidn
22120253Sjoergstruct userconf *
22220253Sjoergread_userconfig(char const * file)
22320253Sjoerg{
22420253Sjoerg	FILE           *fp;
22520253Sjoerg
22620747Sdavidn	extendarray(&config.groups, &config.numgroups, 200);
22720747Sdavidn	memset(config.groups, 0, config.numgroups * sizeof(char *));
22820253Sjoerg	if (file == NULL)
22920253Sjoerg		file = _PATH_PW_CONF;
23020253Sjoerg	if ((fp = fopen(file, "r")) != NULL) {
23120747Sdavidn		int	    buflen = LNBUFSZ;
23220747Sdavidn		char       *buf = malloc(buflen);
23320253Sjoerg
23420747Sdavidn	nextline:
23520747Sdavidn		while (fgets(buf, buflen, fp) != NULL) {
23620747Sdavidn			char           *p;
23720253Sjoerg
23820747Sdavidn			while ((p = strchr(buf, '\n')) == NULL) {
23920747Sdavidn				int	  l;
24020747Sdavidn				if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
24120747Sdavidn					int	ch;
24220747Sdavidn					while ((ch = fgetc(fp)) != '\n' && ch != EOF);
24320747Sdavidn					goto nextline;	/* Ignore it */
24420747Sdavidn				}
24520747Sdavidn				l = strlen(buf);
24620747Sdavidn				if (fgets(buf + l, buflen - l, fp) == NULL)
24720747Sdavidn					break;	/* Unterminated last line */
24820747Sdavidn			}
24920253Sjoerg
25020747Sdavidn			if (p != NULL)
25120253Sjoerg				*p = '\0';
25220253Sjoerg
25320747Sdavidn			if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
25420747Sdavidn				static char const toks[] = " \t\r\n,=";
25520747Sdavidn				char           *q = strtok(NULL, toks);
25620747Sdavidn				int             i = 0;
25720747Sdavidn
25820747Sdavidn				while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
25920747Sdavidn					++i;
26020253Sjoerg#if debugging
26120747Sdavidn				if (i == _UC_FIELDS)
26220747Sdavidn					printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
26320747Sdavidn				else
26420747Sdavidn					printf("Got kwd[%s]=%s\n", p, q);
26520253Sjoerg#endif
26620747Sdavidn				switch (i) {
26720747Sdavidn				case _UC_DEFAULTPWD:
26820747Sdavidn					config.default_password = boolean_val(q, 1);
26920747Sdavidn					break;
27020747Sdavidn				case _UC_REUSEUID:
27120747Sdavidn					config.reuse_uids = boolean_val(q, 0);
27220747Sdavidn					break;
27320747Sdavidn				case _UC_REUSEGID:
27420747Sdavidn					config.reuse_gids = boolean_val(q, 0);
27520747Sdavidn					break;
27621330Sdavidn				case _UC_NISPASSWD:
27721330Sdavidn					config.nispasswd = (q == NULL || !boolean_val(q, 1))
27821330Sdavidn						? NULL : newstr(q);
27921330Sdavidn					break;
28020747Sdavidn				case _UC_DOTDIR:
28120747Sdavidn					config.dotdir = (q == NULL || !boolean_val(q, 1))
28220747Sdavidn						? NULL : newstr(q);
28320747Sdavidn					break;
28420747Sdavidn				case _UC_NEWMAIL:
28520747Sdavidn					config.newmail = (q == NULL || !boolean_val(q, 1))
28620747Sdavidn						? NULL : newstr(q);
28720747Sdavidn					break;
28820747Sdavidn				case _UC_LOGFILE:
28920747Sdavidn					config.logfile = (q == NULL || !boolean_val(q, 1))
29020747Sdavidn						? NULL : newstr(q);
29120747Sdavidn					break;
29220747Sdavidn				case _UC_HOMEROOT:
29320747Sdavidn					config.home = (q == NULL || !boolean_val(q, 1))
29420747Sdavidn						? "/home" : newstr(q);
29520747Sdavidn					break;
29620747Sdavidn				case _UC_SHELLPATH:
29720747Sdavidn					config.shelldir = (q == NULL || !boolean_val(q, 1))
29820747Sdavidn						? "/bin" : newstr(q);
29920747Sdavidn					break;
30020747Sdavidn				case _UC_SHELLS:
30120747Sdavidn					for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
30220747Sdavidn						system_shells[i] = newstr(q);
30320747Sdavidn					if (i > 0)
30420747Sdavidn						while (i < _UC_MAXSHELLS)
30520747Sdavidn							system_shells[i++] = NULL;
30620747Sdavidn					break;
30720747Sdavidn				case _UC_DEFAULTSHELL:
30820747Sdavidn					config.shell_default = (q == NULL || !boolean_val(q, 1))
30920747Sdavidn						? (char *) bourne_shell : newstr(q);
31020747Sdavidn					break;
31120747Sdavidn				case _UC_DEFAULTGROUP:
31229002Sdavidn					q = unquote(q);
31320747Sdavidn					config.default_group = (q == NULL || !boolean_val(q, 1) || getgrnam(q) == NULL)
31420747Sdavidn						? NULL : newstr(q);
31520747Sdavidn					break;
31620747Sdavidn				case _UC_EXTRAGROUPS:
31720747Sdavidn					for (i = 0; q != NULL; q = strtok(NULL, toks)) {
31820747Sdavidn						if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
31920747Sdavidn							config.groups[i++] = newstr(q);
32020253Sjoerg					}
32120747Sdavidn					if (i > 0)
32220747Sdavidn						while (i < config.numgroups)
32320747Sdavidn							config.groups[i++] = NULL;
32420747Sdavidn					break;
32520747Sdavidn				case _UC_DEFAULTCLASS:
32620747Sdavidn					config.default_class = (q == NULL || !boolean_val(q, 1))
32720747Sdavidn						? NULL : newstr(q);
32820747Sdavidn					break;
32920747Sdavidn				case _UC_MINUID:
33020747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
33120747Sdavidn						config.min_uid = (uid_t) atol(q);
33220747Sdavidn					break;
33320747Sdavidn				case _UC_MAXUID:
33420747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
33520747Sdavidn						config.max_uid = (uid_t) atol(q);
33620747Sdavidn					break;
33720747Sdavidn				case _UC_MINGID:
33820747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
33920747Sdavidn						config.min_gid = (gid_t) atol(q);
34020747Sdavidn					break;
34120747Sdavidn				case _UC_MAXGID:
34220747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
34320747Sdavidn						config.max_gid = (gid_t) atol(q);
34420747Sdavidn					break;
34520747Sdavidn				case _UC_EXPIRE:
34620747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
34720747Sdavidn						config.expire_days = atoi(q);
34820747Sdavidn					break;
34920747Sdavidn				case _UC_PASSWORD:
35020747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
35120747Sdavidn						config.password_days = atoi(q);
35220747Sdavidn					break;
35320747Sdavidn				case _UC_FIELDS:
35420747Sdavidn				case _UC_NONE:
35520747Sdavidn					break;
35620253Sjoerg				}
35720253Sjoerg			}
35820253Sjoerg		}
35920747Sdavidn		free(buf);
36020253Sjoerg		fclose(fp);
36120253Sjoerg	}
36220253Sjoerg	return &config;
36320253Sjoerg}
36420253Sjoerg
36520253Sjoerg
36620253Sjoergint
36720253Sjoergwrite_userconfig(char const * file)
36820253Sjoerg{
36920253Sjoerg	int             fd;
37020253Sjoerg
37120253Sjoerg	if (file == NULL)
37220253Sjoerg		file = _PATH_PW_CONF;
37320253Sjoerg
37420253Sjoerg	if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
37520253Sjoerg		FILE           *fp;
37620253Sjoerg
37720253Sjoerg		if ((fp = fdopen(fd, "w")) == NULL)
37820253Sjoerg			close(fd);
37920253Sjoerg		else {
38020253Sjoerg			int             i, j, k;
38120747Sdavidn			int		len = LNBUFSZ;
38220747Sdavidn			char           *buf = malloc(len);
38320253Sjoerg
38420253Sjoerg			for (i = _UC_NONE; i < _UC_FIELDS; i++) {
38520253Sjoerg				int             quote = 1;
38620253Sjoerg				char const     *val = buf;
38720253Sjoerg
38820253Sjoerg				*buf = '\0';
38920253Sjoerg				switch (i) {
39020253Sjoerg				case _UC_DEFAULTPWD:
39120253Sjoerg					val = boolean_str(config.default_password);
39220253Sjoerg					break;
39320253Sjoerg				case _UC_REUSEUID:
39420253Sjoerg					val = boolean_str(config.reuse_uids);
39520253Sjoerg					break;
39620253Sjoerg				case _UC_REUSEGID:
39720253Sjoerg					val = boolean_str(config.reuse_gids);
39820253Sjoerg					break;
39921330Sdavidn				case _UC_NISPASSWD:
40021330Sdavidn					val = config.nispasswd ? config.nispasswd : "";
40121330Sdavidn					quote = 0;
40221330Sdavidn					break;
40320253Sjoerg				case _UC_DOTDIR:
40420253Sjoerg					val = config.dotdir ? config.dotdir : boolean_str(0);
40520253Sjoerg					break;
40620253Sjoerg				case _UC_NEWMAIL:
40720253Sjoerg					val = config.newmail ? config.newmail : boolean_str(0);
40820253Sjoerg					break;
40920253Sjoerg				case _UC_LOGFILE:
41020253Sjoerg					val = config.logfile ? config.logfile : boolean_str(0);
41120253Sjoerg					break;
41220253Sjoerg				case _UC_HOMEROOT:
41320253Sjoerg					val = config.home;
41420253Sjoerg					break;
41520253Sjoerg				case _UC_SHELLPATH:
41620253Sjoerg					val = config.shelldir;
41720253Sjoerg					break;
41820253Sjoerg				case _UC_SHELLS:
41920747Sdavidn					for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
42020747Sdavidn						char	lbuf[64];
42120747Sdavidn						int	l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
42220747Sdavidn						if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
42320747Sdavidn							strcpy(buf + k, lbuf);
42420747Sdavidn							k += l;
42520747Sdavidn						}
42620747Sdavidn					}
42720253Sjoerg					quote = 0;
42820253Sjoerg					break;
42920253Sjoerg				case _UC_DEFAULTSHELL:
43020253Sjoerg					val = config.shell_default ? config.shell_default : bourne_shell;
43120253Sjoerg					break;
43220253Sjoerg				case _UC_DEFAULTGROUP:
43320253Sjoerg					val = config.default_group ? config.default_group : "";
43420253Sjoerg					break;
43520253Sjoerg				case _UC_EXTRAGROUPS:
43620747Sdavidn					extendarray(&config.groups, &config.numgroups, 200);
43720747Sdavidn					for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
43820747Sdavidn						char	lbuf[64];
43920747Sdavidn						int	l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
44020747Sdavidn						if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
44120747Sdavidn							strcpy(buf + k, lbuf);
44220747Sdavidn							k +=  l;
44320747Sdavidn						}
44420747Sdavidn					}
44520253Sjoerg					quote = 0;
44620253Sjoerg					break;
44720253Sjoerg				case _UC_DEFAULTCLASS:
44820253Sjoerg					val = config.default_class ? config.default_class : "";
44920253Sjoerg					break;
45020253Sjoerg				case _UC_MINUID:
45120253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.min_uid);
45220253Sjoerg					quote = 0;
45320253Sjoerg					break;
45420253Sjoerg				case _UC_MAXUID:
45520253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.max_uid);
45620253Sjoerg					quote = 0;
45720253Sjoerg					break;
45820253Sjoerg				case _UC_MINGID:
45920253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.min_gid);
46020253Sjoerg					quote = 0;
46120253Sjoerg					break;
46220253Sjoerg				case _UC_MAXGID:
46320253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.max_gid);
46420253Sjoerg					quote = 0;
46520253Sjoerg					break;
46620253Sjoerg				case _UC_EXPIRE:
46720253Sjoerg					sprintf(buf, "%d", config.expire_days);
46820253Sjoerg					quote = 0;
46920253Sjoerg					break;
47020253Sjoerg				case _UC_PASSWORD:
47120253Sjoerg					sprintf(buf, "%d", config.password_days);
47220253Sjoerg					quote = 0;
47320253Sjoerg					break;
47420253Sjoerg				case _UC_NONE:
47520253Sjoerg					break;
47620253Sjoerg				}
47720253Sjoerg
47820253Sjoerg				if (comments[i])
47920253Sjoerg					fputs(comments[i], fp);
48020253Sjoerg
48120253Sjoerg				if (*kwds[i]) {
48220253Sjoerg					if (quote)
48320253Sjoerg						fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
48420253Sjoerg					else
48520253Sjoerg						fprintf(fp, "%s = %s\n", kwds[i], val);
48620253Sjoerg#if debugging
48720253Sjoerg					printf("WROTE: %s = %s\n", kwds[i], val);
48820253Sjoerg#endif
48920253Sjoerg				}
49020253Sjoerg			}
49120747Sdavidn			free(buf);
49220253Sjoerg			return fclose(fp) != EOF;
49320253Sjoerg		}
49420253Sjoerg	}
49520253Sjoerg	return 0;
49620253Sjoerg}
497