pw_conf.c revision 44229
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[] =
2944229Sdavidn	"$Id: pw_conf.c,v 1.7 1997/10/10 06:23:36 charnier Exp $";
3030259Scharnier#endif /* not lint */
3130259Scharnier
3220253Sjoerg#include <string.h>
3320253Sjoerg#include <ctype.h>
3420253Sjoerg#include <fcntl.h>
3520253Sjoerg
3620253Sjoerg#include "pw.h"
3720253Sjoerg
3820253Sjoerg#define debugging 0
3920253Sjoerg
4020253Sjoergenum {
4120253Sjoerg	_UC_NONE,
4220253Sjoerg	_UC_DEFAULTPWD,
4320253Sjoerg	_UC_REUSEUID,
4420253Sjoerg	_UC_REUSEGID,
4521330Sdavidn	_UC_NISPASSWD,
4620253Sjoerg	_UC_DOTDIR,
4720253Sjoerg	_UC_NEWMAIL,
4820253Sjoerg	_UC_LOGFILE,
4920253Sjoerg	_UC_HOMEROOT,
5020253Sjoerg	_UC_SHELLPATH,
5120253Sjoerg	_UC_SHELLS,
5220253Sjoerg	_UC_DEFAULTSHELL,
5320253Sjoerg	_UC_DEFAULTGROUP,
5420253Sjoerg	_UC_EXTRAGROUPS,
5520253Sjoerg	_UC_DEFAULTCLASS,
5620253Sjoerg	_UC_MINUID,
5720253Sjoerg	_UC_MAXUID,
5820253Sjoerg	_UC_MINGID,
5920253Sjoerg	_UC_MAXGID,
6020253Sjoerg	_UC_EXPIRE,
6120253Sjoerg	_UC_PASSWORD,
6220253Sjoerg	_UC_FIELDS
6320253Sjoerg};
6420253Sjoerg
6520253Sjoergstatic char     bourne_shell[] = "sh";
6620253Sjoerg
6720253Sjoergstatic char    *system_shells[_UC_MAXSHELLS] =
6820253Sjoerg{
6920253Sjoerg	bourne_shell,
7020253Sjoerg	"csh"
7120253Sjoerg};
7220253Sjoerg
7320253Sjoergstatic char const *booltrue[] =
7420253Sjoerg{
7520253Sjoerg	"yes", "true", "1", "on", NULL
7620253Sjoerg};
7720253Sjoergstatic char const *boolfalse[] =
7820253Sjoerg{
7920253Sjoerg	"no", "false", "0", "off", NULL
8020253Sjoerg};
8120253Sjoerg
8220253Sjoergstatic struct userconf config =
8320253Sjoerg{
8420253Sjoerg	0,			/* Default password for new users? (nologin) */
8520253Sjoerg	0,			/* Reuse uids? */
8620253Sjoerg	0,			/* Reuse gids? */
8721330Sdavidn	NULL,			/* NIS version of the passwd file */
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 */
9620747Sdavidn	NULL,			/* 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",
11021330Sdavidn	"\n# Path to the NIS passwd file (blank or 'no' for none)\n",
11120253Sjoerg	"\n# Obtain default dotfiles from this directory\n",
11220253Sjoerg	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
11320253Sjoerg	"\n# Log add/change/remove information in this file\n",
11420253Sjoerg	"\n# Root directory in which $HOME directory is created\n",
11520253Sjoerg	"\n# Colon separated list of directories containing valid shells\n",
11620253Sjoerg	"\n# Space separated list of available shells (without paths)\n",
11720253Sjoerg	"\n# Default shell (without path)\n",
11820253Sjoerg	"\n# Default group (leave blank for new group per user)\n",
11920253Sjoerg	"\n# Extra groups for new users\n",
12020253Sjoerg	"\n# Default login class for new users\n",
12120253Sjoerg	"\n# Range of valid default user ids\n",
12220253Sjoerg	NULL,
12320253Sjoerg	"\n# Range of valid default group ids\n",
12420253Sjoerg	NULL,
12520253Sjoerg	"\n# Days after which account expires (0=disabled)\n",
12620253Sjoerg	"\n# Days after which password expires (0=disabled)\n"
12720253Sjoerg};
12820253Sjoerg
12920253Sjoergstatic char const *kwds[] =
13020253Sjoerg{
13120253Sjoerg	"",
13220253Sjoerg	"defaultpasswd",
13320253Sjoerg	"reuseuids",
13420253Sjoerg	"reusegids",
13521330Sdavidn	"nispasswd",
13620253Sjoerg	"skeleton",
13720253Sjoerg	"newmail",
13820253Sjoerg	"logfile",
13920253Sjoerg	"home",
14020253Sjoerg	"shellpath",
14120253Sjoerg	"shells",
14220253Sjoerg	"defaultshell",
14320253Sjoerg	"defaultgroup",
14420253Sjoerg	"extragroups",
14520253Sjoerg	"defaultclass",
14620253Sjoerg	"minuid",
14720253Sjoerg	"maxuid",
14820253Sjoerg	"mingid",
14920253Sjoerg	"maxgid",
15020253Sjoerg	"expire_days",
15120253Sjoerg	"password_days",
15220253Sjoerg	NULL
15320253Sjoerg};
15420253Sjoerg
15520253Sjoergstatic char    *
15620253Sjoergunquote(char const * str)
15720253Sjoerg{
15820253Sjoerg	if (str && (*str == '"' || *str == '\'')) {
15920253Sjoerg		char           *p = strchr(str + 1, *str);
16020253Sjoerg
16120253Sjoerg		if (p != NULL)
16220253Sjoerg			*p = '\0';
16320253Sjoerg		return (char *) (*++str ? str : NULL);
16420253Sjoerg	}
16520253Sjoerg	return (char *) str;
16620253Sjoerg}
16720253Sjoerg
16820253Sjoergint
16920253Sjoergboolean_val(char const * str, int dflt)
17020253Sjoerg{
17120253Sjoerg	if ((str = unquote(str)) != NULL) {
17220253Sjoerg		int             i;
17320253Sjoerg
17420253Sjoerg		for (i = 0; booltrue[i]; i++)
17520253Sjoerg			if (strcmp(str, booltrue[i]) == 0)
17620253Sjoerg				return 1;
17720253Sjoerg		for (i = 0; boolfalse[i]; i++)
17820253Sjoerg			if (strcmp(str, boolfalse[i]) == 0)
17920253Sjoerg				return 0;
18020253Sjoerg
18120253Sjoerg		/*
18220253Sjoerg		 * Special cases for defaultpassword
18320253Sjoerg		 */
18420253Sjoerg		if (strcmp(str, "random") == 0)
18520253Sjoerg			return -1;
18620253Sjoerg		if (strcmp(str, "none") == 0)
18720253Sjoerg			return -2;
18820253Sjoerg	}
18920253Sjoerg	return dflt;
19020253Sjoerg}
19120253Sjoerg
19220253Sjoergchar const     *
19320253Sjoergboolean_str(int val)
19420253Sjoerg{
19520253Sjoerg	if (val == -1)
19620253Sjoerg		return "random";
19720253Sjoerg	else if (val == -2)
19820253Sjoerg		return "none";
19920253Sjoerg	else
20020253Sjoerg		return val ? booltrue[0] : boolfalse[0];
20120253Sjoerg}
20220253Sjoerg
20320253Sjoergchar           *
20420253Sjoergnewstr(char const * p)
20520253Sjoerg{
20620253Sjoerg	char           *q = NULL;
20720253Sjoerg
20820253Sjoerg	if ((p = unquote(p)) != NULL) {
20920253Sjoerg		int             l = strlen(p) + 1;
21020253Sjoerg
21120253Sjoerg		if ((q = malloc(l)) != NULL)
21220253Sjoerg			memcpy(q, p, l);
21320253Sjoerg	}
21420253Sjoerg	return q;
21520253Sjoerg}
21620253Sjoerg
21720747Sdavidn#define LNBUFSZ 1024
21820253Sjoerg
21920747Sdavidn
22020253Sjoergstruct userconf *
22120253Sjoergread_userconfig(char const * file)
22220253Sjoerg{
22320253Sjoerg	FILE           *fp;
22420253Sjoerg
22520747Sdavidn	extendarray(&config.groups, &config.numgroups, 200);
22620747Sdavidn	memset(config.groups, 0, config.numgroups * sizeof(char *));
22720253Sjoerg	if (file == NULL)
22820253Sjoerg		file = _PATH_PW_CONF;
22920253Sjoerg	if ((fp = fopen(file, "r")) != NULL) {
23020747Sdavidn		int	    buflen = LNBUFSZ;
23120747Sdavidn		char       *buf = malloc(buflen);
23220253Sjoerg
23320747Sdavidn	nextline:
23420747Sdavidn		while (fgets(buf, buflen, fp) != NULL) {
23520747Sdavidn			char           *p;
23620253Sjoerg
23720747Sdavidn			while ((p = strchr(buf, '\n')) == NULL) {
23820747Sdavidn				int	  l;
23920747Sdavidn				if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
24020747Sdavidn					int	ch;
24120747Sdavidn					while ((ch = fgetc(fp)) != '\n' && ch != EOF);
24220747Sdavidn					goto nextline;	/* Ignore it */
24320747Sdavidn				}
24420747Sdavidn				l = strlen(buf);
24520747Sdavidn				if (fgets(buf + l, buflen - l, fp) == NULL)
24620747Sdavidn					break;	/* Unterminated last line */
24720747Sdavidn			}
24820253Sjoerg
24920747Sdavidn			if (p != NULL)
25020253Sjoerg				*p = '\0';
25120253Sjoerg
25220747Sdavidn			if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
25320747Sdavidn				static char const toks[] = " \t\r\n,=";
25420747Sdavidn				char           *q = strtok(NULL, toks);
25520747Sdavidn				int             i = 0;
25620747Sdavidn
25720747Sdavidn				while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
25820747Sdavidn					++i;
25920253Sjoerg#if debugging
26020747Sdavidn				if (i == _UC_FIELDS)
26120747Sdavidn					printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
26220747Sdavidn				else
26320747Sdavidn					printf("Got kwd[%s]=%s\n", p, q);
26420253Sjoerg#endif
26520747Sdavidn				switch (i) {
26620747Sdavidn				case _UC_DEFAULTPWD:
26720747Sdavidn					config.default_password = boolean_val(q, 1);
26820747Sdavidn					break;
26920747Sdavidn				case _UC_REUSEUID:
27020747Sdavidn					config.reuse_uids = boolean_val(q, 0);
27120747Sdavidn					break;
27220747Sdavidn				case _UC_REUSEGID:
27320747Sdavidn					config.reuse_gids = boolean_val(q, 0);
27420747Sdavidn					break;
27521330Sdavidn				case _UC_NISPASSWD:
27621330Sdavidn					config.nispasswd = (q == NULL || !boolean_val(q, 1))
27721330Sdavidn						? NULL : newstr(q);
27821330Sdavidn					break;
27920747Sdavidn				case _UC_DOTDIR:
28020747Sdavidn					config.dotdir = (q == NULL || !boolean_val(q, 1))
28120747Sdavidn						? NULL : newstr(q);
28220747Sdavidn					break;
28320747Sdavidn				case _UC_NEWMAIL:
28420747Sdavidn					config.newmail = (q == NULL || !boolean_val(q, 1))
28520747Sdavidn						? NULL : newstr(q);
28620747Sdavidn					break;
28720747Sdavidn				case _UC_LOGFILE:
28820747Sdavidn					config.logfile = (q == NULL || !boolean_val(q, 1))
28920747Sdavidn						? NULL : newstr(q);
29020747Sdavidn					break;
29120747Sdavidn				case _UC_HOMEROOT:
29220747Sdavidn					config.home = (q == NULL || !boolean_val(q, 1))
29320747Sdavidn						? "/home" : newstr(q);
29420747Sdavidn					break;
29520747Sdavidn				case _UC_SHELLPATH:
29620747Sdavidn					config.shelldir = (q == NULL || !boolean_val(q, 1))
29720747Sdavidn						? "/bin" : newstr(q);
29820747Sdavidn					break;
29920747Sdavidn				case _UC_SHELLS:
30020747Sdavidn					for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
30120747Sdavidn						system_shells[i] = newstr(q);
30220747Sdavidn					if (i > 0)
30320747Sdavidn						while (i < _UC_MAXSHELLS)
30420747Sdavidn							system_shells[i++] = NULL;
30520747Sdavidn					break;
30620747Sdavidn				case _UC_DEFAULTSHELL:
30720747Sdavidn					config.shell_default = (q == NULL || !boolean_val(q, 1))
30820747Sdavidn						? (char *) bourne_shell : newstr(q);
30920747Sdavidn					break;
31020747Sdavidn				case _UC_DEFAULTGROUP:
31129002Sdavidn					q = unquote(q);
31244229Sdavidn					config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
31320747Sdavidn						? NULL : newstr(q);
31420747Sdavidn					break;
31520747Sdavidn				case _UC_EXTRAGROUPS:
31620747Sdavidn					for (i = 0; q != NULL; q = strtok(NULL, toks)) {
31720747Sdavidn						if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
31820747Sdavidn							config.groups[i++] = newstr(q);
31920253Sjoerg					}
32020747Sdavidn					if (i > 0)
32120747Sdavidn						while (i < config.numgroups)
32220747Sdavidn							config.groups[i++] = NULL;
32320747Sdavidn					break;
32420747Sdavidn				case _UC_DEFAULTCLASS:
32520747Sdavidn					config.default_class = (q == NULL || !boolean_val(q, 1))
32620747Sdavidn						? NULL : newstr(q);
32720747Sdavidn					break;
32820747Sdavidn				case _UC_MINUID:
32920747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
33020747Sdavidn						config.min_uid = (uid_t) atol(q);
33120747Sdavidn					break;
33220747Sdavidn				case _UC_MAXUID:
33320747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
33420747Sdavidn						config.max_uid = (uid_t) atol(q);
33520747Sdavidn					break;
33620747Sdavidn				case _UC_MINGID:
33720747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
33820747Sdavidn						config.min_gid = (gid_t) atol(q);
33920747Sdavidn					break;
34020747Sdavidn				case _UC_MAXGID:
34120747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
34220747Sdavidn						config.max_gid = (gid_t) atol(q);
34320747Sdavidn					break;
34420747Sdavidn				case _UC_EXPIRE:
34520747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
34620747Sdavidn						config.expire_days = atoi(q);
34720747Sdavidn					break;
34820747Sdavidn				case _UC_PASSWORD:
34920747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
35020747Sdavidn						config.password_days = atoi(q);
35120747Sdavidn					break;
35220747Sdavidn				case _UC_FIELDS:
35320747Sdavidn				case _UC_NONE:
35420747Sdavidn					break;
35520253Sjoerg				}
35620253Sjoerg			}
35720253Sjoerg		}
35820747Sdavidn		free(buf);
35920253Sjoerg		fclose(fp);
36020253Sjoerg	}
36120253Sjoerg	return &config;
36220253Sjoerg}
36320253Sjoerg
36420253Sjoerg
36520253Sjoergint
36620253Sjoergwrite_userconfig(char const * file)
36720253Sjoerg{
36820253Sjoerg	int             fd;
36920253Sjoerg
37020253Sjoerg	if (file == NULL)
37120253Sjoerg		file = _PATH_PW_CONF;
37220253Sjoerg
37320253Sjoerg	if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
37420253Sjoerg		FILE           *fp;
37520253Sjoerg
37620253Sjoerg		if ((fp = fdopen(fd, "w")) == NULL)
37720253Sjoerg			close(fd);
37820253Sjoerg		else {
37920253Sjoerg			int             i, j, k;
38020747Sdavidn			int		len = LNBUFSZ;
38120747Sdavidn			char           *buf = malloc(len);
38220253Sjoerg
38320253Sjoerg			for (i = _UC_NONE; i < _UC_FIELDS; i++) {
38420253Sjoerg				int             quote = 1;
38520253Sjoerg				char const     *val = buf;
38620253Sjoerg
38720253Sjoerg				*buf = '\0';
38820253Sjoerg				switch (i) {
38920253Sjoerg				case _UC_DEFAULTPWD:
39020253Sjoerg					val = boolean_str(config.default_password);
39120253Sjoerg					break;
39220253Sjoerg				case _UC_REUSEUID:
39320253Sjoerg					val = boolean_str(config.reuse_uids);
39420253Sjoerg					break;
39520253Sjoerg				case _UC_REUSEGID:
39620253Sjoerg					val = boolean_str(config.reuse_gids);
39720253Sjoerg					break;
39821330Sdavidn				case _UC_NISPASSWD:
39921330Sdavidn					val = config.nispasswd ? config.nispasswd : "";
40021330Sdavidn					quote = 0;
40121330Sdavidn					break;
40220253Sjoerg				case _UC_DOTDIR:
40320253Sjoerg					val = config.dotdir ? config.dotdir : boolean_str(0);
40420253Sjoerg					break;
40520253Sjoerg				case _UC_NEWMAIL:
40620253Sjoerg					val = config.newmail ? config.newmail : boolean_str(0);
40720253Sjoerg					break;
40820253Sjoerg				case _UC_LOGFILE:
40920253Sjoerg					val = config.logfile ? config.logfile : boolean_str(0);
41020253Sjoerg					break;
41120253Sjoerg				case _UC_HOMEROOT:
41220253Sjoerg					val = config.home;
41320253Sjoerg					break;
41420253Sjoerg				case _UC_SHELLPATH:
41520253Sjoerg					val = config.shelldir;
41620253Sjoerg					break;
41720253Sjoerg				case _UC_SHELLS:
41820747Sdavidn					for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
41920747Sdavidn						char	lbuf[64];
42020747Sdavidn						int	l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
42120747Sdavidn						if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
42220747Sdavidn							strcpy(buf + k, lbuf);
42320747Sdavidn							k += l;
42420747Sdavidn						}
42520747Sdavidn					}
42620253Sjoerg					quote = 0;
42720253Sjoerg					break;
42820253Sjoerg				case _UC_DEFAULTSHELL:
42920253Sjoerg					val = config.shell_default ? config.shell_default : bourne_shell;
43020253Sjoerg					break;
43120253Sjoerg				case _UC_DEFAULTGROUP:
43220253Sjoerg					val = config.default_group ? config.default_group : "";
43320253Sjoerg					break;
43420253Sjoerg				case _UC_EXTRAGROUPS:
43520747Sdavidn					extendarray(&config.groups, &config.numgroups, 200);
43620747Sdavidn					for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
43720747Sdavidn						char	lbuf[64];
43820747Sdavidn						int	l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
43920747Sdavidn						if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
44020747Sdavidn							strcpy(buf + k, lbuf);
44120747Sdavidn							k +=  l;
44220747Sdavidn						}
44320747Sdavidn					}
44420253Sjoerg					quote = 0;
44520253Sjoerg					break;
44620253Sjoerg				case _UC_DEFAULTCLASS:
44720253Sjoerg					val = config.default_class ? config.default_class : "";
44820253Sjoerg					break;
44920253Sjoerg				case _UC_MINUID:
45020253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.min_uid);
45120253Sjoerg					quote = 0;
45220253Sjoerg					break;
45320253Sjoerg				case _UC_MAXUID:
45420253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.max_uid);
45520253Sjoerg					quote = 0;
45620253Sjoerg					break;
45720253Sjoerg				case _UC_MINGID:
45820253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.min_gid);
45920253Sjoerg					quote = 0;
46020253Sjoerg					break;
46120253Sjoerg				case _UC_MAXGID:
46220253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.max_gid);
46320253Sjoerg					quote = 0;
46420253Sjoerg					break;
46520253Sjoerg				case _UC_EXPIRE:
46620253Sjoerg					sprintf(buf, "%d", config.expire_days);
46720253Sjoerg					quote = 0;
46820253Sjoerg					break;
46920253Sjoerg				case _UC_PASSWORD:
47020253Sjoerg					sprintf(buf, "%d", config.password_days);
47120253Sjoerg					quote = 0;
47220253Sjoerg					break;
47320253Sjoerg				case _UC_NONE:
47420253Sjoerg					break;
47520253Sjoerg				}
47620253Sjoerg
47720253Sjoerg				if (comments[i])
47820253Sjoerg					fputs(comments[i], fp);
47920253Sjoerg
48020253Sjoerg				if (*kwds[i]) {
48120253Sjoerg					if (quote)
48220253Sjoerg						fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
48320253Sjoerg					else
48420253Sjoerg						fprintf(fp, "%s = %s\n", kwds[i], val);
48520253Sjoerg#if debugging
48620253Sjoerg					printf("WROTE: %s = %s\n", kwds[i], val);
48720253Sjoerg#endif
48820253Sjoerg				}
48920253Sjoerg			}
49020747Sdavidn			free(buf);
49120253Sjoerg			return fclose(fp) != EOF;
49220253Sjoerg		}
49320253Sjoerg	}
49420253Sjoerg	return 0;
49520253Sjoerg}
496