pw_conf.c revision 70133
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[] =
2950479Speter  "$FreeBSD: head/usr.sbin/pw/pw_conf.c 70133 2000-12-18 01:35:56Z dougb $";
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,
7063239Sdavidn	"csh",
7163239Sdavidn	"tcsh"
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 */
10249171Sdavidn	0,			/* Days until password expires */
10349171Sdavidn	0			/* size of default_group array */
10420253Sjoerg};
10520253Sjoerg
10620253Sjoergstatic char const *comments[_UC_FIELDS] =
10720253Sjoerg{
10820253Sjoerg	"#\n# pw.conf - user/group configuration defaults\n#\n",
10920253Sjoerg	"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
11020253Sjoerg	"\n# Reuse gaps in uid sequence? (yes or no)\n",
11120253Sjoerg	"\n# Reuse gaps in gid sequence? (yes or no)\n",
11221330Sdavidn	"\n# Path to the NIS passwd file (blank or 'no' for none)\n",
11320253Sjoerg	"\n# Obtain default dotfiles from this directory\n",
11420253Sjoerg	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
11520253Sjoerg	"\n# Log add/change/remove information in this file\n",
11620253Sjoerg	"\n# Root directory in which $HOME directory is created\n",
11720253Sjoerg	"\n# Colon separated list of directories containing valid shells\n",
11870133Sdougb	"\n# Comma separated list of available shells (without paths)\n",
11920253Sjoerg	"\n# Default shell (without path)\n",
12020253Sjoerg	"\n# Default group (leave blank for new group per user)\n",
12120253Sjoerg	"\n# Extra groups for new users\n",
12220253Sjoerg	"\n# Default login class for new users\n",
12320253Sjoerg	"\n# Range of valid default user ids\n",
12420253Sjoerg	NULL,
12520253Sjoerg	"\n# Range of valid default group ids\n",
12620253Sjoerg	NULL,
12720253Sjoerg	"\n# Days after which account expires (0=disabled)\n",
12820253Sjoerg	"\n# Days after which password expires (0=disabled)\n"
12920253Sjoerg};
13020253Sjoerg
13120253Sjoergstatic char const *kwds[] =
13220253Sjoerg{
13320253Sjoerg	"",
13420253Sjoerg	"defaultpasswd",
13520253Sjoerg	"reuseuids",
13620253Sjoerg	"reusegids",
13721330Sdavidn	"nispasswd",
13820253Sjoerg	"skeleton",
13920253Sjoerg	"newmail",
14020253Sjoerg	"logfile",
14120253Sjoerg	"home",
14220253Sjoerg	"shellpath",
14320253Sjoerg	"shells",
14420253Sjoerg	"defaultshell",
14520253Sjoerg	"defaultgroup",
14620253Sjoerg	"extragroups",
14720253Sjoerg	"defaultclass",
14820253Sjoerg	"minuid",
14920253Sjoerg	"maxuid",
15020253Sjoerg	"mingid",
15120253Sjoerg	"maxgid",
15220253Sjoerg	"expire_days",
15320253Sjoerg	"password_days",
15420253Sjoerg	NULL
15520253Sjoerg};
15620253Sjoerg
15720253Sjoergstatic char    *
15820253Sjoergunquote(char const * str)
15920253Sjoerg{
16020253Sjoerg	if (str && (*str == '"' || *str == '\'')) {
16120253Sjoerg		char           *p = strchr(str + 1, *str);
16220253Sjoerg
16320253Sjoerg		if (p != NULL)
16420253Sjoerg			*p = '\0';
16520253Sjoerg		return (char *) (*++str ? str : NULL);
16620253Sjoerg	}
16720253Sjoerg	return (char *) str;
16820253Sjoerg}
16920253Sjoerg
17020253Sjoergint
17120253Sjoergboolean_val(char const * str, int dflt)
17220253Sjoerg{
17320253Sjoerg	if ((str = unquote(str)) != NULL) {
17420253Sjoerg		int             i;
17520253Sjoerg
17620253Sjoerg		for (i = 0; booltrue[i]; i++)
17720253Sjoerg			if (strcmp(str, booltrue[i]) == 0)
17820253Sjoerg				return 1;
17920253Sjoerg		for (i = 0; boolfalse[i]; i++)
18020253Sjoerg			if (strcmp(str, boolfalse[i]) == 0)
18120253Sjoerg				return 0;
18220253Sjoerg
18320253Sjoerg		/*
18420253Sjoerg		 * Special cases for defaultpassword
18520253Sjoerg		 */
18620253Sjoerg		if (strcmp(str, "random") == 0)
18720253Sjoerg			return -1;
18820253Sjoerg		if (strcmp(str, "none") == 0)
18920253Sjoerg			return -2;
19020253Sjoerg	}
19120253Sjoerg	return dflt;
19220253Sjoerg}
19320253Sjoerg
19420253Sjoergchar const     *
19520253Sjoergboolean_str(int val)
19620253Sjoerg{
19720253Sjoerg	if (val == -1)
19820253Sjoerg		return "random";
19920253Sjoerg	else if (val == -2)
20020253Sjoerg		return "none";
20120253Sjoerg	else
20220253Sjoerg		return val ? booltrue[0] : boolfalse[0];
20320253Sjoerg}
20420253Sjoerg
20520253Sjoergchar           *
20620253Sjoergnewstr(char const * p)
20720253Sjoerg{
20820253Sjoerg	char           *q = NULL;
20920253Sjoerg
21020253Sjoerg	if ((p = unquote(p)) != NULL) {
21120253Sjoerg		int             l = strlen(p) + 1;
21220253Sjoerg
21320253Sjoerg		if ((q = malloc(l)) != NULL)
21420253Sjoerg			memcpy(q, p, l);
21520253Sjoerg	}
21620253Sjoerg	return q;
21720253Sjoerg}
21820253Sjoerg
21920747Sdavidn#define LNBUFSZ 1024
22020253Sjoerg
22120747Sdavidn
22220253Sjoergstruct userconf *
22320253Sjoergread_userconfig(char const * file)
22420253Sjoerg{
22520253Sjoerg	FILE           *fp;
22620253Sjoerg
22720747Sdavidn	extendarray(&config.groups, &config.numgroups, 200);
22820747Sdavidn	memset(config.groups, 0, config.numgroups * sizeof(char *));
22920253Sjoerg	if (file == NULL)
23020253Sjoerg		file = _PATH_PW_CONF;
23120253Sjoerg	if ((fp = fopen(file, "r")) != NULL) {
23220747Sdavidn		int	    buflen = LNBUFSZ;
23320747Sdavidn		char       *buf = malloc(buflen);
23420253Sjoerg
23520747Sdavidn	nextline:
23620747Sdavidn		while (fgets(buf, buflen, fp) != NULL) {
23720747Sdavidn			char           *p;
23820253Sjoerg
23920747Sdavidn			while ((p = strchr(buf, '\n')) == NULL) {
24020747Sdavidn				int	  l;
24120747Sdavidn				if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
24220747Sdavidn					int	ch;
24320747Sdavidn					while ((ch = fgetc(fp)) != '\n' && ch != EOF);
24420747Sdavidn					goto nextline;	/* Ignore it */
24520747Sdavidn				}
24620747Sdavidn				l = strlen(buf);
24720747Sdavidn				if (fgets(buf + l, buflen - l, fp) == NULL)
24820747Sdavidn					break;	/* Unterminated last line */
24920747Sdavidn			}
25020253Sjoerg
25120747Sdavidn			if (p != NULL)
25220253Sjoerg				*p = '\0';
25320253Sjoerg
25420747Sdavidn			if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
25520747Sdavidn				static char const toks[] = " \t\r\n,=";
25620747Sdavidn				char           *q = strtok(NULL, toks);
25720747Sdavidn				int             i = 0;
25820747Sdavidn
25920747Sdavidn				while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
26020747Sdavidn					++i;
26120253Sjoerg#if debugging
26220747Sdavidn				if (i == _UC_FIELDS)
26320747Sdavidn					printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
26420747Sdavidn				else
26520747Sdavidn					printf("Got kwd[%s]=%s\n", p, q);
26620253Sjoerg#endif
26720747Sdavidn				switch (i) {
26820747Sdavidn				case _UC_DEFAULTPWD:
26920747Sdavidn					config.default_password = boolean_val(q, 1);
27020747Sdavidn					break;
27120747Sdavidn				case _UC_REUSEUID:
27220747Sdavidn					config.reuse_uids = boolean_val(q, 0);
27320747Sdavidn					break;
27420747Sdavidn				case _UC_REUSEGID:
27520747Sdavidn					config.reuse_gids = boolean_val(q, 0);
27620747Sdavidn					break;
27721330Sdavidn				case _UC_NISPASSWD:
27821330Sdavidn					config.nispasswd = (q == NULL || !boolean_val(q, 1))
27921330Sdavidn						? NULL : newstr(q);
28021330Sdavidn					break;
28120747Sdavidn				case _UC_DOTDIR:
28220747Sdavidn					config.dotdir = (q == NULL || !boolean_val(q, 1))
28320747Sdavidn						? NULL : newstr(q);
28420747Sdavidn					break;
28520747Sdavidn				case _UC_NEWMAIL:
28620747Sdavidn					config.newmail = (q == NULL || !boolean_val(q, 1))
28720747Sdavidn						? NULL : newstr(q);
28820747Sdavidn					break;
28920747Sdavidn				case _UC_LOGFILE:
29020747Sdavidn					config.logfile = (q == NULL || !boolean_val(q, 1))
29120747Sdavidn						? NULL : newstr(q);
29220747Sdavidn					break;
29320747Sdavidn				case _UC_HOMEROOT:
29420747Sdavidn					config.home = (q == NULL || !boolean_val(q, 1))
29520747Sdavidn						? "/home" : newstr(q);
29620747Sdavidn					break;
29720747Sdavidn				case _UC_SHELLPATH:
29820747Sdavidn					config.shelldir = (q == NULL || !boolean_val(q, 1))
29920747Sdavidn						? "/bin" : newstr(q);
30020747Sdavidn					break;
30120747Sdavidn				case _UC_SHELLS:
30220747Sdavidn					for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
30320747Sdavidn						system_shells[i] = newstr(q);
30420747Sdavidn					if (i > 0)
30520747Sdavidn						while (i < _UC_MAXSHELLS)
30620747Sdavidn							system_shells[i++] = NULL;
30720747Sdavidn					break;
30820747Sdavidn				case _UC_DEFAULTSHELL:
30920747Sdavidn					config.shell_default = (q == NULL || !boolean_val(q, 1))
31020747Sdavidn						? (char *) bourne_shell : newstr(q);
31120747Sdavidn					break;
31220747Sdavidn				case _UC_DEFAULTGROUP:
31329002Sdavidn					q = unquote(q);
31444229Sdavidn					config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
31520747Sdavidn						? NULL : newstr(q);
31620747Sdavidn					break;
31720747Sdavidn				case _UC_EXTRAGROUPS:
31820747Sdavidn					for (i = 0; q != NULL; q = strtok(NULL, toks)) {
31920747Sdavidn						if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
32020747Sdavidn							config.groups[i++] = newstr(q);
32120253Sjoerg					}
32220747Sdavidn					if (i > 0)
32320747Sdavidn						while (i < config.numgroups)
32420747Sdavidn							config.groups[i++] = NULL;
32520747Sdavidn					break;
32620747Sdavidn				case _UC_DEFAULTCLASS:
32720747Sdavidn					config.default_class = (q == NULL || !boolean_val(q, 1))
32820747Sdavidn						? NULL : newstr(q);
32920747Sdavidn					break;
33020747Sdavidn				case _UC_MINUID:
33120747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
33220747Sdavidn						config.min_uid = (uid_t) atol(q);
33320747Sdavidn					break;
33420747Sdavidn				case _UC_MAXUID:
33520747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
33620747Sdavidn						config.max_uid = (uid_t) atol(q);
33720747Sdavidn					break;
33820747Sdavidn				case _UC_MINGID:
33920747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
34020747Sdavidn						config.min_gid = (gid_t) atol(q);
34120747Sdavidn					break;
34220747Sdavidn				case _UC_MAXGID:
34320747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
34420747Sdavidn						config.max_gid = (gid_t) atol(q);
34520747Sdavidn					break;
34620747Sdavidn				case _UC_EXPIRE:
34720747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
34820747Sdavidn						config.expire_days = atoi(q);
34920747Sdavidn					break;
35020747Sdavidn				case _UC_PASSWORD:
35120747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
35220747Sdavidn						config.password_days = atoi(q);
35320747Sdavidn					break;
35420747Sdavidn				case _UC_FIELDS:
35520747Sdavidn				case _UC_NONE:
35620747Sdavidn					break;
35720253Sjoerg				}
35820253Sjoerg			}
35920253Sjoerg		}
36020747Sdavidn		free(buf);
36120253Sjoerg		fclose(fp);
36220253Sjoerg	}
36320253Sjoerg	return &config;
36420253Sjoerg}
36520253Sjoerg
36620253Sjoerg
36720253Sjoergint
36820253Sjoergwrite_userconfig(char const * file)
36920253Sjoerg{
37020253Sjoerg	int             fd;
37120253Sjoerg
37220253Sjoerg	if (file == NULL)
37320253Sjoerg		file = _PATH_PW_CONF;
37420253Sjoerg
37520253Sjoerg	if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
37620253Sjoerg		FILE           *fp;
37720253Sjoerg
37820253Sjoerg		if ((fp = fdopen(fd, "w")) == NULL)
37920253Sjoerg			close(fd);
38020253Sjoerg		else {
38120253Sjoerg			int             i, j, k;
38220747Sdavidn			int		len = LNBUFSZ;
38320747Sdavidn			char           *buf = malloc(len);
38420253Sjoerg
38520253Sjoerg			for (i = _UC_NONE; i < _UC_FIELDS; i++) {
38620253Sjoerg				int             quote = 1;
38720253Sjoerg				char const     *val = buf;
38820253Sjoerg
38920253Sjoerg				*buf = '\0';
39020253Sjoerg				switch (i) {
39120253Sjoerg				case _UC_DEFAULTPWD:
39220253Sjoerg					val = boolean_str(config.default_password);
39320253Sjoerg					break;
39420253Sjoerg				case _UC_REUSEUID:
39520253Sjoerg					val = boolean_str(config.reuse_uids);
39620253Sjoerg					break;
39720253Sjoerg				case _UC_REUSEGID:
39820253Sjoerg					val = boolean_str(config.reuse_gids);
39920253Sjoerg					break;
40021330Sdavidn				case _UC_NISPASSWD:
40121330Sdavidn					val = config.nispasswd ? config.nispasswd : "";
40221330Sdavidn					quote = 0;
40321330Sdavidn					break;
40420253Sjoerg				case _UC_DOTDIR:
40520253Sjoerg					val = config.dotdir ? config.dotdir : boolean_str(0);
40620253Sjoerg					break;
40720253Sjoerg				case _UC_NEWMAIL:
40820253Sjoerg					val = config.newmail ? config.newmail : boolean_str(0);
40920253Sjoerg					break;
41020253Sjoerg				case _UC_LOGFILE:
41120253Sjoerg					val = config.logfile ? config.logfile : boolean_str(0);
41220253Sjoerg					break;
41320253Sjoerg				case _UC_HOMEROOT:
41420253Sjoerg					val = config.home;
41520253Sjoerg					break;
41620253Sjoerg				case _UC_SHELLPATH:
41720253Sjoerg					val = config.shelldir;
41820253Sjoerg					break;
41920253Sjoerg				case _UC_SHELLS:
42020747Sdavidn					for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
42120747Sdavidn						char	lbuf[64];
42220747Sdavidn						int	l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
42320747Sdavidn						if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
42420747Sdavidn							strcpy(buf + k, lbuf);
42520747Sdavidn							k += l;
42620747Sdavidn						}
42720747Sdavidn					}
42820253Sjoerg					quote = 0;
42920253Sjoerg					break;
43020253Sjoerg				case _UC_DEFAULTSHELL:
43120253Sjoerg					val = config.shell_default ? config.shell_default : bourne_shell;
43220253Sjoerg					break;
43320253Sjoerg				case _UC_DEFAULTGROUP:
43420253Sjoerg					val = config.default_group ? config.default_group : "";
43520253Sjoerg					break;
43620253Sjoerg				case _UC_EXTRAGROUPS:
43720747Sdavidn					extendarray(&config.groups, &config.numgroups, 200);
43820747Sdavidn					for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
43920747Sdavidn						char	lbuf[64];
44020747Sdavidn						int	l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
44120747Sdavidn						if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
44220747Sdavidn							strcpy(buf + k, lbuf);
44320747Sdavidn							k +=  l;
44420747Sdavidn						}
44520747Sdavidn					}
44620253Sjoerg					quote = 0;
44720253Sjoerg					break;
44820253Sjoerg				case _UC_DEFAULTCLASS:
44920253Sjoerg					val = config.default_class ? config.default_class : "";
45020253Sjoerg					break;
45120253Sjoerg				case _UC_MINUID:
45220253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.min_uid);
45320253Sjoerg					quote = 0;
45420253Sjoerg					break;
45520253Sjoerg				case _UC_MAXUID:
45620253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.max_uid);
45720253Sjoerg					quote = 0;
45820253Sjoerg					break;
45920253Sjoerg				case _UC_MINGID:
46020253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.min_gid);
46120253Sjoerg					quote = 0;
46220253Sjoerg					break;
46320253Sjoerg				case _UC_MAXGID:
46420253Sjoerg					sprintf(buf, "%lu", (unsigned long) config.max_gid);
46520253Sjoerg					quote = 0;
46620253Sjoerg					break;
46720253Sjoerg				case _UC_EXPIRE:
46820253Sjoerg					sprintf(buf, "%d", config.expire_days);
46920253Sjoerg					quote = 0;
47020253Sjoerg					break;
47120253Sjoerg				case _UC_PASSWORD:
47220253Sjoerg					sprintf(buf, "%d", config.password_days);
47320253Sjoerg					quote = 0;
47420253Sjoerg					break;
47520253Sjoerg				case _UC_NONE:
47620253Sjoerg					break;
47720253Sjoerg				}
47820253Sjoerg
47920253Sjoerg				if (comments[i])
48020253Sjoerg					fputs(comments[i], fp);
48120253Sjoerg
48220253Sjoerg				if (*kwds[i]) {
48320253Sjoerg					if (quote)
48420253Sjoerg						fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
48520253Sjoerg					else
48620253Sjoerg						fprintf(fp, "%s = %s\n", kwds[i], val);
48720253Sjoerg#if debugging
48820253Sjoerg					printf("WROTE: %s = %s\n", kwds[i], val);
48920253Sjoerg#endif
49020253Sjoerg				}
49120253Sjoerg			}
49220747Sdavidn			free(buf);
49320253Sjoerg			return fclose(fp) != EOF;
49420253Sjoerg		}
49520253Sjoerg	}
49620253Sjoerg	return 0;
49720253Sjoerg}
498