pw_conf.c revision 70133
1204076Spjd/*-
2204076Spjd * Copyright (C) 1996
3219351Spjd *	David L. Nugent.  All rights reserved.
4204076Spjd *
5204076Spjd * Redistribution and use in source and binary forms, with or without
6204076Spjd * modification, are permitted provided that the following conditions
7204076Spjd * are met:
8204076Spjd * 1. Redistributions of source code must retain the above copyright
9204076Spjd *    notice, this list of conditions and the following disclaimer.
10204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
11204076Spjd *    notice, this list of conditions and the following disclaimer in the
12204076Spjd *    documentation and/or other materials provided with the distribution.
13204076Spjd *
14204076Spjd * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24204076Spjd * SUCH DAMAGE.
25204076Spjd */
26204076Spjd
27204076Spjd#ifndef lint
28204076Spjdstatic const char rcsid[] =
29204076Spjd  "$FreeBSD: head/usr.sbin/pw/pw_conf.c 70133 2000-12-18 01:35:56Z dougb $";
30204076Spjd#endif /* not lint */
31204076Spjd
32204076Spjd#include <string.h>
33204076Spjd#include <ctype.h>
34204076Spjd#include <fcntl.h>
35204076Spjd
36204076Spjd#include "pw.h"
37204076Spjd
38204076Spjd#define debugging 0
39204076Spjd
40204076Spjdenum {
41204076Spjd	_UC_NONE,
42204076Spjd	_UC_DEFAULTPWD,
43204076Spjd	_UC_REUSEUID,
44204076Spjd	_UC_REUSEGID,
45204076Spjd	_UC_NISPASSWD,
46204076Spjd	_UC_DOTDIR,
47204076Spjd	_UC_NEWMAIL,
48211982Spjd	_UC_LOGFILE,
49204076Spjd	_UC_HOMEROOT,
50204076Spjd	_UC_SHELLPATH,
51204076Spjd	_UC_SHELLS,
52204076Spjd	_UC_DEFAULTSHELL,
53204076Spjd	_UC_DEFAULTGROUP,
54204076Spjd	_UC_EXTRAGROUPS,
55204076Spjd	_UC_DEFAULTCLASS,
56204076Spjd	_UC_MINUID,
57204076Spjd	_UC_MAXUID,
58204076Spjd	_UC_MINGID,
59204076Spjd	_UC_MAXGID,
60212038Spjd	_UC_EXPIRE,
61204076Spjd	_UC_PASSWORD,
62204076Spjd	_UC_FIELDS
63204076Spjd};
64211886Spjd
65204076Spjdstatic char     bourne_shell[] = "sh";
66204076Spjd
67204076Spjdstatic char    *system_shells[_UC_MAXSHELLS] =
68204076Spjd{
69204076Spjd	bourne_shell,
70204076Spjd	"csh",
71210886Spjd	"tcsh"
72210886Spjd};
73210886Spjd
74204076Spjdstatic char const *booltrue[] =
75204076Spjd{
76204076Spjd	"yes", "true", "1", "on", NULL
77204076Spjd};
78204076Spjdstatic char const *boolfalse[] =
79204076Spjd{
80204076Spjd	"no", "false", "0", "off", NULL
81204076Spjd};
82204076Spjd
83204076Spjdstatic struct userconf config =
84204076Spjd{
85204076Spjd	0,			/* Default password for new users? (nologin) */
86204076Spjd	0,			/* Reuse uids? */
87204076Spjd	0,			/* Reuse gids? */
88204076Spjd	NULL,			/* NIS version of the passwd file */
89219818Spjd	"/usr/share/skel",	/* Where to obtain skeleton files */
90204076Spjd	NULL,			/* Mail to send to new accounts */
91204076Spjd	"/var/log/userlog",	/* Where to log changes */
92226859Spjd	"/home",		/* Where to create home directory */
93226859Spjd	"/bin",			/* Where shells are located */
94226859Spjd	system_shells,		/* List of shells (first is default) */
95226859Spjd	bourne_shell,		/* Default shell */
96226859Spjd	NULL,			/* Default group name */
97226859Spjd	NULL,			/* Default (additional) groups */
98226859Spjd	NULL,			/* Default login class */
99226859Spjd	1000, 32000,		/* Allowed range of uids */
100226859Spjd	1000, 32000,		/* Allowed range of gids */
101204076Spjd	0,			/* Days until account expires */
102204076Spjd	0,			/* Days until password expires */
103204076Spjd	0			/* size of default_group array */
104204076Spjd};
105204076Spjd
106204076Spjdstatic char const *comments[_UC_FIELDS] =
107204076Spjd{
108204076Spjd	"#\n# pw.conf - user/group configuration defaults\n#\n",
109204076Spjd	"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
110204076Spjd	"\n# Reuse gaps in uid sequence? (yes or no)\n",
111204076Spjd	"\n# Reuse gaps in gid sequence? (yes or no)\n",
112204076Spjd	"\n# Path to the NIS passwd file (blank or 'no' for none)\n",
113204076Spjd	"\n# Obtain default dotfiles from this directory\n",
114204076Spjd	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
115204076Spjd	"\n# Log add/change/remove information in this file\n",
116204076Spjd	"\n# Root directory in which $HOME directory is created\n",
117204076Spjd	"\n# Colon separated list of directories containing valid shells\n",
118204076Spjd	"\n# Comma separated list of available shells (without paths)\n",
119204076Spjd	"\n# Default shell (without path)\n",
120204076Spjd	"\n# Default group (leave blank for new group per user)\n",
121204076Spjd	"\n# Extra groups for new users\n",
122204076Spjd	"\n# Default login class for new users\n",
123204076Spjd	"\n# Range of valid default user ids\n",
124204076Spjd	NULL,
125204076Spjd	"\n# Range of valid default group ids\n",
126204076Spjd	NULL,
127204076Spjd	"\n# Days after which account expires (0=disabled)\n",
128204076Spjd	"\n# Days after which password expires (0=disabled)\n"
129204076Spjd};
130204076Spjd
131204076Spjdstatic char const *kwds[] =
132204076Spjd{
133204076Spjd	"",
134204076Spjd	"defaultpasswd",
135204076Spjd	"reuseuids",
136204076Spjd	"reusegids",
137204076Spjd	"nispasswd",
138204076Spjd	"skeleton",
139204076Spjd	"newmail",
140204076Spjd	"logfile",
141204076Spjd	"home",
142204076Spjd	"shellpath",
143204076Spjd	"shells",
144204076Spjd	"defaultshell",
145204076Spjd	"defaultgroup",
146204076Spjd	"extragroups",
147204076Spjd	"defaultclass",
148204076Spjd	"minuid",
149204076Spjd	"maxuid",
150204076Spjd	"mingid",
151204076Spjd	"maxgid",
152204076Spjd	"expire_days",
153204076Spjd	"password_days",
154204076Spjd	NULL
155204076Spjd};
156204076Spjd
157204076Spjdstatic char    *
158204076Spjdunquote(char const * str)
159204076Spjd{
160204076Spjd	if (str && (*str == '"' || *str == '\'')) {
161204076Spjd		char           *p = strchr(str + 1, *str);
162204076Spjd
163204076Spjd		if (p != NULL)
164204076Spjd			*p = '\0';
165204076Spjd		return (char *) (*++str ? str : NULL);
166204076Spjd	}
167204076Spjd	return (char *) str;
168204076Spjd}
169204076Spjd
170204076Spjdint
171204076Spjdboolean_val(char const * str, int dflt)
172204076Spjd{
173204076Spjd	if ((str = unquote(str)) != NULL) {
174204076Spjd		int             i;
175204076Spjd
176204076Spjd		for (i = 0; booltrue[i]; i++)
177204076Spjd			if (strcmp(str, booltrue[i]) == 0)
178204076Spjd				return 1;
179204076Spjd		for (i = 0; boolfalse[i]; i++)
180204076Spjd			if (strcmp(str, boolfalse[i]) == 0)
181204076Spjd				return 0;
182204076Spjd
183204076Spjd		/*
184204076Spjd		 * Special cases for defaultpassword
185204076Spjd		 */
186204076Spjd		if (strcmp(str, "random") == 0)
187214692Spjd			return -1;
188214692Spjd		if (strcmp(str, "none") == 0)
189214692Spjd			return -2;
190204076Spjd	}
191214692Spjd	return dflt;
192214692Spjd}
193214692Spjd
194214692Spjdchar const     *
195219864Spjdboolean_str(int val)
196214692Spjd{
197204076Spjd	if (val == -1)
198214692Spjd		return "random";
199214692Spjd	else if (val == -2)
200214692Spjd		return "none";
201214692Spjd	else
202204076Spjd		return val ? booltrue[0] : boolfalse[0];
203204076Spjd}
204204076Spjd
205204076Spjdchar           *
206204076Spjdnewstr(char const * p)
207204076Spjd{
208204076Spjd	char           *q = NULL;
209204076Spjd
210204076Spjd	if ((p = unquote(p)) != NULL) {
211204076Spjd		int             l = strlen(p) + 1;
212204076Spjd
213204076Spjd		if ((q = malloc(l)) != NULL)
214209183Spjd			memcpy(q, p, l);
215209183Spjd	}
216209183Spjd	return q;
217209183Spjd}
218204076Spjd
219204076Spjd#define LNBUFSZ 1024
220204076Spjd
221204076Spjd
222204076Spjdstruct userconf *
223204076Spjdread_userconfig(char const * file)
224204076Spjd{
225204076Spjd	FILE           *fp;
226204076Spjd
227204076Spjd	extendarray(&config.groups, &config.numgroups, 200);
228204076Spjd	memset(config.groups, 0, config.numgroups * sizeof(char *));
229204076Spjd	if (file == NULL)
230204076Spjd		file = _PATH_PW_CONF;
231220898Spjd	if ((fp = fopen(file, "r")) != NULL) {
232204076Spjd		int	    buflen = LNBUFSZ;
233204076Spjd		char       *buf = malloc(buflen);
234204076Spjd
235204076Spjd	nextline:
236204076Spjd		while (fgets(buf, buflen, fp) != NULL) {
237204076Spjd			char           *p;
238204076Spjd
239204076Spjd			while ((p = strchr(buf, '\n')) == NULL) {
240204076Spjd				int	  l;
241211982Spjd				if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
242204076Spjd					int	ch;
243204076Spjd					while ((ch = fgetc(fp)) != '\n' && ch != EOF);
244204076Spjd					goto nextline;	/* Ignore it */
245204076Spjd				}
246204076Spjd				l = strlen(buf);
247204076Spjd				if (fgets(buf + l, buflen - l, fp) == NULL)
248204076Spjd					break;	/* Unterminated last line */
249204076Spjd			}
250204076Spjd
251204076Spjd			if (p != NULL)
252204076Spjd				*p = '\0';
253213533Spjd
254204076Spjd			if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
255204076Spjd				static char const toks[] = " \t\r\n,=";
256204076Spjd				char           *q = strtok(NULL, toks);
257229945Spjd				int             i = 0;
258213531Spjd
259213531Spjd				while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
260204076Spjd					++i;
261204076Spjd#if debugging
262204076Spjd				if (i == _UC_FIELDS)
263204076Spjd					printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
264204076Spjd				else
265204076Spjd					printf("Got kwd[%s]=%s\n", p, q);
266204076Spjd#endif
267204076Spjd				switch (i) {
268204076Spjd				case _UC_DEFAULTPWD:
269212899Spjd					config.default_password = boolean_val(q, 1);
270204076Spjd					break;
271204076Spjd				case _UC_REUSEUID:
272204076Spjd					config.reuse_uids = boolean_val(q, 0);
273204076Spjd					break;
274218138Spjd				case _UC_REUSEGID:
275204076Spjd					config.reuse_gids = boolean_val(q, 0);
276204076Spjd					break;
277204076Spjd				case _UC_NISPASSWD:
278204076Spjd					config.nispasswd = (q == NULL || !boolean_val(q, 1))
279204076Spjd						? NULL : newstr(q);
280204076Spjd					break;
281204076Spjd				case _UC_DOTDIR:
282212899Spjd					config.dotdir = (q == NULL || !boolean_val(q, 1))
283204076Spjd						? NULL : newstr(q);
284204076Spjd					break;
285204076Spjd				case _UC_NEWMAIL:
286204076Spjd					config.newmail = (q == NULL || !boolean_val(q, 1))
287204076Spjd						? NULL : newstr(q);
288204076Spjd					break;
289204076Spjd				case _UC_LOGFILE:
290204076Spjd					config.logfile = (q == NULL || !boolean_val(q, 1))
291204076Spjd						? NULL : newstr(q);
292204076Spjd					break;
293204076Spjd				case _UC_HOMEROOT:
294204076Spjd					config.home = (q == NULL || !boolean_val(q, 1))
295204076Spjd						? "/home" : newstr(q);
296204076Spjd					break;
297204076Spjd				case _UC_SHELLPATH:
298204076Spjd					config.shelldir = (q == NULL || !boolean_val(q, 1))
299204076Spjd						? "/bin" : newstr(q);
300204076Spjd					break;
301218138Spjd				case _UC_SHELLS:
302218138Spjd					for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
303204076Spjd						system_shells[i] = newstr(q);
304204076Spjd					if (i > 0)
305225786Spjd						while (i < _UC_MAXSHELLS)
306204076Spjd							system_shells[i++] = NULL;
307204076Spjd					break;
308225830Spjd				case _UC_DEFAULTSHELL:
309225830Spjd					config.shell_default = (q == NULL || !boolean_val(q, 1))
310225830Spjd						? (char *) bourne_shell : newstr(q);
311225830Spjd					break;
312225830Spjd				case _UC_DEFAULTGROUP:
313225830Spjd					q = unquote(q);
314225830Spjd					config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
315225830Spjd						? NULL : newstr(q);
316225830Spjd					break;
317225830Spjd				case _UC_EXTRAGROUPS:
318225830Spjd					for (i = 0; q != NULL; q = strtok(NULL, toks)) {
319204076Spjd						if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
320204076Spjd							config.groups[i++] = newstr(q);
321204076Spjd					}
322210881Spjd					if (i > 0)
323210881Spjd						while (i < config.numgroups)
324210881Spjd							config.groups[i++] = NULL;
325210881Spjd					break;
326210881Spjd				case _UC_DEFAULTCLASS:
327210881Spjd					config.default_class = (q == NULL || !boolean_val(q, 1))
328210881Spjd						? NULL : newstr(q);
329204076Spjd					break;
330204076Spjd				case _UC_MINUID:
331204076Spjd					if ((q = unquote(q)) != NULL && isdigit(*q))
332204076Spjd						config.min_uid = (uid_t) atol(q);
333204076Spjd					break;
334204076Spjd				case _UC_MAXUID:
335204076Spjd					if ((q = unquote(q)) != NULL && isdigit(*q))
336204076Spjd						config.max_uid = (uid_t) atol(q);
337204076Spjd					break;
338204076Spjd				case _UC_MINGID:
339204076Spjd					if ((q = unquote(q)) != NULL && isdigit(*q))
340204076Spjd						config.min_gid = (gid_t) atol(q);
341204076Spjd					break;
342204076Spjd				case _UC_MAXGID:
343204076Spjd					if ((q = unquote(q)) != NULL && isdigit(*q))
344204076Spjd						config.max_gid = (gid_t) atol(q);
345204076Spjd					break;
346204076Spjd				case _UC_EXPIRE:
347204076Spjd					if ((q = unquote(q)) != NULL && isdigit(*q))
348204076Spjd						config.expire_days = atoi(q);
349204076Spjd					break;
350204076Spjd				case _UC_PASSWORD:
351204076Spjd					if ((q = unquote(q)) != NULL && isdigit(*q))
352204076Spjd						config.password_days = atoi(q);
353204076Spjd					break;
354204076Spjd				case _UC_FIELDS:
355204076Spjd				case _UC_NONE:
356204076Spjd					break;
357204076Spjd				}
358204076Spjd			}
359204076Spjd		}
360204076Spjd		free(buf);
361204076Spjd		fclose(fp);
362204076Spjd	}
363204076Spjd	return &config;
364204076Spjd}
365204076Spjd
366204076Spjd
367204076Spjdint
368204076Spjdwrite_userconfig(char const * file)
369204076Spjd{
370204076Spjd	int             fd;
371204076Spjd
372204076Spjd	if (file == NULL)
373204076Spjd		file = _PATH_PW_CONF;
374204076Spjd
375204076Spjd	if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
376204076Spjd		FILE           *fp;
377204076Spjd
378204076Spjd		if ((fp = fdopen(fd, "w")) == NULL)
379204076Spjd			close(fd);
380204076Spjd		else {
381204076Spjd			int             i, j, k;
382204076Spjd			int		len = LNBUFSZ;
383204076Spjd			char           *buf = malloc(len);
384204076Spjd
385204076Spjd			for (i = _UC_NONE; i < _UC_FIELDS; i++) {
386204076Spjd				int             quote = 1;
387204076Spjd				char const     *val = buf;
388204076Spjd
389204076Spjd				*buf = '\0';
390204076Spjd				switch (i) {
391204076Spjd				case _UC_DEFAULTPWD:
392204076Spjd					val = boolean_str(config.default_password);
393204076Spjd					break;
394204076Spjd				case _UC_REUSEUID:
395204076Spjd					val = boolean_str(config.reuse_uids);
396204076Spjd					break;
397204076Spjd				case _UC_REUSEGID:
398204076Spjd					val = boolean_str(config.reuse_gids);
399204076Spjd					break;
400204076Spjd				case _UC_NISPASSWD:
401204076Spjd					val = config.nispasswd ? config.nispasswd : "";
402204076Spjd					quote = 0;
403204076Spjd					break;
404204076Spjd				case _UC_DOTDIR:
405204076Spjd					val = config.dotdir ? config.dotdir : boolean_str(0);
406204076Spjd					break;
407204076Spjd				case _UC_NEWMAIL:
408204076Spjd					val = config.newmail ? config.newmail : boolean_str(0);
409204076Spjd					break;
410204076Spjd				case _UC_LOGFILE:
411204076Spjd					val = config.logfile ? config.logfile : boolean_str(0);
412204076Spjd					break;
413204076Spjd				case _UC_HOMEROOT:
414204076Spjd					val = config.home;
415204076Spjd					break;
416204076Spjd				case _UC_SHELLPATH:
417204076Spjd					val = config.shelldir;
418204076Spjd					break;
419204076Spjd				case _UC_SHELLS:
420204076Spjd					for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
421204076Spjd						char	lbuf[64];
422204076Spjd						int	l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
423204076Spjd						if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
424204076Spjd							strcpy(buf + k, lbuf);
425204076Spjd							k += l;
426204076Spjd						}
427204076Spjd					}
428204076Spjd					quote = 0;
429204076Spjd					break;
430204076Spjd				case _UC_DEFAULTSHELL:
431204076Spjd					val = config.shell_default ? config.shell_default : bourne_shell;
432204076Spjd					break;
433204076Spjd				case _UC_DEFAULTGROUP:
434204076Spjd					val = config.default_group ? config.default_group : "";
435204076Spjd					break;
436204076Spjd				case _UC_EXTRAGROUPS:
437204076Spjd					extendarray(&config.groups, &config.numgroups, 200);
438204076Spjd					for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
439204076Spjd						char	lbuf[64];
440204076Spjd						int	l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
441204076Spjd						if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
442214284Spjd							strcpy(buf + k, lbuf);
443214284Spjd							k +=  l;
444214284Spjd						}
445214284Spjd					}
446214284Spjd					quote = 0;
447214284Spjd					break;
448214284Spjd				case _UC_DEFAULTCLASS:
449214284Spjd					val = config.default_class ? config.default_class : "";
450214284Spjd					break;
451214284Spjd				case _UC_MINUID:
452214284Spjd					sprintf(buf, "%lu", (unsigned long) config.min_uid);
453214284Spjd					quote = 0;
454229945Spjd					break;
455214284Spjd				case _UC_MAXUID:
456214284Spjd					sprintf(buf, "%lu", (unsigned long) config.max_uid);
457214284Spjd					quote = 0;
458214284Spjd					break;
459214284Spjd				case _UC_MINGID:
460204076Spjd					sprintf(buf, "%lu", (unsigned long) config.min_gid);
461204076Spjd					quote = 0;
462204076Spjd					break;
463204076Spjd				case _UC_MAXGID:
464204076Spjd					sprintf(buf, "%lu", (unsigned long) config.max_gid);
465204076Spjd					quote = 0;
466229945Spjd					break;
467204076Spjd				case _UC_EXPIRE:
468204076Spjd					sprintf(buf, "%d", config.expire_days);
469204076Spjd					quote = 0;
470229945Spjd					break;
471204076Spjd				case _UC_PASSWORD:
472204076Spjd					sprintf(buf, "%d", config.password_days);
473204076Spjd					quote = 0;
474204076Spjd					break;
475229945Spjd				case _UC_NONE:
476204076Spjd					break;
477204076Spjd				}
478229945Spjd
479204076Spjd				if (comments[i])
480204076Spjd					fputs(comments[i], fp);
481204076Spjd
482204076Spjd				if (*kwds[i]) {
483204076Spjd					if (quote)
484204076Spjd						fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
485204076Spjd					else
486204076Spjd						fprintf(fp, "%s = %s\n", kwds[i], val);
487204076Spjd#if debugging
488204076Spjd					printf("WROTE: %s = %s\n", kwds[i], val);
489204076Spjd#endif
490204076Spjd				}
491209181Spjd			}
492204076Spjd			free(buf);
493204076Spjd			return fclose(fp) != EOF;
494204076Spjd		}
495214284Spjd	}
496214284Spjd	return 0;
497214284Spjd}
498214284Spjd