120253Sjoerg/*-
2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330449Seadler *
420302Sjoerg * Copyright (C) 1996
520302Sjoerg *	David L. Nugent.  All rights reserved.
620253Sjoerg *
720253Sjoerg * Redistribution and use in source and binary forms, with or without
820253Sjoerg * modification, are permitted provided that the following conditions
920253Sjoerg * are met:
1020253Sjoerg * 1. Redistributions of source code must retain the above copyright
1120302Sjoerg *    notice, this list of conditions and the following disclaimer.
1220253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1320253Sjoerg *    notice, this list of conditions and the following disclaimer in the
1420253Sjoerg *    documentation and/or other materials provided with the distribution.
1520253Sjoerg *
1620302Sjoerg * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
1720253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1820253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1920302Sjoerg * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
2020253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2120253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2220253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2320253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2420253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2520253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2620253Sjoerg * SUCH DAMAGE.
2720253Sjoerg */
2820253Sjoerg
2930259Scharnier#ifndef lint
3030259Scharnierstatic const char rcsid[] =
3150479Speter  "$FreeBSD: stable/11/usr.sbin/pw/pw_conf.c 330449 2018-03-05 07:26:05Z eadler $";
3230259Scharnier#endif /* not lint */
3330259Scharnier
34282681Sbapt#include <sys/types.h>
35282681Sbapt#include <sys/sbuf.h>
36286201Sbapt
37286201Sbapt#include <err.h>
38286201Sbapt#include <fcntl.h>
3920253Sjoerg#include <string.h>
40286201Sbapt#include <unistd.h>
4120253Sjoerg
4220253Sjoerg#include "pw.h"
4320253Sjoerg
4420253Sjoerg#define debugging 0
4520253Sjoerg
4620253Sjoergenum {
4720253Sjoerg	_UC_NONE,
4820253Sjoerg	_UC_DEFAULTPWD,
4920253Sjoerg	_UC_REUSEUID,
5020253Sjoerg	_UC_REUSEGID,
5121330Sdavidn	_UC_NISPASSWD,
5220253Sjoerg	_UC_DOTDIR,
5320253Sjoerg	_UC_NEWMAIL,
5420253Sjoerg	_UC_LOGFILE,
5520253Sjoerg	_UC_HOMEROOT,
56168044Sle	_UC_HOMEMODE,
5720253Sjoerg	_UC_SHELLPATH,
5820253Sjoerg	_UC_SHELLS,
5920253Sjoerg	_UC_DEFAULTSHELL,
6020253Sjoerg	_UC_DEFAULTGROUP,
6120253Sjoerg	_UC_EXTRAGROUPS,
6220253Sjoerg	_UC_DEFAULTCLASS,
6320253Sjoerg	_UC_MINUID,
6420253Sjoerg	_UC_MAXUID,
6520253Sjoerg	_UC_MINGID,
6620253Sjoerg	_UC_MAXGID,
6720253Sjoerg	_UC_EXPIRE,
6820253Sjoerg	_UC_PASSWORD,
6920253Sjoerg	_UC_FIELDS
7020253Sjoerg};
7120253Sjoerg
7220253Sjoergstatic char     bourne_shell[] = "sh";
7320253Sjoerg
7420253Sjoergstatic char    *system_shells[_UC_MAXSHELLS] =
7520253Sjoerg{
7620253Sjoerg	bourne_shell,
7763239Sdavidn	"csh",
7863239Sdavidn	"tcsh"
7920253Sjoerg};
8020253Sjoerg
8120253Sjoergstatic char const *booltrue[] =
8220253Sjoerg{
8320253Sjoerg	"yes", "true", "1", "on", NULL
8420253Sjoerg};
8520253Sjoergstatic char const *boolfalse[] =
8620253Sjoerg{
8720253Sjoerg	"no", "false", "0", "off", NULL
8820253Sjoerg};
8920253Sjoerg
9020253Sjoergstatic struct userconf config =
9120253Sjoerg{
9220253Sjoerg	0,			/* Default password for new users? (nologin) */
9320253Sjoerg	0,			/* Reuse uids? */
9420253Sjoerg	0,			/* Reuse gids? */
9521330Sdavidn	NULL,			/* NIS version of the passwd file */
9620253Sjoerg	"/usr/share/skel",	/* Where to obtain skeleton files */
9720253Sjoerg	NULL,			/* Mail to send to new accounts */
9820253Sjoerg	"/var/log/userlog",	/* Where to log changes */
9920253Sjoerg	"/home",		/* Where to create home directory */
100219408Sjkim	_DEF_DIRMODE,		/* Home directory perms, modified by umask */
10120253Sjoerg	"/bin",			/* Where shells are located */
10220253Sjoerg	system_shells,		/* List of shells (first is default) */
10320253Sjoerg	bourne_shell,		/* Default shell */
10420253Sjoerg	NULL,			/* Default group name */
10520747Sdavidn	NULL,			/* Default (additional) groups */
10620253Sjoerg	NULL,			/* Default login class */
10720253Sjoerg	1000, 32000,		/* Allowed range of uids */
10820253Sjoerg	1000, 32000,		/* Allowed range of gids */
10920253Sjoerg	0,			/* Days until account expires */
110285412Sbapt	0			/* Days until password expires */
11120253Sjoerg};
11220253Sjoerg
11320253Sjoergstatic char const *comments[_UC_FIELDS] =
11420253Sjoerg{
11520253Sjoerg	"#\n# pw.conf - user/group configuration defaults\n#\n",
11620253Sjoerg	"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
11720253Sjoerg	"\n# Reuse gaps in uid sequence? (yes or no)\n",
11820253Sjoerg	"\n# Reuse gaps in gid sequence? (yes or no)\n",
11921330Sdavidn	"\n# Path to the NIS passwd file (blank or 'no' for none)\n",
12020253Sjoerg	"\n# Obtain default dotfiles from this directory\n",
12120253Sjoerg	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
12220253Sjoerg	"\n# Log add/change/remove information in this file\n",
12320253Sjoerg	"\n# Root directory in which $HOME directory is created\n",
124168044Sle	"\n# Mode for the new $HOME directory, will be modified by umask\n",
12520253Sjoerg	"\n# Colon separated list of directories containing valid shells\n",
12670133Sdougb	"\n# Comma separated list of available shells (without paths)\n",
12720253Sjoerg	"\n# Default shell (without path)\n",
12820253Sjoerg	"\n# Default group (leave blank for new group per user)\n",
12920253Sjoerg	"\n# Extra groups for new users\n",
13020253Sjoerg	"\n# Default login class for new users\n",
13120253Sjoerg	"\n# Range of valid default user ids\n",
13220253Sjoerg	NULL,
13320253Sjoerg	"\n# Range of valid default group ids\n",
13420253Sjoerg	NULL,
13520253Sjoerg	"\n# Days after which account expires (0=disabled)\n",
13620253Sjoerg	"\n# Days after which password expires (0=disabled)\n"
13720253Sjoerg};
13820253Sjoerg
13920253Sjoergstatic char const *kwds[] =
14020253Sjoerg{
14120253Sjoerg	"",
14220253Sjoerg	"defaultpasswd",
14320253Sjoerg	"reuseuids",
14420253Sjoerg	"reusegids",
14521330Sdavidn	"nispasswd",
14620253Sjoerg	"skeleton",
14720253Sjoerg	"newmail",
14820253Sjoerg	"logfile",
14920253Sjoerg	"home",
150168044Sle	"homemode",
15120253Sjoerg	"shellpath",
15220253Sjoerg	"shells",
15320253Sjoerg	"defaultshell",
15420253Sjoerg	"defaultgroup",
15520253Sjoerg	"extragroups",
15620253Sjoerg	"defaultclass",
15720253Sjoerg	"minuid",
15820253Sjoerg	"maxuid",
15920253Sjoerg	"mingid",
16020253Sjoerg	"maxgid",
16120253Sjoerg	"expire_days",
16220253Sjoerg	"password_days",
16320253Sjoerg	NULL
16420253Sjoerg};
16520253Sjoerg
16620253Sjoergstatic char    *
16720253Sjoergunquote(char const * str)
16820253Sjoerg{
16920253Sjoerg	if (str && (*str == '"' || *str == '\'')) {
17020253Sjoerg		char           *p = strchr(str + 1, *str);
17120253Sjoerg
17220253Sjoerg		if (p != NULL)
17320253Sjoerg			*p = '\0';
17420253Sjoerg		return (char *) (*++str ? str : NULL);
17520253Sjoerg	}
17620253Sjoerg	return (char *) str;
17720253Sjoerg}
17820253Sjoerg
17920253Sjoergint
18020253Sjoergboolean_val(char const * str, int dflt)
18120253Sjoerg{
18220253Sjoerg	if ((str = unquote(str)) != NULL) {
18320253Sjoerg		int             i;
18420253Sjoerg
18520253Sjoerg		for (i = 0; booltrue[i]; i++)
18620253Sjoerg			if (strcmp(str, booltrue[i]) == 0)
18720253Sjoerg				return 1;
18820253Sjoerg		for (i = 0; boolfalse[i]; i++)
18920253Sjoerg			if (strcmp(str, boolfalse[i]) == 0)
19020253Sjoerg				return 0;
191305741Sasomers	}
192305741Sasomers	return dflt;
193305741Sasomers}
19420253Sjoerg
195305741Sasomersint
196305741Sasomerspasswd_val(char const * str, int dflt)
197305741Sasomers{
198305741Sasomers	if ((str = unquote(str)) != NULL) {
199305741Sasomers		int             i;
200305741Sasomers
201305741Sasomers		for (i = 0; booltrue[i]; i++)
202305741Sasomers			if (strcmp(str, booltrue[i]) == 0)
203326848Seugen				return P_YES;
204305741Sasomers		for (i = 0; boolfalse[i]; i++)
205305741Sasomers			if (strcmp(str, boolfalse[i]) == 0)
206326848Seugen				return P_NO;
207305741Sasomers
20820253Sjoerg		/*
20920253Sjoerg		 * Special cases for defaultpassword
21020253Sjoerg		 */
21120253Sjoerg		if (strcmp(str, "random") == 0)
212326848Seugen			return P_RANDOM;
21320253Sjoerg		if (strcmp(str, "none") == 0)
214326848Seugen			return P_NONE;
215305741Sasomers
216305741Sasomers		errx(1, "Invalid value for default password");
21720253Sjoerg	}
21820253Sjoerg	return dflt;
21920253Sjoerg}
22020253Sjoerg
22120253Sjoergchar const     *
22220253Sjoergboolean_str(int val)
22320253Sjoerg{
22420253Sjoerg	if (val == -1)
22520253Sjoerg		return "random";
22620253Sjoerg	else if (val == -2)
22720253Sjoerg		return "none";
22820253Sjoerg	else
22920253Sjoerg		return val ? booltrue[0] : boolfalse[0];
23020253Sjoerg}
23120253Sjoerg
23220253Sjoergchar           *
23320253Sjoergnewstr(char const * p)
23420253Sjoerg{
235282718Sbapt	char	*q;
23620253Sjoerg
237282718Sbapt	if ((p = unquote(p)) == NULL)
238282718Sbapt		return (NULL);
23920253Sjoerg
240282719Sbapt	if ((q = strdup(p)) == NULL)
241282719Sbapt		err(1, "strdup()");
242282718Sbapt
243282718Sbapt	return (q);
24420253Sjoerg}
24520253Sjoerg
24620253Sjoergstruct userconf *
24720253Sjoergread_userconfig(char const * file)
24820253Sjoerg{
249264781Sbapt	FILE	*fp;
250264781Sbapt	char	*buf, *p;
251286154Sbapt	const char *errstr;
252264781Sbapt	size_t	linecap;
253264781Sbapt	ssize_t	linelen;
25420253Sjoerg
255264781Sbapt	buf = NULL;
256264781Sbapt	linecap = 0;
257264781Sbapt
25820253Sjoerg	if (file == NULL)
25920253Sjoerg		file = _PATH_PW_CONF;
260264781Sbapt
261283815Sbapt	if ((fp = fopen(file, "r")) == NULL)
262283815Sbapt		return (&config);
26320747Sdavidn
264283815Sbapt	while ((linelen = getline(&buf, &linecap, fp)) > 0) {
265283815Sbapt		if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
266283815Sbapt			static char const toks[] = " \t\r\n,=";
267283815Sbapt			char           *q = strtok(NULL, toks);
268283815Sbapt			int             i = 0;
269283815Sbapt			mode_t          *modeset;
270283815Sbapt
271283815Sbapt			while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
272283815Sbapt				++i;
27320253Sjoerg#if debugging
274283815Sbapt			if (i == _UC_FIELDS)
275283815Sbapt				printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
276283815Sbapt			else
277283815Sbapt				printf("Got kwd[%s]=%s\n", p, q);
27820253Sjoerg#endif
279283815Sbapt			switch (i) {
280283815Sbapt			case _UC_DEFAULTPWD:
281305741Sasomers				config.default_password = passwd_val(q, 1);
282283815Sbapt				break;
283283815Sbapt			case _UC_REUSEUID:
284283815Sbapt				config.reuse_uids = boolean_val(q, 0);
285283815Sbapt				break;
286283815Sbapt			case _UC_REUSEGID:
287283815Sbapt				config.reuse_gids = boolean_val(q, 0);
288283815Sbapt				break;
289283815Sbapt			case _UC_NISPASSWD:
290283815Sbapt				config.nispasswd = (q == NULL || !boolean_val(q, 1))
291283815Sbapt					? NULL : newstr(q);
292283815Sbapt				break;
293283815Sbapt			case _UC_DOTDIR:
294283815Sbapt				config.dotdir = (q == NULL || !boolean_val(q, 1))
295283815Sbapt					? NULL : newstr(q);
296283815Sbapt				break;
29720747Sdavidn				case _UC_NEWMAIL:
298283815Sbapt				config.newmail = (q == NULL || !boolean_val(q, 1))
299283815Sbapt					? NULL : newstr(q);
300283815Sbapt				break;
301283815Sbapt			case _UC_LOGFILE:
302283815Sbapt				config.logfile = (q == NULL || !boolean_val(q, 1))
303283815Sbapt					? NULL : newstr(q);
304283815Sbapt				break;
305283815Sbapt			case _UC_HOMEROOT:
306283815Sbapt				config.home = (q == NULL || !boolean_val(q, 1))
307283815Sbapt					? "/home" : newstr(q);
308283815Sbapt				break;
309283815Sbapt			case _UC_HOMEMODE:
310283815Sbapt				modeset = setmode(q);
311283815Sbapt				config.homemode = (q == NULL || !boolean_val(q, 1))
312283815Sbapt					? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
313283815Sbapt				free(modeset);
314283815Sbapt				break;
315283815Sbapt			case _UC_SHELLPATH:
316283815Sbapt				config.shelldir = (q == NULL || !boolean_val(q, 1))
317283815Sbapt					? "/bin" : newstr(q);
318283815Sbapt				break;
319283815Sbapt			case _UC_SHELLS:
320283815Sbapt				for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
321283815Sbapt					system_shells[i] = newstr(q);
322283815Sbapt				if (i > 0)
323283815Sbapt					while (i < _UC_MAXSHELLS)
324283815Sbapt						system_shells[i++] = NULL;
325283815Sbapt				break;
326283815Sbapt			case _UC_DEFAULTSHELL:
327283815Sbapt				config.shell_default = (q == NULL || !boolean_val(q, 1))
328283815Sbapt					? (char *) bourne_shell : newstr(q);
329283815Sbapt				break;
330283815Sbapt			case _UC_DEFAULTGROUP:
331283815Sbapt				q = unquote(q);
332283815Sbapt				config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
333283815Sbapt					? NULL : newstr(q);
334283815Sbapt				break;
335283815Sbapt			case _UC_EXTRAGROUPS:
336292849Sbapt				while ((q = strtok(NULL, toks)) != NULL) {
337286196Sbapt					if (config.groups == NULL)
338286196Sbapt						config.groups = sl_init();
339285412Sbapt					sl_add(config.groups, newstr(q));
340286196Sbapt				}
341283815Sbapt				break;
342283815Sbapt			case _UC_DEFAULTCLASS:
343283815Sbapt				config.default_class = (q == NULL || !boolean_val(q, 1))
344283815Sbapt					? NULL : newstr(q);
345283815Sbapt				break;
346283815Sbapt			case _UC_MINUID:
347286151Sbapt				if ((q = unquote(q)) != NULL) {
348286154Sbapt					config.min_uid = strtounum(q, 0,
349286154Sbapt					    UID_MAX, &errstr);
350286151Sbapt					if (errstr)
351286154Sbapt						warnx("Invalid min_uid: '%s';"
352286154Sbapt						    " ignoring", q);
353286151Sbapt				}
354283815Sbapt				break;
355283815Sbapt			case _UC_MAXUID:
356286151Sbapt				if ((q = unquote(q)) != NULL) {
357286154Sbapt					config.max_uid = strtounum(q, 0,
358286154Sbapt					    UID_MAX, &errstr);
359286151Sbapt					if (errstr)
360286154Sbapt						warnx("Invalid max_uid: '%s';"
361286154Sbapt						    " ignoring", q);
362286151Sbapt				}
363283815Sbapt				break;
364283815Sbapt			case _UC_MINGID:
365286154Sbapt				if ((q = unquote(q)) != NULL) {
366286154Sbapt					config.min_gid = strtounum(q, 0,
367286154Sbapt					    GID_MAX, &errstr);
368286151Sbapt					if (errstr)
369286154Sbapt						warnx("Invalid min_gid: '%s';"
370286154Sbapt						    " ignoring", q);
371286155Sbapt				}
372283815Sbapt				break;
373283815Sbapt			case _UC_MAXGID:
374286151Sbapt				if ((q = unquote(q)) != NULL) {
375286154Sbapt					config.max_gid = strtounum(q, 0,
376286154Sbapt					    GID_MAX, &errstr);
377286151Sbapt					if (errstr)
378286154Sbapt						warnx("Invalid max_gid: '%s';"
379286154Sbapt						    " ignoring", q);
380286151Sbapt				}
381283815Sbapt				break;
382283815Sbapt			case _UC_EXPIRE:
383286152Sbapt				if ((q = unquote(q)) != NULL) {
384286154Sbapt					config.expire_days = strtonum(q, 0,
385286154Sbapt					    INT_MAX, &errstr);
386286152Sbapt					if (errstr)
387286154Sbapt						warnx("Invalid expire days:"
388286154Sbapt						    " '%s'; ignoring", q);
389286152Sbapt				}
390283815Sbapt				break;
391283815Sbapt			case _UC_PASSWORD:
392286152Sbapt				if ((q = unquote(q)) != NULL) {
393286154Sbapt					config.password_days = strtonum(q, 0,
394286154Sbapt					    INT_MAX, &errstr);
395286152Sbapt					if (errstr)
396286154Sbapt						warnx("Invalid password days:"
397286154Sbapt						    " '%s'; ignoring", q);
398286152Sbapt				}
399283815Sbapt				break;
400283815Sbapt			case _UC_FIELDS:
401283815Sbapt			case _UC_NONE:
402283815Sbapt				break;
40320253Sjoerg			}
40420253Sjoerg		}
40520253Sjoerg	}
406283818Sbapt	free(buf);
407283818Sbapt	fclose(fp);
408283818Sbapt
409283815Sbapt	return (&config);
41020253Sjoerg}
41120253Sjoerg
41220253Sjoerg
41320253Sjoergint
414286196Sbaptwrite_userconfig(struct userconf *cnf, const char *file)
41520253Sjoerg{
41620253Sjoerg	int             fd;
417282697Sbapt	int             i, j;
418282681Sbapt	struct sbuf	*buf;
419282697Sbapt	FILE           *fp;
42020253Sjoerg
42120253Sjoerg	if (file == NULL)
42220253Sjoerg		file = _PATH_PW_CONF;
42320253Sjoerg
424282697Sbapt	if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
425282697Sbapt		return (0);
42620253Sjoerg
427282697Sbapt	if ((fp = fdopen(fd, "w")) == NULL) {
428282697Sbapt		close(fd);
429282697Sbapt		return (0);
430282697Sbapt	}
431282681Sbapt
432282697Sbapt	buf = sbuf_new_auto();
433282697Sbapt	for (i = _UC_NONE; i < _UC_FIELDS; i++) {
434282697Sbapt		int             quote = 1;
43520253Sjoerg
436282697Sbapt		sbuf_clear(buf);
437282697Sbapt		switch (i) {
438282697Sbapt		case _UC_DEFAULTPWD:
439286196Sbapt			sbuf_cat(buf, boolean_str(cnf->default_password));
440282697Sbapt			break;
441282697Sbapt		case _UC_REUSEUID:
442286196Sbapt			sbuf_cat(buf, boolean_str(cnf->reuse_uids));
443282697Sbapt			break;
444282697Sbapt		case _UC_REUSEGID:
445286196Sbapt			sbuf_cat(buf, boolean_str(cnf->reuse_gids));
446282697Sbapt			break;
447282697Sbapt		case _UC_NISPASSWD:
448286196Sbapt			sbuf_cat(buf, cnf->nispasswd ?  cnf->nispasswd : "");
449282697Sbapt			quote = 0;
450282697Sbapt			break;
451282697Sbapt		case _UC_DOTDIR:
452286196Sbapt			sbuf_cat(buf, cnf->dotdir ?  cnf->dotdir :
453282697Sbapt			    boolean_str(0));
454282697Sbapt			break;
455282697Sbapt		case _UC_NEWMAIL:
456286196Sbapt			sbuf_cat(buf, cnf->newmail ?  cnf->newmail :
457282697Sbapt			    boolean_str(0));
458282697Sbapt			break;
459282697Sbapt		case _UC_LOGFILE:
460286196Sbapt			sbuf_cat(buf, cnf->logfile ?  cnf->logfile :
461282697Sbapt			    boolean_str(0));
462282697Sbapt			break;
463282697Sbapt		case _UC_HOMEROOT:
464286196Sbapt			sbuf_cat(buf, cnf->home);
465282697Sbapt			break;
466282697Sbapt		case _UC_HOMEMODE:
467286196Sbapt			sbuf_printf(buf, "%04o", cnf->homemode);
468282697Sbapt			quote = 0;
469282697Sbapt			break;
470282697Sbapt		case _UC_SHELLPATH:
471286196Sbapt			sbuf_cat(buf, cnf->shelldir);
472282697Sbapt			break;
473282697Sbapt		case _UC_SHELLS:
474282697Sbapt			for (j = 0; j < _UC_MAXSHELLS &&
475282697Sbapt			    system_shells[j] != NULL; j++)
476282697Sbapt				sbuf_printf(buf, "%s\"%s\"", j ?
477282697Sbapt				    "," : "", system_shells[j]);
478282697Sbapt			quote = 0;
479282697Sbapt			break;
480282697Sbapt		case _UC_DEFAULTSHELL:
481286196Sbapt			sbuf_cat(buf, cnf->shell_default ?
482286196Sbapt			    cnf->shell_default : bourne_shell);
483282697Sbapt			break;
484282697Sbapt		case _UC_DEFAULTGROUP:
485286196Sbapt			sbuf_cat(buf, cnf->default_group ?
486286196Sbapt			    cnf->default_group : "");
487282697Sbapt			break;
488282697Sbapt		case _UC_EXTRAGROUPS:
489286196Sbapt			for (j = 0; cnf->groups != NULL &&
490286196Sbapt			    j < (int)cnf->groups->sl_cur; j++)
491282697Sbapt				sbuf_printf(buf, "%s\"%s\"", j ?
492286196Sbapt				    "," : "", cnf->groups->sl_str[j]);
493282697Sbapt			quote = 0;
494282697Sbapt			break;
495282697Sbapt		case _UC_DEFAULTCLASS:
496286196Sbapt			sbuf_cat(buf, cnf->default_class ?
497286196Sbapt			    cnf->default_class : "");
498282697Sbapt			break;
499282697Sbapt		case _UC_MINUID:
500286196Sbapt			sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_uid);
501282697Sbapt			quote = 0;
502282697Sbapt			break;
503282697Sbapt		case _UC_MAXUID:
504286196Sbapt			sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_uid);
505282697Sbapt			quote = 0;
506282697Sbapt			break;
507282697Sbapt		case _UC_MINGID:
508286196Sbapt			sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_gid);
509282697Sbapt			quote = 0;
510282697Sbapt			break;
511282697Sbapt		case _UC_MAXGID:
512286196Sbapt			sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_gid);
513282697Sbapt			quote = 0;
514282697Sbapt			break;
515282697Sbapt		case _UC_EXPIRE:
516286211Sbapt			sbuf_printf(buf, "%jd", (intmax_t)cnf->expire_days);
517282697Sbapt			quote = 0;
518282697Sbapt			break;
519282697Sbapt		case _UC_PASSWORD:
520286211Sbapt			sbuf_printf(buf, "%jd", (intmax_t)cnf->password_days);
521282697Sbapt			quote = 0;
522282697Sbapt			break;
523282697Sbapt		case _UC_NONE:
524282697Sbapt			break;
525282697Sbapt		}
526282697Sbapt		sbuf_finish(buf);
52720253Sjoerg
528282697Sbapt		if (comments[i])
529282697Sbapt			fputs(comments[i], fp);
53020253Sjoerg
531282697Sbapt		if (*kwds[i]) {
532282697Sbapt			if (quote)
533282697Sbapt				fprintf(fp, "%s = \"%s\"\n", kwds[i],
534282697Sbapt				    sbuf_data(buf));
535282697Sbapt			else
536282697Sbapt				fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
53720253Sjoerg#if debugging
538282697Sbapt			printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
53920253Sjoerg#endif
54020253Sjoerg		}
54120253Sjoerg	}
542282697Sbapt	sbuf_delete(buf);
543282697Sbapt	return (fclose(fp) != EOF);
54420253Sjoerg}
545