pw_conf.c revision 282719
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 282719 2015-05-10 10:15:36Z 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>
37282718Sbapt#include <err.h>
3820253Sjoerg
3920253Sjoerg#include "pw.h"
4020253Sjoerg
4120253Sjoerg#define debugging 0
4220253Sjoerg
4320253Sjoergenum {
4420253Sjoerg	_UC_NONE,
4520253Sjoerg	_UC_DEFAULTPWD,
4620253Sjoerg	_UC_REUSEUID,
4720253Sjoerg	_UC_REUSEGID,
4821330Sdavidn	_UC_NISPASSWD,
4920253Sjoerg	_UC_DOTDIR,
5020253Sjoerg	_UC_NEWMAIL,
5120253Sjoerg	_UC_LOGFILE,
5220253Sjoerg	_UC_HOMEROOT,
53168044Sle	_UC_HOMEMODE,
5420253Sjoerg	_UC_SHELLPATH,
5520253Sjoerg	_UC_SHELLS,
5620253Sjoerg	_UC_DEFAULTSHELL,
5720253Sjoerg	_UC_DEFAULTGROUP,
5820253Sjoerg	_UC_EXTRAGROUPS,
5920253Sjoerg	_UC_DEFAULTCLASS,
6020253Sjoerg	_UC_MINUID,
6120253Sjoerg	_UC_MAXUID,
6220253Sjoerg	_UC_MINGID,
6320253Sjoerg	_UC_MAXGID,
6420253Sjoerg	_UC_EXPIRE,
6520253Sjoerg	_UC_PASSWORD,
6620253Sjoerg	_UC_FIELDS
6720253Sjoerg};
6820253Sjoerg
6920253Sjoergstatic char     bourne_shell[] = "sh";
7020253Sjoerg
7120253Sjoergstatic char    *system_shells[_UC_MAXSHELLS] =
7220253Sjoerg{
7320253Sjoerg	bourne_shell,
7463239Sdavidn	"csh",
7563239Sdavidn	"tcsh"
7620253Sjoerg};
7720253Sjoerg
7820253Sjoergstatic char const *booltrue[] =
7920253Sjoerg{
8020253Sjoerg	"yes", "true", "1", "on", NULL
8120253Sjoerg};
8220253Sjoergstatic char const *boolfalse[] =
8320253Sjoerg{
8420253Sjoerg	"no", "false", "0", "off", NULL
8520253Sjoerg};
8620253Sjoerg
8720253Sjoergstatic struct userconf config =
8820253Sjoerg{
8920253Sjoerg	0,			/* Default password for new users? (nologin) */
9020253Sjoerg	0,			/* Reuse uids? */
9120253Sjoerg	0,			/* Reuse gids? */
9221330Sdavidn	NULL,			/* NIS version of the passwd file */
9320253Sjoerg	"/usr/share/skel",	/* Where to obtain skeleton files */
9420253Sjoerg	NULL,			/* Mail to send to new accounts */
9520253Sjoerg	"/var/log/userlog",	/* Where to log changes */
9620253Sjoerg	"/home",		/* Where to create home directory */
97219408Sjkim	_DEF_DIRMODE,		/* Home directory perms, modified by umask */
9820253Sjoerg	"/bin",			/* Where shells are located */
9920253Sjoerg	system_shells,		/* List of shells (first is default) */
10020253Sjoerg	bourne_shell,		/* Default shell */
10120253Sjoerg	NULL,			/* Default group name */
10220747Sdavidn	NULL,			/* Default (additional) groups */
10320253Sjoerg	NULL,			/* Default login class */
10420253Sjoerg	1000, 32000,		/* Allowed range of uids */
10520253Sjoerg	1000, 32000,		/* Allowed range of gids */
10620253Sjoerg	0,			/* Days until account expires */
10749171Sdavidn	0,			/* Days until password expires */
10849171Sdavidn	0			/* size of default_group array */
10920253Sjoerg};
11020253Sjoerg
11120253Sjoergstatic char const *comments[_UC_FIELDS] =
11220253Sjoerg{
11320253Sjoerg	"#\n# pw.conf - user/group configuration defaults\n#\n",
11420253Sjoerg	"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
11520253Sjoerg	"\n# Reuse gaps in uid sequence? (yes or no)\n",
11620253Sjoerg	"\n# Reuse gaps in gid sequence? (yes or no)\n",
11721330Sdavidn	"\n# Path to the NIS passwd file (blank or 'no' for none)\n",
11820253Sjoerg	"\n# Obtain default dotfiles from this directory\n",
11920253Sjoerg	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
12020253Sjoerg	"\n# Log add/change/remove information in this file\n",
12120253Sjoerg	"\n# Root directory in which $HOME directory is created\n",
122168044Sle	"\n# Mode for the new $HOME directory, will be modified by umask\n",
12320253Sjoerg	"\n# Colon separated list of directories containing valid shells\n",
12470133Sdougb	"\n# Comma separated list of available shells (without paths)\n",
12520253Sjoerg	"\n# Default shell (without path)\n",
12620253Sjoerg	"\n# Default group (leave blank for new group per user)\n",
12720253Sjoerg	"\n# Extra groups for new users\n",
12820253Sjoerg	"\n# Default login class for new users\n",
12920253Sjoerg	"\n# Range of valid default user ids\n",
13020253Sjoerg	NULL,
13120253Sjoerg	"\n# Range of valid default group ids\n",
13220253Sjoerg	NULL,
13320253Sjoerg	"\n# Days after which account expires (0=disabled)\n",
13420253Sjoerg	"\n# Days after which password expires (0=disabled)\n"
13520253Sjoerg};
13620253Sjoerg
13720253Sjoergstatic char const *kwds[] =
13820253Sjoerg{
13920253Sjoerg	"",
14020253Sjoerg	"defaultpasswd",
14120253Sjoerg	"reuseuids",
14220253Sjoerg	"reusegids",
14321330Sdavidn	"nispasswd",
14420253Sjoerg	"skeleton",
14520253Sjoerg	"newmail",
14620253Sjoerg	"logfile",
14720253Sjoerg	"home",
148168044Sle	"homemode",
14920253Sjoerg	"shellpath",
15020253Sjoerg	"shells",
15120253Sjoerg	"defaultshell",
15220253Sjoerg	"defaultgroup",
15320253Sjoerg	"extragroups",
15420253Sjoerg	"defaultclass",
15520253Sjoerg	"minuid",
15620253Sjoerg	"maxuid",
15720253Sjoerg	"mingid",
15820253Sjoerg	"maxgid",
15920253Sjoerg	"expire_days",
16020253Sjoerg	"password_days",
16120253Sjoerg	NULL
16220253Sjoerg};
16320253Sjoerg
16420253Sjoergstatic char    *
16520253Sjoergunquote(char const * str)
16620253Sjoerg{
16720253Sjoerg	if (str && (*str == '"' || *str == '\'')) {
16820253Sjoerg		char           *p = strchr(str + 1, *str);
16920253Sjoerg
17020253Sjoerg		if (p != NULL)
17120253Sjoerg			*p = '\0';
17220253Sjoerg		return (char *) (*++str ? str : NULL);
17320253Sjoerg	}
17420253Sjoerg	return (char *) str;
17520253Sjoerg}
17620253Sjoerg
17720253Sjoergint
17820253Sjoergboolean_val(char const * str, int dflt)
17920253Sjoerg{
18020253Sjoerg	if ((str = unquote(str)) != NULL) {
18120253Sjoerg		int             i;
18220253Sjoerg
18320253Sjoerg		for (i = 0; booltrue[i]; i++)
18420253Sjoerg			if (strcmp(str, booltrue[i]) == 0)
18520253Sjoerg				return 1;
18620253Sjoerg		for (i = 0; boolfalse[i]; i++)
18720253Sjoerg			if (strcmp(str, boolfalse[i]) == 0)
18820253Sjoerg				return 0;
18920253Sjoerg
19020253Sjoerg		/*
19120253Sjoerg		 * Special cases for defaultpassword
19220253Sjoerg		 */
19320253Sjoerg		if (strcmp(str, "random") == 0)
19420253Sjoerg			return -1;
19520253Sjoerg		if (strcmp(str, "none") == 0)
19620253Sjoerg			return -2;
19720253Sjoerg	}
19820253Sjoerg	return dflt;
19920253Sjoerg}
20020253Sjoerg
20120253Sjoergchar const     *
20220253Sjoergboolean_str(int val)
20320253Sjoerg{
20420253Sjoerg	if (val == -1)
20520253Sjoerg		return "random";
20620253Sjoerg	else if (val == -2)
20720253Sjoerg		return "none";
20820253Sjoerg	else
20920253Sjoerg		return val ? booltrue[0] : boolfalse[0];
21020253Sjoerg}
21120253Sjoerg
21220253Sjoergchar           *
21320253Sjoergnewstr(char const * p)
21420253Sjoerg{
215282718Sbapt	char	*q;
21620253Sjoerg
217282718Sbapt	if ((p = unquote(p)) == NULL)
218282718Sbapt		return (NULL);
21920253Sjoerg
220282719Sbapt	if ((q = strdup(p)) == NULL)
221282719Sbapt		err(1, "strdup()");
222282718Sbapt
223282718Sbapt	return (q);
22420253Sjoerg}
22520253Sjoerg
22620253Sjoergstruct userconf *
22720253Sjoergread_userconfig(char const * file)
22820253Sjoerg{
229264781Sbapt	FILE	*fp;
230264781Sbapt	char	*buf, *p;
231264781Sbapt	size_t	linecap;
232264781Sbapt	ssize_t	linelen;
23320253Sjoerg
234264781Sbapt	buf = NULL;
235264781Sbapt	linecap = 0;
236264781Sbapt
23720747Sdavidn	extendarray(&config.groups, &config.numgroups, 200);
23820747Sdavidn	memset(config.groups, 0, config.numgroups * sizeof(char *));
23920253Sjoerg	if (file == NULL)
24020253Sjoerg		file = _PATH_PW_CONF;
241264781Sbapt
24220253Sjoerg	if ((fp = fopen(file, "r")) != NULL) {
243264781Sbapt		while ((linelen = getline(&buf, &linecap, fp)) > 0) {
24420747Sdavidn			if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
24520747Sdavidn				static char const toks[] = " \t\r\n,=";
24620747Sdavidn				char           *q = strtok(NULL, toks);
24720747Sdavidn				int             i = 0;
248168044Sle				mode_t          *modeset;
24920747Sdavidn
25020747Sdavidn				while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
25120747Sdavidn					++i;
25220253Sjoerg#if debugging
25320747Sdavidn				if (i == _UC_FIELDS)
25420747Sdavidn					printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
25520747Sdavidn				else
25620747Sdavidn					printf("Got kwd[%s]=%s\n", p, q);
25720253Sjoerg#endif
25820747Sdavidn				switch (i) {
25920747Sdavidn				case _UC_DEFAULTPWD:
26020747Sdavidn					config.default_password = boolean_val(q, 1);
26120747Sdavidn					break;
26220747Sdavidn				case _UC_REUSEUID:
26320747Sdavidn					config.reuse_uids = boolean_val(q, 0);
26420747Sdavidn					break;
26520747Sdavidn				case _UC_REUSEGID:
26620747Sdavidn					config.reuse_gids = boolean_val(q, 0);
26720747Sdavidn					break;
26821330Sdavidn				case _UC_NISPASSWD:
26921330Sdavidn					config.nispasswd = (q == NULL || !boolean_val(q, 1))
27021330Sdavidn						? NULL : newstr(q);
27121330Sdavidn					break;
27220747Sdavidn				case _UC_DOTDIR:
27320747Sdavidn					config.dotdir = (q == NULL || !boolean_val(q, 1))
27420747Sdavidn						? NULL : newstr(q);
27520747Sdavidn					break;
27620747Sdavidn				case _UC_NEWMAIL:
27720747Sdavidn					config.newmail = (q == NULL || !boolean_val(q, 1))
27820747Sdavidn						? NULL : newstr(q);
27920747Sdavidn					break;
28020747Sdavidn				case _UC_LOGFILE:
28120747Sdavidn					config.logfile = (q == NULL || !boolean_val(q, 1))
28220747Sdavidn						? NULL : newstr(q);
28320747Sdavidn					break;
28420747Sdavidn				case _UC_HOMEROOT:
28520747Sdavidn					config.home = (q == NULL || !boolean_val(q, 1))
28620747Sdavidn						? "/home" : newstr(q);
28720747Sdavidn					break;
288168044Sle				case _UC_HOMEMODE:
289168044Sle					modeset = setmode(q);
290168044Sle					config.homemode = (q == NULL || !boolean_val(q, 1))
291219408Sjkim						? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
292168044Sle					free(modeset);
293168044Sle					break;
29420747Sdavidn				case _UC_SHELLPATH:
29520747Sdavidn					config.shelldir = (q == NULL || !boolean_val(q, 1))
29620747Sdavidn						? "/bin" : newstr(q);
29720747Sdavidn					break;
29820747Sdavidn				case _UC_SHELLS:
29920747Sdavidn					for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
30020747Sdavidn						system_shells[i] = newstr(q);
30120747Sdavidn					if (i > 0)
30220747Sdavidn						while (i < _UC_MAXSHELLS)
30320747Sdavidn							system_shells[i++] = NULL;
30420747Sdavidn					break;
30520747Sdavidn				case _UC_DEFAULTSHELL:
30620747Sdavidn					config.shell_default = (q == NULL || !boolean_val(q, 1))
30720747Sdavidn						? (char *) bourne_shell : newstr(q);
30820747Sdavidn					break;
30920747Sdavidn				case _UC_DEFAULTGROUP:
31029002Sdavidn					q = unquote(q);
31144229Sdavidn					config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
31220747Sdavidn						? NULL : newstr(q);
31320747Sdavidn					break;
31420747Sdavidn				case _UC_EXTRAGROUPS:
31520747Sdavidn					for (i = 0; q != NULL; q = strtok(NULL, toks)) {
31620747Sdavidn						if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
31720747Sdavidn							config.groups[i++] = newstr(q);
31820253Sjoerg					}
31920747Sdavidn					if (i > 0)
32020747Sdavidn						while (i < config.numgroups)
32120747Sdavidn							config.groups[i++] = NULL;
32220747Sdavidn					break;
32320747Sdavidn				case _UC_DEFAULTCLASS:
32420747Sdavidn					config.default_class = (q == NULL || !boolean_val(q, 1))
32520747Sdavidn						? NULL : newstr(q);
32620747Sdavidn					break;
32720747Sdavidn				case _UC_MINUID:
32820747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
32920747Sdavidn						config.min_uid = (uid_t) atol(q);
33020747Sdavidn					break;
33120747Sdavidn				case _UC_MAXUID:
33220747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
33320747Sdavidn						config.max_uid = (uid_t) atol(q);
33420747Sdavidn					break;
33520747Sdavidn				case _UC_MINGID:
33620747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
33720747Sdavidn						config.min_gid = (gid_t) atol(q);
33820747Sdavidn					break;
33920747Sdavidn				case _UC_MAXGID:
34020747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
34120747Sdavidn						config.max_gid = (gid_t) atol(q);
34220747Sdavidn					break;
34320747Sdavidn				case _UC_EXPIRE:
34420747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
34520747Sdavidn						config.expire_days = atoi(q);
34620747Sdavidn					break;
34720747Sdavidn				case _UC_PASSWORD:
34820747Sdavidn					if ((q = unquote(q)) != NULL && isdigit(*q))
34920747Sdavidn						config.password_days = atoi(q);
35020747Sdavidn					break;
35120747Sdavidn				case _UC_FIELDS:
35220747Sdavidn				case _UC_NONE:
35320747Sdavidn					break;
35420253Sjoerg				}
35520253Sjoerg			}
35620253Sjoerg		}
357264781Sbapt		if (linecap > 0)
358264781Sbapt			free(buf);
35920253Sjoerg		fclose(fp);
36020253Sjoerg	}
36120253Sjoerg	return &config;
36220253Sjoerg}
36320253Sjoerg
36420253Sjoerg
36520253Sjoergint
36620253Sjoergwrite_userconfig(char const * file)
36720253Sjoerg{
36820253Sjoerg	int             fd;
369282697Sbapt	int             i, j;
370282681Sbapt	struct sbuf	*buf;
371282697Sbapt	FILE           *fp;
37220253Sjoerg
37320253Sjoerg	if (file == NULL)
37420253Sjoerg		file = _PATH_PW_CONF;
37520253Sjoerg
376282697Sbapt	if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
377282697Sbapt		return (0);
37820253Sjoerg
379282697Sbapt	if ((fp = fdopen(fd, "w")) == NULL) {
380282697Sbapt		close(fd);
381282697Sbapt		return (0);
382282697Sbapt	}
383282681Sbapt
384282697Sbapt	buf = sbuf_new_auto();
385282697Sbapt	for (i = _UC_NONE; i < _UC_FIELDS; i++) {
386282697Sbapt		int             quote = 1;
38720253Sjoerg
388282697Sbapt		sbuf_clear(buf);
389282697Sbapt		switch (i) {
390282697Sbapt		case _UC_DEFAULTPWD:
391282697Sbapt			sbuf_cat(buf, boolean_str(config.default_password));
392282697Sbapt			break;
393282697Sbapt		case _UC_REUSEUID:
394282697Sbapt			sbuf_cat(buf, boolean_str(config.reuse_uids));
395282697Sbapt			break;
396282697Sbapt		case _UC_REUSEGID:
397282697Sbapt			sbuf_cat(buf, boolean_str(config.reuse_gids));
398282697Sbapt			break;
399282697Sbapt		case _UC_NISPASSWD:
400282697Sbapt			sbuf_cat(buf, config.nispasswd ?  config.nispasswd :
401282697Sbapt			    "");
402282697Sbapt			quote = 0;
403282697Sbapt			break;
404282697Sbapt		case _UC_DOTDIR:
405282697Sbapt			sbuf_cat(buf, config.dotdir ?  config.dotdir :
406282697Sbapt			    boolean_str(0));
407282697Sbapt			break;
408282697Sbapt		case _UC_NEWMAIL:
409282697Sbapt			sbuf_cat(buf, config.newmail ?  config.newmail :
410282697Sbapt			    boolean_str(0));
411282697Sbapt			break;
412282697Sbapt		case _UC_LOGFILE:
413282697Sbapt			sbuf_cat(buf, config.logfile ?  config.logfile :
414282697Sbapt			    boolean_str(0));
415282697Sbapt			break;
416282697Sbapt		case _UC_HOMEROOT:
417282697Sbapt			sbuf_cat(buf, config.home);
418282697Sbapt			break;
419282697Sbapt		case _UC_HOMEMODE:
420282697Sbapt			sbuf_printf(buf, "%04o", config.homemode);
421282697Sbapt			quote = 0;
422282697Sbapt			break;
423282697Sbapt		case _UC_SHELLPATH:
424282697Sbapt			sbuf_cat(buf, config.shelldir);
425282697Sbapt			break;
426282697Sbapt		case _UC_SHELLS:
427282697Sbapt			for (j = 0; j < _UC_MAXSHELLS &&
428282697Sbapt			    system_shells[j] != NULL; j++)
429282697Sbapt				sbuf_printf(buf, "%s\"%s\"", j ?
430282697Sbapt				    "," : "", system_shells[j]);
431282697Sbapt			quote = 0;
432282697Sbapt			break;
433282697Sbapt		case _UC_DEFAULTSHELL:
434282697Sbapt			sbuf_cat(buf, config.shell_default ?
435282697Sbapt			    config.shell_default : bourne_shell);
436282697Sbapt			break;
437282697Sbapt		case _UC_DEFAULTGROUP:
438282697Sbapt			sbuf_cat(buf, config.default_group ?
439282697Sbapt			    config.default_group : "");
440282697Sbapt			break;
441282697Sbapt		case _UC_EXTRAGROUPS:
442282697Sbapt			for (j = 0; j < config.numgroups &&
443282697Sbapt			    config.groups[j] != NULL; j++)
444282697Sbapt				sbuf_printf(buf, "%s\"%s\"", j ?
445282697Sbapt				    "," : "", config.groups[j]);
446282697Sbapt			quote = 0;
447282697Sbapt			break;
448282697Sbapt		case _UC_DEFAULTCLASS:
449282697Sbapt			sbuf_cat(buf, config.default_class ?
450282697Sbapt			    config.default_class : "");
451282697Sbapt			break;
452282697Sbapt		case _UC_MINUID:
453282697Sbapt			sbuf_printf(buf, "%lu", (unsigned long) config.min_uid);
454282697Sbapt			quote = 0;
455282697Sbapt			break;
456282697Sbapt		case _UC_MAXUID:
457282697Sbapt			sbuf_printf(buf, "%lu", (unsigned long) config.max_uid);
458282697Sbapt			quote = 0;
459282697Sbapt			break;
460282697Sbapt		case _UC_MINGID:
461282697Sbapt			sbuf_printf(buf, "%lu", (unsigned long) config.min_gid);
462282697Sbapt			quote = 0;
463282697Sbapt			break;
464282697Sbapt		case _UC_MAXGID:
465282697Sbapt			sbuf_printf(buf, "%lu", (unsigned long) config.max_gid);
466282697Sbapt			quote = 0;
467282697Sbapt			break;
468282697Sbapt		case _UC_EXPIRE:
469282697Sbapt			sbuf_printf(buf, "%d", config.expire_days);
470282697Sbapt			quote = 0;
471282697Sbapt			break;
472282697Sbapt		case _UC_PASSWORD:
473282697Sbapt			sbuf_printf(buf, "%d", config.password_days);
474282697Sbapt			quote = 0;
475282697Sbapt			break;
476282697Sbapt		case _UC_NONE:
477282697Sbapt			break;
478282697Sbapt		}
479282697Sbapt		sbuf_finish(buf);
48020253Sjoerg
481282697Sbapt		if (comments[i])
482282697Sbapt			fputs(comments[i], fp);
48320253Sjoerg
484282697Sbapt		if (*kwds[i]) {
485282697Sbapt			if (quote)
486282697Sbapt				fprintf(fp, "%s = \"%s\"\n", kwds[i],
487282697Sbapt				    sbuf_data(buf));
488282697Sbapt			else
489282697Sbapt				fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
49020253Sjoerg#if debugging
491282697Sbapt			printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
49220253Sjoerg#endif
49320253Sjoerg		}
49420253Sjoerg	}
495282697Sbapt	sbuf_delete(buf);
496282697Sbapt	return (fclose(fp) != EOF);
49720253Sjoerg}
498