pw_conf.c revision 286196
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 286196 2015-08-02 12:47:50Z bapt $";
3030259Scharnier#endif /* not lint */
3130259Scharnier
32282681Sbapt#include <sys/types.h>
33282681Sbapt#include <sys/sbuf.h>
34286150Sbapt#include <inttypes.h>
3520253Sjoerg#include <string.h>
3620253Sjoerg#include <ctype.h>
3720253Sjoerg#include <fcntl.h>
38282718Sbapt#include <err.h>
3920253Sjoerg
4020253Sjoerg#include "pw.h"
4120253Sjoerg
4220253Sjoerg#define debugging 0
4320253Sjoerg
4420253Sjoergenum {
4520253Sjoerg	_UC_NONE,
4620253Sjoerg	_UC_DEFAULTPWD,
4720253Sjoerg	_UC_REUSEUID,
4820253Sjoerg	_UC_REUSEGID,
4921330Sdavidn	_UC_NISPASSWD,
5020253Sjoerg	_UC_DOTDIR,
5120253Sjoerg	_UC_NEWMAIL,
5220253Sjoerg	_UC_LOGFILE,
5320253Sjoerg	_UC_HOMEROOT,
54168044Sle	_UC_HOMEMODE,
5520253Sjoerg	_UC_SHELLPATH,
5620253Sjoerg	_UC_SHELLS,
5720253Sjoerg	_UC_DEFAULTSHELL,
5820253Sjoerg	_UC_DEFAULTGROUP,
5920253Sjoerg	_UC_EXTRAGROUPS,
6020253Sjoerg	_UC_DEFAULTCLASS,
6120253Sjoerg	_UC_MINUID,
6220253Sjoerg	_UC_MAXUID,
6320253Sjoerg	_UC_MINGID,
6420253Sjoerg	_UC_MAXGID,
6520253Sjoerg	_UC_EXPIRE,
6620253Sjoerg	_UC_PASSWORD,
6720253Sjoerg	_UC_FIELDS
6820253Sjoerg};
6920253Sjoerg
7020253Sjoergstatic char     bourne_shell[] = "sh";
7120253Sjoerg
7220253Sjoergstatic char    *system_shells[_UC_MAXSHELLS] =
7320253Sjoerg{
7420253Sjoerg	bourne_shell,
7563239Sdavidn	"csh",
7663239Sdavidn	"tcsh"
7720253Sjoerg};
7820253Sjoerg
7920253Sjoergstatic char const *booltrue[] =
8020253Sjoerg{
8120253Sjoerg	"yes", "true", "1", "on", NULL
8220253Sjoerg};
8320253Sjoergstatic char const *boolfalse[] =
8420253Sjoerg{
8520253Sjoerg	"no", "false", "0", "off", NULL
8620253Sjoerg};
8720253Sjoerg
8820253Sjoergstatic struct userconf config =
8920253Sjoerg{
9020253Sjoerg	0,			/* Default password for new users? (nologin) */
9120253Sjoerg	0,			/* Reuse uids? */
9220253Sjoerg	0,			/* Reuse gids? */
9321330Sdavidn	NULL,			/* NIS version of the passwd file */
9420253Sjoerg	"/usr/share/skel",	/* Where to obtain skeleton files */
9520253Sjoerg	NULL,			/* Mail to send to new accounts */
9620253Sjoerg	"/var/log/userlog",	/* Where to log changes */
9720253Sjoerg	"/home",		/* Where to create home directory */
98219408Sjkim	_DEF_DIRMODE,		/* Home directory perms, modified by umask */
9920253Sjoerg	"/bin",			/* Where shells are located */
10020253Sjoerg	system_shells,		/* List of shells (first is default) */
10120253Sjoerg	bourne_shell,		/* Default shell */
10220253Sjoerg	NULL,			/* Default group name */
10320747Sdavidn	NULL,			/* Default (additional) groups */
10420253Sjoerg	NULL,			/* Default login class */
10520253Sjoerg	1000, 32000,		/* Allowed range of uids */
10620253Sjoerg	1000, 32000,		/* Allowed range of gids */
10720253Sjoerg	0,			/* Days until account expires */
108285412Sbapt	0			/* Days until password expires */
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;
231286154Sbapt	const char *errstr;
232264781Sbapt	size_t	linecap;
233264781Sbapt	ssize_t	linelen;
23420253Sjoerg
235264781Sbapt	buf = NULL;
236264781Sbapt	linecap = 0;
237264781Sbapt
23820253Sjoerg	if (file == NULL)
23920253Sjoerg		file = _PATH_PW_CONF;
240264781Sbapt
241283815Sbapt	if ((fp = fopen(file, "r")) == NULL)
242283815Sbapt		return (&config);
24320747Sdavidn
244283815Sbapt	while ((linelen = getline(&buf, &linecap, fp)) > 0) {
245283815Sbapt		if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
246283815Sbapt			static char const toks[] = " \t\r\n,=";
247283815Sbapt			char           *q = strtok(NULL, toks);
248283815Sbapt			int             i = 0;
249283815Sbapt			mode_t          *modeset;
250283815Sbapt
251283815Sbapt			while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
252283815Sbapt				++i;
25320253Sjoerg#if debugging
254283815Sbapt			if (i == _UC_FIELDS)
255283815Sbapt				printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
256283815Sbapt			else
257283815Sbapt				printf("Got kwd[%s]=%s\n", p, q);
25820253Sjoerg#endif
259283815Sbapt			switch (i) {
260283815Sbapt			case _UC_DEFAULTPWD:
261283815Sbapt				config.default_password = boolean_val(q, 1);
262283815Sbapt				break;
263283815Sbapt			case _UC_REUSEUID:
264283815Sbapt				config.reuse_uids = boolean_val(q, 0);
265283815Sbapt				break;
266283815Sbapt			case _UC_REUSEGID:
267283815Sbapt				config.reuse_gids = boolean_val(q, 0);
268283815Sbapt				break;
269283815Sbapt			case _UC_NISPASSWD:
270283815Sbapt				config.nispasswd = (q == NULL || !boolean_val(q, 1))
271283815Sbapt					? NULL : newstr(q);
272283815Sbapt				break;
273283815Sbapt			case _UC_DOTDIR:
274283815Sbapt				config.dotdir = (q == NULL || !boolean_val(q, 1))
275283815Sbapt					? NULL : newstr(q);
276283815Sbapt				break;
27720747Sdavidn				case _UC_NEWMAIL:
278283815Sbapt				config.newmail = (q == NULL || !boolean_val(q, 1))
279283815Sbapt					? NULL : newstr(q);
280283815Sbapt				break;
281283815Sbapt			case _UC_LOGFILE:
282283815Sbapt				config.logfile = (q == NULL || !boolean_val(q, 1))
283283815Sbapt					? NULL : newstr(q);
284283815Sbapt				break;
285283815Sbapt			case _UC_HOMEROOT:
286283815Sbapt				config.home = (q == NULL || !boolean_val(q, 1))
287283815Sbapt					? "/home" : newstr(q);
288283815Sbapt				break;
289283815Sbapt			case _UC_HOMEMODE:
290283815Sbapt				modeset = setmode(q);
291283815Sbapt				config.homemode = (q == NULL || !boolean_val(q, 1))
292283815Sbapt					? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
293283815Sbapt				free(modeset);
294283815Sbapt				break;
295283815Sbapt			case _UC_SHELLPATH:
296283815Sbapt				config.shelldir = (q == NULL || !boolean_val(q, 1))
297283815Sbapt					? "/bin" : newstr(q);
298283815Sbapt				break;
299283815Sbapt			case _UC_SHELLS:
300283815Sbapt				for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
301283815Sbapt					system_shells[i] = newstr(q);
302283815Sbapt				if (i > 0)
303283815Sbapt					while (i < _UC_MAXSHELLS)
304283815Sbapt						system_shells[i++] = NULL;
305283815Sbapt				break;
306283815Sbapt			case _UC_DEFAULTSHELL:
307283815Sbapt				config.shell_default = (q == NULL || !boolean_val(q, 1))
308283815Sbapt					? (char *) bourne_shell : newstr(q);
309283815Sbapt				break;
310283815Sbapt			case _UC_DEFAULTGROUP:
311283815Sbapt				q = unquote(q);
312283815Sbapt				config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
313283815Sbapt					? NULL : newstr(q);
314283815Sbapt				break;
315283815Sbapt			case _UC_EXTRAGROUPS:
316286196Sbapt				for (i = 0; q != NULL; q = strtok(NULL, toks)) {
317286196Sbapt					if (config.groups == NULL)
318286196Sbapt						config.groups = sl_init();
319285412Sbapt					sl_add(config.groups, newstr(q));
320286196Sbapt				}
321283815Sbapt				break;
322283815Sbapt			case _UC_DEFAULTCLASS:
323283815Sbapt				config.default_class = (q == NULL || !boolean_val(q, 1))
324283815Sbapt					? NULL : newstr(q);
325283815Sbapt				break;
326283815Sbapt			case _UC_MINUID:
327286151Sbapt				if ((q = unquote(q)) != NULL) {
328286154Sbapt					config.min_uid = strtounum(q, 0,
329286154Sbapt					    UID_MAX, &errstr);
330286151Sbapt					if (errstr)
331286154Sbapt						warnx("Invalid min_uid: '%s';"
332286154Sbapt						    " ignoring", q);
333286151Sbapt				}
334283815Sbapt				break;
335283815Sbapt			case _UC_MAXUID:
336286151Sbapt				if ((q = unquote(q)) != NULL) {
337286154Sbapt					config.max_uid = strtounum(q, 0,
338286154Sbapt					    UID_MAX, &errstr);
339286151Sbapt					if (errstr)
340286154Sbapt						warnx("Invalid max_uid: '%s';"
341286154Sbapt						    " ignoring", q);
342286151Sbapt				}
343283815Sbapt				break;
344283815Sbapt			case _UC_MINGID:
345286154Sbapt				if ((q = unquote(q)) != NULL) {
346286154Sbapt					config.min_gid = strtounum(q, 0,
347286154Sbapt					    GID_MAX, &errstr);
348286151Sbapt					if (errstr)
349286154Sbapt						warnx("Invalid min_gid: '%s';"
350286154Sbapt						    " ignoring", q);
351286155Sbapt				}
352283815Sbapt				break;
353283815Sbapt			case _UC_MAXGID:
354286151Sbapt				if ((q = unquote(q)) != NULL) {
355286154Sbapt					config.max_gid = strtounum(q, 0,
356286154Sbapt					    GID_MAX, &errstr);
357286151Sbapt					if (errstr)
358286154Sbapt						warnx("Invalid max_gid: '%s';"
359286154Sbapt						    " ignoring", q);
360286151Sbapt				}
361283815Sbapt				break;
362283815Sbapt			case _UC_EXPIRE:
363286152Sbapt				if ((q = unquote(q)) != NULL) {
364286154Sbapt					config.expire_days = strtonum(q, 0,
365286154Sbapt					    INT_MAX, &errstr);
366286152Sbapt					if (errstr)
367286154Sbapt						warnx("Invalid expire days:"
368286154Sbapt						    " '%s'; ignoring", q);
369286152Sbapt				}
370283815Sbapt				break;
371283815Sbapt			case _UC_PASSWORD:
372286152Sbapt				if ((q = unquote(q)) != NULL) {
373286154Sbapt					config.password_days = strtonum(q, 0,
374286154Sbapt					    INT_MAX, &errstr);
375286152Sbapt					if (errstr)
376286154Sbapt						warnx("Invalid password days:"
377286154Sbapt						    " '%s'; ignoring", q);
378286152Sbapt				}
379283815Sbapt				break;
380283815Sbapt			case _UC_FIELDS:
381283815Sbapt			case _UC_NONE:
382283815Sbapt				break;
38320253Sjoerg			}
38420253Sjoerg		}
38520253Sjoerg	}
386283818Sbapt	free(buf);
387283818Sbapt	fclose(fp);
388283818Sbapt
389283815Sbapt	return (&config);
39020253Sjoerg}
39120253Sjoerg
39220253Sjoerg
39320253Sjoergint
394286196Sbaptwrite_userconfig(struct userconf *cnf, const char *file)
39520253Sjoerg{
39620253Sjoerg	int             fd;
397282697Sbapt	int             i, j;
398282681Sbapt	struct sbuf	*buf;
399282697Sbapt	FILE           *fp;
40020253Sjoerg
40120253Sjoerg	if (file == NULL)
40220253Sjoerg		file = _PATH_PW_CONF;
40320253Sjoerg
404282697Sbapt	if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
405282697Sbapt		return (0);
40620253Sjoerg
407282697Sbapt	if ((fp = fdopen(fd, "w")) == NULL) {
408282697Sbapt		close(fd);
409282697Sbapt		return (0);
410282697Sbapt	}
411282681Sbapt
412282697Sbapt	buf = sbuf_new_auto();
413282697Sbapt	for (i = _UC_NONE; i < _UC_FIELDS; i++) {
414282697Sbapt		int             quote = 1;
41520253Sjoerg
416282697Sbapt		sbuf_clear(buf);
417282697Sbapt		switch (i) {
418282697Sbapt		case _UC_DEFAULTPWD:
419286196Sbapt			sbuf_cat(buf, boolean_str(cnf->default_password));
420282697Sbapt			break;
421282697Sbapt		case _UC_REUSEUID:
422286196Sbapt			sbuf_cat(buf, boolean_str(cnf->reuse_uids));
423282697Sbapt			break;
424282697Sbapt		case _UC_REUSEGID:
425286196Sbapt			sbuf_cat(buf, boolean_str(cnf->reuse_gids));
426282697Sbapt			break;
427282697Sbapt		case _UC_NISPASSWD:
428286196Sbapt			sbuf_cat(buf, cnf->nispasswd ?  cnf->nispasswd : "");
429282697Sbapt			quote = 0;
430282697Sbapt			break;
431282697Sbapt		case _UC_DOTDIR:
432286196Sbapt			sbuf_cat(buf, cnf->dotdir ?  cnf->dotdir :
433282697Sbapt			    boolean_str(0));
434282697Sbapt			break;
435282697Sbapt		case _UC_NEWMAIL:
436286196Sbapt			sbuf_cat(buf, cnf->newmail ?  cnf->newmail :
437282697Sbapt			    boolean_str(0));
438282697Sbapt			break;
439282697Sbapt		case _UC_LOGFILE:
440286196Sbapt			sbuf_cat(buf, cnf->logfile ?  cnf->logfile :
441282697Sbapt			    boolean_str(0));
442282697Sbapt			break;
443282697Sbapt		case _UC_HOMEROOT:
444286196Sbapt			sbuf_cat(buf, cnf->home);
445282697Sbapt			break;
446282697Sbapt		case _UC_HOMEMODE:
447286196Sbapt			sbuf_printf(buf, "%04o", cnf->homemode);
448282697Sbapt			quote = 0;
449282697Sbapt			break;
450282697Sbapt		case _UC_SHELLPATH:
451286196Sbapt			sbuf_cat(buf, cnf->shelldir);
452282697Sbapt			break;
453282697Sbapt		case _UC_SHELLS:
454282697Sbapt			for (j = 0; j < _UC_MAXSHELLS &&
455282697Sbapt			    system_shells[j] != NULL; j++)
456282697Sbapt				sbuf_printf(buf, "%s\"%s\"", j ?
457282697Sbapt				    "," : "", system_shells[j]);
458282697Sbapt			quote = 0;
459282697Sbapt			break;
460282697Sbapt		case _UC_DEFAULTSHELL:
461286196Sbapt			sbuf_cat(buf, cnf->shell_default ?
462286196Sbapt			    cnf->shell_default : bourne_shell);
463282697Sbapt			break;
464282697Sbapt		case _UC_DEFAULTGROUP:
465286196Sbapt			sbuf_cat(buf, cnf->default_group ?
466286196Sbapt			    cnf->default_group : "");
467282697Sbapt			break;
468282697Sbapt		case _UC_EXTRAGROUPS:
469286196Sbapt			for (j = 0; cnf->groups != NULL &&
470286196Sbapt			    j < (int)cnf->groups->sl_cur; j++)
471282697Sbapt				sbuf_printf(buf, "%s\"%s\"", j ?
472286196Sbapt				    "," : "", cnf->groups->sl_str[j]);
473282697Sbapt			quote = 0;
474282697Sbapt			break;
475282697Sbapt		case _UC_DEFAULTCLASS:
476286196Sbapt			sbuf_cat(buf, cnf->default_class ?
477286196Sbapt			    cnf->default_class : "");
478282697Sbapt			break;
479282697Sbapt		case _UC_MINUID:
480286196Sbapt			sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_uid);
481282697Sbapt			quote = 0;
482282697Sbapt			break;
483282697Sbapt		case _UC_MAXUID:
484286196Sbapt			sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_uid);
485282697Sbapt			quote = 0;
486282697Sbapt			break;
487282697Sbapt		case _UC_MINGID:
488286196Sbapt			sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_gid);
489282697Sbapt			quote = 0;
490282697Sbapt			break;
491282697Sbapt		case _UC_MAXGID:
492286196Sbapt			sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_gid);
493282697Sbapt			quote = 0;
494282697Sbapt			break;
495282697Sbapt		case _UC_EXPIRE:
496286196Sbapt			sbuf_printf(buf, "%ld", cnf->expire_days);
497282697Sbapt			quote = 0;
498282697Sbapt			break;
499282697Sbapt		case _UC_PASSWORD:
500286196Sbapt			sbuf_printf(buf, "%ld", cnf->password_days);
501282697Sbapt			quote = 0;
502282697Sbapt			break;
503282697Sbapt		case _UC_NONE:
504282697Sbapt			break;
505282697Sbapt		}
506282697Sbapt		sbuf_finish(buf);
50720253Sjoerg
508282697Sbapt		if (comments[i])
509282697Sbapt			fputs(comments[i], fp);
51020253Sjoerg
511282697Sbapt		if (*kwds[i]) {
512282697Sbapt			if (quote)
513282697Sbapt				fprintf(fp, "%s = \"%s\"\n", kwds[i],
514282697Sbapt				    sbuf_data(buf));
515282697Sbapt			else
516282697Sbapt				fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
51720253Sjoerg#if debugging
518282697Sbapt			printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
51920253Sjoerg#endif
52020253Sjoerg		}
52120253Sjoerg	}
522282697Sbapt	sbuf_delete(buf);
523282697Sbapt	return (fclose(fp) != EOF);
52420253Sjoerg}
525