pw_conf.c revision 282697
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 282697 2015-05-09 21:53:33Z bapt $";
3030259Scharnier#endif /* not lint */
3130259Scharnier
32282681Sbapt#include <sys/types.h>
33282681Sbapt#include <sys/sbuf.h>
3420253Sjoerg#include <string.h>
3520253Sjoerg#include <ctype.h>
3620253Sjoerg#include <fcntl.h>
3720253Sjoerg
3820253Sjoerg#include "pw.h"
3920253Sjoerg
4020253Sjoerg#define debugging 0
4120253Sjoerg
4220253Sjoergenum {
4320253Sjoerg	_UC_NONE,
4420253Sjoerg	_UC_DEFAULTPWD,
4520253Sjoerg	_UC_REUSEUID,
4620253Sjoerg	_UC_REUSEGID,
4721330Sdavidn	_UC_NISPASSWD,
4820253Sjoerg	_UC_DOTDIR,
4920253Sjoerg	_UC_NEWMAIL,
5020253Sjoerg	_UC_LOGFILE,
5120253Sjoerg	_UC_HOMEROOT,
52168044Sle	_UC_HOMEMODE,
5320253Sjoerg	_UC_SHELLPATH,
5420253Sjoerg	_UC_SHELLS,
5520253Sjoerg	_UC_DEFAULTSHELL,
5620253Sjoerg	_UC_DEFAULTGROUP,
5720253Sjoerg	_UC_EXTRAGROUPS,
5820253Sjoerg	_UC_DEFAULTCLASS,
5920253Sjoerg	_UC_MINUID,
6020253Sjoerg	_UC_MAXUID,
6120253Sjoerg	_UC_MINGID,
6220253Sjoerg	_UC_MAXGID,
6320253Sjoerg	_UC_EXPIRE,
6420253Sjoerg	_UC_PASSWORD,
6520253Sjoerg	_UC_FIELDS
6620253Sjoerg};
6720253Sjoerg
6820253Sjoergstatic char     bourne_shell[] = "sh";
6920253Sjoerg
7020253Sjoergstatic char    *system_shells[_UC_MAXSHELLS] =
7120253Sjoerg{
7220253Sjoerg	bourne_shell,
7363239Sdavidn	"csh",
7463239Sdavidn	"tcsh"
7520253Sjoerg};
7620253Sjoerg
7720253Sjoergstatic char const *booltrue[] =
7820253Sjoerg{
7920253Sjoerg	"yes", "true", "1", "on", NULL
8020253Sjoerg};
8120253Sjoergstatic char const *boolfalse[] =
8220253Sjoerg{
8320253Sjoerg	"no", "false", "0", "off", NULL
8420253Sjoerg};
8520253Sjoerg
8620253Sjoergstatic struct userconf config =
8720253Sjoerg{
8820253Sjoerg	0,			/* Default password for new users? (nologin) */
8920253Sjoerg	0,			/* Reuse uids? */
9020253Sjoerg	0,			/* Reuse gids? */
9121330Sdavidn	NULL,			/* NIS version of the passwd file */
9220253Sjoerg	"/usr/share/skel",	/* Where to obtain skeleton files */
9320253Sjoerg	NULL,			/* Mail to send to new accounts */
9420253Sjoerg	"/var/log/userlog",	/* Where to log changes */
9520253Sjoerg	"/home",		/* Where to create home directory */
96219408Sjkim	_DEF_DIRMODE,		/* Home directory perms, modified by umask */
9720253Sjoerg	"/bin",			/* Where shells are located */
9820253Sjoerg	system_shells,		/* List of shells (first is default) */
9920253Sjoerg	bourne_shell,		/* Default shell */
10020253Sjoerg	NULL,			/* Default group name */
10120747Sdavidn	NULL,			/* Default (additional) groups */
10220253Sjoerg	NULL,			/* Default login class */
10320253Sjoerg	1000, 32000,		/* Allowed range of uids */
10420253Sjoerg	1000, 32000,		/* Allowed range of gids */
10520253Sjoerg	0,			/* Days until account expires */
10649171Sdavidn	0,			/* Days until password expires */
10749171Sdavidn	0			/* size of default_group array */
10820253Sjoerg};
10920253Sjoerg
11020253Sjoergstatic char const *comments[_UC_FIELDS] =
11120253Sjoerg{
11220253Sjoerg	"#\n# pw.conf - user/group configuration defaults\n#\n",
11320253Sjoerg	"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
11420253Sjoerg	"\n# Reuse gaps in uid sequence? (yes or no)\n",
11520253Sjoerg	"\n# Reuse gaps in gid sequence? (yes or no)\n",
11621330Sdavidn	"\n# Path to the NIS passwd file (blank or 'no' for none)\n",
11720253Sjoerg	"\n# Obtain default dotfiles from this directory\n",
11820253Sjoerg	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
11920253Sjoerg	"\n# Log add/change/remove information in this file\n",
12020253Sjoerg	"\n# Root directory in which $HOME directory is created\n",
121168044Sle	"\n# Mode for the new $HOME directory, will be modified by umask\n",
12220253Sjoerg	"\n# Colon separated list of directories containing valid shells\n",
12370133Sdougb	"\n# Comma separated list of available shells (without paths)\n",
12420253Sjoerg	"\n# Default shell (without path)\n",
12520253Sjoerg	"\n# Default group (leave blank for new group per user)\n",
12620253Sjoerg	"\n# Extra groups for new users\n",
12720253Sjoerg	"\n# Default login class for new users\n",
12820253Sjoerg	"\n# Range of valid default user ids\n",
12920253Sjoerg	NULL,
13020253Sjoerg	"\n# Range of valid default group ids\n",
13120253Sjoerg	NULL,
13220253Sjoerg	"\n# Days after which account expires (0=disabled)\n",
13320253Sjoerg	"\n# Days after which password expires (0=disabled)\n"
13420253Sjoerg};
13520253Sjoerg
13620253Sjoergstatic char const *kwds[] =
13720253Sjoerg{
13820253Sjoerg	"",
13920253Sjoerg	"defaultpasswd",
14020253Sjoerg	"reuseuids",
14120253Sjoerg	"reusegids",
14221330Sdavidn	"nispasswd",
14320253Sjoerg	"skeleton",
14420253Sjoerg	"newmail",
14520253Sjoerg	"logfile",
14620253Sjoerg	"home",
147168044Sle	"homemode",
14820253Sjoerg	"shellpath",
14920253Sjoerg	"shells",
15020253Sjoerg	"defaultshell",
15120253Sjoerg	"defaultgroup",
15220253Sjoerg	"extragroups",
15320253Sjoerg	"defaultclass",
15420253Sjoerg	"minuid",
15520253Sjoerg	"maxuid",
15620253Sjoerg	"mingid",
15720253Sjoerg	"maxgid",
15820253Sjoerg	"expire_days",
15920253Sjoerg	"password_days",
16020253Sjoerg	NULL
16120253Sjoerg};
16220253Sjoerg
16320253Sjoergstatic char    *
16420253Sjoergunquote(char const * str)
16520253Sjoerg{
16620253Sjoerg	if (str && (*str == '"' || *str == '\'')) {
16720253Sjoerg		char           *p = strchr(str + 1, *str);
16820253Sjoerg
16920253Sjoerg		if (p != NULL)
17020253Sjoerg			*p = '\0';
17120253Sjoerg		return (char *) (*++str ? str : NULL);
17220253Sjoerg	}
17320253Sjoerg	return (char *) str;
17420253Sjoerg}
17520253Sjoerg
17620253Sjoergint
17720253Sjoergboolean_val(char const * str, int dflt)
17820253Sjoerg{
17920253Sjoerg	if ((str = unquote(str)) != NULL) {
18020253Sjoerg		int             i;
18120253Sjoerg
18220253Sjoerg		for (i = 0; booltrue[i]; i++)
18320253Sjoerg			if (strcmp(str, booltrue[i]) == 0)
18420253Sjoerg				return 1;
18520253Sjoerg		for (i = 0; boolfalse[i]; i++)
18620253Sjoerg			if (strcmp(str, boolfalse[i]) == 0)
18720253Sjoerg				return 0;
18820253Sjoerg
18920253Sjoerg		/*
19020253Sjoerg		 * Special cases for defaultpassword
19120253Sjoerg		 */
19220253Sjoerg		if (strcmp(str, "random") == 0)
19320253Sjoerg			return -1;
19420253Sjoerg		if (strcmp(str, "none") == 0)
19520253Sjoerg			return -2;
19620253Sjoerg	}
19720253Sjoerg	return dflt;
19820253Sjoerg}
19920253Sjoerg
20020253Sjoergchar const     *
20120253Sjoergboolean_str(int val)
20220253Sjoerg{
20320253Sjoerg	if (val == -1)
20420253Sjoerg		return "random";
20520253Sjoerg	else if (val == -2)
20620253Sjoerg		return "none";
20720253Sjoerg	else
20820253Sjoerg		return val ? booltrue[0] : boolfalse[0];
20920253Sjoerg}
21020253Sjoerg
21120253Sjoergchar           *
21220253Sjoergnewstr(char const * p)
21320253Sjoerg{
21420253Sjoerg	char           *q = NULL;
21520253Sjoerg
21620253Sjoerg	if ((p = unquote(p)) != NULL) {
21720253Sjoerg		int             l = strlen(p) + 1;
21820253Sjoerg
21920253Sjoerg		if ((q = malloc(l)) != NULL)
22020253Sjoerg			memcpy(q, p, l);
22120253Sjoerg	}
22220253Sjoerg	return q;
22320253Sjoerg}
22420253Sjoerg
22520747Sdavidn#define LNBUFSZ 1024
22620253Sjoerg
22720747Sdavidn
22820253Sjoergstruct userconf *
22920253Sjoergread_userconfig(char const * file)
23020253Sjoerg{
231264781Sbapt	FILE	*fp;
232264781Sbapt	char	*buf, *p;
233264781Sbapt	size_t	linecap;
234264781Sbapt	ssize_t	linelen;
23520253Sjoerg
236264781Sbapt	buf = NULL;
237264781Sbapt	linecap = 0;
238264781Sbapt
23920747Sdavidn	extendarray(&config.groups, &config.numgroups, 200);
24020747Sdavidn	memset(config.groups, 0, config.numgroups * sizeof(char *));
24120253Sjoerg	if (file == NULL)
24220253Sjoerg		file = _PATH_PW_CONF;
243264781Sbapt
24420253Sjoerg	if ((fp = fopen(file, "r")) != NULL) {
245264781Sbapt		while ((linelen = getline(&buf, &linecap, fp)) > 0) {
24620747Sdavidn			if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
24720747Sdavidn				static char const toks[] = " \t\r\n,=";
24820747Sdavidn				char           *q = strtok(NULL, toks);
24920747Sdavidn				int             i = 0;
250168044Sle				mode_t          *modeset;
25120747Sdavidn
25220747Sdavidn				while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
25320747Sdavidn					++i;
25420253Sjoerg#if debugging
25520747Sdavidn				if (i == _UC_FIELDS)
25620747Sdavidn					printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
25720747Sdavidn				else
25820747Sdavidn					printf("Got kwd[%s]=%s\n", p, q);
25920253Sjoerg#endif
26020747Sdavidn				switch (i) {
26120747Sdavidn				case _UC_DEFAULTPWD:
26220747Sdavidn					config.default_password = boolean_val(q, 1);
26320747Sdavidn					break;
26420747Sdavidn				case _UC_REUSEUID:
26520747Sdavidn					config.reuse_uids = boolean_val(q, 0);
26620747Sdavidn					break;
26720747Sdavidn				case _UC_REUSEGID:
26820747Sdavidn					config.reuse_gids = boolean_val(q, 0);
26920747Sdavidn					break;
27021330Sdavidn				case _UC_NISPASSWD:
27121330Sdavidn					config.nispasswd = (q == NULL || !boolean_val(q, 1))
27221330Sdavidn						? NULL : newstr(q);
27321330Sdavidn					break;
27420747Sdavidn				case _UC_DOTDIR:
27520747Sdavidn					config.dotdir = (q == NULL || !boolean_val(q, 1))
27620747Sdavidn						? NULL : newstr(q);
27720747Sdavidn					break;
27820747Sdavidn				case _UC_NEWMAIL:
27920747Sdavidn					config.newmail = (q == NULL || !boolean_val(q, 1))
28020747Sdavidn						? NULL : newstr(q);
28120747Sdavidn					break;
28220747Sdavidn				case _UC_LOGFILE:
28320747Sdavidn					config.logfile = (q == NULL || !boolean_val(q, 1))
28420747Sdavidn						? NULL : newstr(q);
28520747Sdavidn					break;
28620747Sdavidn				case _UC_HOMEROOT:
28720747Sdavidn					config.home = (q == NULL || !boolean_val(q, 1))
28820747Sdavidn						? "/home" : newstr(q);
28920747Sdavidn					break;
290168044Sle				case _UC_HOMEMODE:
291168044Sle					modeset = setmode(q);
292168044Sle					config.homemode = (q == NULL || !boolean_val(q, 1))
293219408Sjkim						? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
294168044Sle					free(modeset);
295168044Sle					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);
31344229Sdavidn					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		}
359264781Sbapt		if (linecap > 0)
360264781Sbapt			free(buf);
36120253Sjoerg		fclose(fp);
36220253Sjoerg	}
36320253Sjoerg	return &config;
36420253Sjoerg}
36520253Sjoerg
36620253Sjoerg
36720253Sjoergint
36820253Sjoergwrite_userconfig(char const * file)
36920253Sjoerg{
37020253Sjoerg	int             fd;
371282697Sbapt	int             i, j;
372282681Sbapt	struct sbuf	*buf;
373282697Sbapt	FILE           *fp;
37420253Sjoerg
37520253Sjoerg	if (file == NULL)
37620253Sjoerg		file = _PATH_PW_CONF;
37720253Sjoerg
378282697Sbapt	if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
379282697Sbapt		return (0);
38020253Sjoerg
381282697Sbapt	if ((fp = fdopen(fd, "w")) == NULL) {
382282697Sbapt		close(fd);
383282697Sbapt		return (0);
384282697Sbapt	}
385282681Sbapt
386282697Sbapt	buf = sbuf_new_auto();
387282697Sbapt	for (i = _UC_NONE; i < _UC_FIELDS; i++) {
388282697Sbapt		int             quote = 1;
38920253Sjoerg
390282697Sbapt		sbuf_clear(buf);
391282697Sbapt		switch (i) {
392282697Sbapt		case _UC_DEFAULTPWD:
393282697Sbapt			sbuf_cat(buf, boolean_str(config.default_password));
394282697Sbapt			break;
395282697Sbapt		case _UC_REUSEUID:
396282697Sbapt			sbuf_cat(buf, boolean_str(config.reuse_uids));
397282697Sbapt			break;
398282697Sbapt		case _UC_REUSEGID:
399282697Sbapt			sbuf_cat(buf, boolean_str(config.reuse_gids));
400282697Sbapt			break;
401282697Sbapt		case _UC_NISPASSWD:
402282697Sbapt			sbuf_cat(buf, config.nispasswd ?  config.nispasswd :
403282697Sbapt			    "");
404282697Sbapt			quote = 0;
405282697Sbapt			break;
406282697Sbapt		case _UC_DOTDIR:
407282697Sbapt			sbuf_cat(buf, config.dotdir ?  config.dotdir :
408282697Sbapt			    boolean_str(0));
409282697Sbapt			break;
410282697Sbapt		case _UC_NEWMAIL:
411282697Sbapt			sbuf_cat(buf, config.newmail ?  config.newmail :
412282697Sbapt			    boolean_str(0));
413282697Sbapt			break;
414282697Sbapt		case _UC_LOGFILE:
415282697Sbapt			sbuf_cat(buf, config.logfile ?  config.logfile :
416282697Sbapt			    boolean_str(0));
417282697Sbapt			break;
418282697Sbapt		case _UC_HOMEROOT:
419282697Sbapt			sbuf_cat(buf, config.home);
420282697Sbapt			break;
421282697Sbapt		case _UC_HOMEMODE:
422282697Sbapt			sbuf_printf(buf, "%04o", config.homemode);
423282697Sbapt			quote = 0;
424282697Sbapt			break;
425282697Sbapt		case _UC_SHELLPATH:
426282697Sbapt			sbuf_cat(buf, config.shelldir);
427282697Sbapt			break;
428282697Sbapt		case _UC_SHELLS:
429282697Sbapt			for (j = 0; j < _UC_MAXSHELLS &&
430282697Sbapt			    system_shells[j] != NULL; j++)
431282697Sbapt				sbuf_printf(buf, "%s\"%s\"", j ?
432282697Sbapt				    "," : "", system_shells[j]);
433282697Sbapt			quote = 0;
434282697Sbapt			break;
435282697Sbapt		case _UC_DEFAULTSHELL:
436282697Sbapt			sbuf_cat(buf, config.shell_default ?
437282697Sbapt			    config.shell_default : bourne_shell);
438282697Sbapt			break;
439282697Sbapt		case _UC_DEFAULTGROUP:
440282697Sbapt			sbuf_cat(buf, config.default_group ?
441282697Sbapt			    config.default_group : "");
442282697Sbapt			break;
443282697Sbapt		case _UC_EXTRAGROUPS:
444282697Sbapt			extendarray(&config.groups, &config.numgroups, 200);
445282697Sbapt			for (j = 0; j < config.numgroups &&
446282697Sbapt			    config.groups[j] != NULL; j++)
447282697Sbapt				sbuf_printf(buf, "%s\"%s\"", j ?
448282697Sbapt				    "," : "", config.groups[j]);
449282697Sbapt			quote = 0;
450282697Sbapt			break;
451282697Sbapt		case _UC_DEFAULTCLASS:
452282697Sbapt			sbuf_cat(buf, config.default_class ?
453282697Sbapt			    config.default_class : "");
454282697Sbapt			break;
455282697Sbapt		case _UC_MINUID:
456282697Sbapt			sbuf_printf(buf, "%lu", (unsigned long) config.min_uid);
457282697Sbapt			quote = 0;
458282697Sbapt			break;
459282697Sbapt		case _UC_MAXUID:
460282697Sbapt			sbuf_printf(buf, "%lu", (unsigned long) config.max_uid);
461282697Sbapt			quote = 0;
462282697Sbapt			break;
463282697Sbapt		case _UC_MINGID:
464282697Sbapt			sbuf_printf(buf, "%lu", (unsigned long) config.min_gid);
465282697Sbapt			quote = 0;
466282697Sbapt			break;
467282697Sbapt		case _UC_MAXGID:
468282697Sbapt			sbuf_printf(buf, "%lu", (unsigned long) config.max_gid);
469282697Sbapt			quote = 0;
470282697Sbapt			break;
471282697Sbapt		case _UC_EXPIRE:
472282697Sbapt			sbuf_printf(buf, "%d", config.expire_days);
473282697Sbapt			quote = 0;
474282697Sbapt			break;
475282697Sbapt		case _UC_PASSWORD:
476282697Sbapt			sbuf_printf(buf, "%d", config.password_days);
477282697Sbapt			quote = 0;
478282697Sbapt			break;
479282697Sbapt		case _UC_NONE:
480282697Sbapt			break;
481282697Sbapt		}
482282697Sbapt		sbuf_finish(buf);
48320253Sjoerg
484282697Sbapt		if (comments[i])
485282697Sbapt			fputs(comments[i], fp);
48620253Sjoerg
487282697Sbapt		if (*kwds[i]) {
488282697Sbapt			if (quote)
489282697Sbapt				fprintf(fp, "%s = \"%s\"\n", kwds[i],
490282697Sbapt				    sbuf_data(buf));
491282697Sbapt			else
492282697Sbapt				fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
49320253Sjoerg#if debugging
494282697Sbapt			printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
49520253Sjoerg#endif
49620253Sjoerg		}
49720253Sjoerg	}
498282697Sbapt	sbuf_delete(buf);
499282697Sbapt	return (fclose(fp) != EOF);
50020253Sjoerg}
501