pw_conf.c revision 44229
1/*-
2 * Copyright (C) 1996
3 *	David L. Nugent.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#ifndef lint
28static const char rcsid[] =
29	"$Id: pw_conf.c,v 1.7 1997/10/10 06:23:36 charnier Exp $";
30#endif /* not lint */
31
32#include <string.h>
33#include <ctype.h>
34#include <fcntl.h>
35
36#include "pw.h"
37
38#define debugging 0
39
40enum {
41	_UC_NONE,
42	_UC_DEFAULTPWD,
43	_UC_REUSEUID,
44	_UC_REUSEGID,
45	_UC_NISPASSWD,
46	_UC_DOTDIR,
47	_UC_NEWMAIL,
48	_UC_LOGFILE,
49	_UC_HOMEROOT,
50	_UC_SHELLPATH,
51	_UC_SHELLS,
52	_UC_DEFAULTSHELL,
53	_UC_DEFAULTGROUP,
54	_UC_EXTRAGROUPS,
55	_UC_DEFAULTCLASS,
56	_UC_MINUID,
57	_UC_MAXUID,
58	_UC_MINGID,
59	_UC_MAXGID,
60	_UC_EXPIRE,
61	_UC_PASSWORD,
62	_UC_FIELDS
63};
64
65static char     bourne_shell[] = "sh";
66
67static char    *system_shells[_UC_MAXSHELLS] =
68{
69	bourne_shell,
70	"csh"
71};
72
73static char const *booltrue[] =
74{
75	"yes", "true", "1", "on", NULL
76};
77static char const *boolfalse[] =
78{
79	"no", "false", "0", "off", NULL
80};
81
82static struct userconf config =
83{
84	0,			/* Default password for new users? (nologin) */
85	0,			/* Reuse uids? */
86	0,			/* Reuse gids? */
87	NULL,			/* NIS version of the passwd file */
88	"/usr/share/skel",	/* Where to obtain skeleton files */
89	NULL,			/* Mail to send to new accounts */
90	"/var/log/userlog",	/* Where to log changes */
91	"/home",		/* Where to create home directory */
92	"/bin",			/* Where shells are located */
93	system_shells,		/* List of shells (first is default) */
94	bourne_shell,		/* Default shell */
95	NULL,			/* Default group name */
96	NULL,			/* Default (additional) groups */
97	NULL,			/* Default login class */
98	1000, 32000,		/* Allowed range of uids */
99	1000, 32000,		/* Allowed range of gids */
100	0,			/* Days until account expires */
101	0			/* Days until password expires */
102};
103
104static char const *comments[_UC_FIELDS] =
105{
106	"#\n# pw.conf - user/group configuration defaults\n#\n",
107	"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
108	"\n# Reuse gaps in uid sequence? (yes or no)\n",
109	"\n# Reuse gaps in gid sequence? (yes or no)\n",
110	"\n# Path to the NIS passwd file (blank or 'no' for none)\n",
111	"\n# Obtain default dotfiles from this directory\n",
112	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
113	"\n# Log add/change/remove information in this file\n",
114	"\n# Root directory in which $HOME directory is created\n",
115	"\n# Colon separated list of directories containing valid shells\n",
116	"\n# Space separated list of available shells (without paths)\n",
117	"\n# Default shell (without path)\n",
118	"\n# Default group (leave blank for new group per user)\n",
119	"\n# Extra groups for new users\n",
120	"\n# Default login class for new users\n",
121	"\n# Range of valid default user ids\n",
122	NULL,
123	"\n# Range of valid default group ids\n",
124	NULL,
125	"\n# Days after which account expires (0=disabled)\n",
126	"\n# Days after which password expires (0=disabled)\n"
127};
128
129static char const *kwds[] =
130{
131	"",
132	"defaultpasswd",
133	"reuseuids",
134	"reusegids",
135	"nispasswd",
136	"skeleton",
137	"newmail",
138	"logfile",
139	"home",
140	"shellpath",
141	"shells",
142	"defaultshell",
143	"defaultgroup",
144	"extragroups",
145	"defaultclass",
146	"minuid",
147	"maxuid",
148	"mingid",
149	"maxgid",
150	"expire_days",
151	"password_days",
152	NULL
153};
154
155static char    *
156unquote(char const * str)
157{
158	if (str && (*str == '"' || *str == '\'')) {
159		char           *p = strchr(str + 1, *str);
160
161		if (p != NULL)
162			*p = '\0';
163		return (char *) (*++str ? str : NULL);
164	}
165	return (char *) str;
166}
167
168int
169boolean_val(char const * str, int dflt)
170{
171	if ((str = unquote(str)) != NULL) {
172		int             i;
173
174		for (i = 0; booltrue[i]; i++)
175			if (strcmp(str, booltrue[i]) == 0)
176				return 1;
177		for (i = 0; boolfalse[i]; i++)
178			if (strcmp(str, boolfalse[i]) == 0)
179				return 0;
180
181		/*
182		 * Special cases for defaultpassword
183		 */
184		if (strcmp(str, "random") == 0)
185			return -1;
186		if (strcmp(str, "none") == 0)
187			return -2;
188	}
189	return dflt;
190}
191
192char const     *
193boolean_str(int val)
194{
195	if (val == -1)
196		return "random";
197	else if (val == -2)
198		return "none";
199	else
200		return val ? booltrue[0] : boolfalse[0];
201}
202
203char           *
204newstr(char const * p)
205{
206	char           *q = NULL;
207
208	if ((p = unquote(p)) != NULL) {
209		int             l = strlen(p) + 1;
210
211		if ((q = malloc(l)) != NULL)
212			memcpy(q, p, l);
213	}
214	return q;
215}
216
217#define LNBUFSZ 1024
218
219
220struct userconf *
221read_userconfig(char const * file)
222{
223	FILE           *fp;
224
225	extendarray(&config.groups, &config.numgroups, 200);
226	memset(config.groups, 0, config.numgroups * sizeof(char *));
227	if (file == NULL)
228		file = _PATH_PW_CONF;
229	if ((fp = fopen(file, "r")) != NULL) {
230		int	    buflen = LNBUFSZ;
231		char       *buf = malloc(buflen);
232
233	nextline:
234		while (fgets(buf, buflen, fp) != NULL) {
235			char           *p;
236
237			while ((p = strchr(buf, '\n')) == NULL) {
238				int	  l;
239				if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
240					int	ch;
241					while ((ch = fgetc(fp)) != '\n' && ch != EOF);
242					goto nextline;	/* Ignore it */
243				}
244				l = strlen(buf);
245				if (fgets(buf + l, buflen - l, fp) == NULL)
246					break;	/* Unterminated last line */
247			}
248
249			if (p != NULL)
250				*p = '\0';
251
252			if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
253				static char const toks[] = " \t\r\n,=";
254				char           *q = strtok(NULL, toks);
255				int             i = 0;
256
257				while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
258					++i;
259#if debugging
260				if (i == _UC_FIELDS)
261					printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
262				else
263					printf("Got kwd[%s]=%s\n", p, q);
264#endif
265				switch (i) {
266				case _UC_DEFAULTPWD:
267					config.default_password = boolean_val(q, 1);
268					break;
269				case _UC_REUSEUID:
270					config.reuse_uids = boolean_val(q, 0);
271					break;
272				case _UC_REUSEGID:
273					config.reuse_gids = boolean_val(q, 0);
274					break;
275				case _UC_NISPASSWD:
276					config.nispasswd = (q == NULL || !boolean_val(q, 1))
277						? NULL : newstr(q);
278					break;
279				case _UC_DOTDIR:
280					config.dotdir = (q == NULL || !boolean_val(q, 1))
281						? NULL : newstr(q);
282					break;
283				case _UC_NEWMAIL:
284					config.newmail = (q == NULL || !boolean_val(q, 1))
285						? NULL : newstr(q);
286					break;
287				case _UC_LOGFILE:
288					config.logfile = (q == NULL || !boolean_val(q, 1))
289						? NULL : newstr(q);
290					break;
291				case _UC_HOMEROOT:
292					config.home = (q == NULL || !boolean_val(q, 1))
293						? "/home" : newstr(q);
294					break;
295				case _UC_SHELLPATH:
296					config.shelldir = (q == NULL || !boolean_val(q, 1))
297						? "/bin" : newstr(q);
298					break;
299				case _UC_SHELLS:
300					for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
301						system_shells[i] = newstr(q);
302					if (i > 0)
303						while (i < _UC_MAXSHELLS)
304							system_shells[i++] = NULL;
305					break;
306				case _UC_DEFAULTSHELL:
307					config.shell_default = (q == NULL || !boolean_val(q, 1))
308						? (char *) bourne_shell : newstr(q);
309					break;
310				case _UC_DEFAULTGROUP:
311					q = unquote(q);
312					config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
313						? NULL : newstr(q);
314					break;
315				case _UC_EXTRAGROUPS:
316					for (i = 0; q != NULL; q = strtok(NULL, toks)) {
317						if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
318							config.groups[i++] = newstr(q);
319					}
320					if (i > 0)
321						while (i < config.numgroups)
322							config.groups[i++] = NULL;
323					break;
324				case _UC_DEFAULTCLASS:
325					config.default_class = (q == NULL || !boolean_val(q, 1))
326						? NULL : newstr(q);
327					break;
328				case _UC_MINUID:
329					if ((q = unquote(q)) != NULL && isdigit(*q))
330						config.min_uid = (uid_t) atol(q);
331					break;
332				case _UC_MAXUID:
333					if ((q = unquote(q)) != NULL && isdigit(*q))
334						config.max_uid = (uid_t) atol(q);
335					break;
336				case _UC_MINGID:
337					if ((q = unquote(q)) != NULL && isdigit(*q))
338						config.min_gid = (gid_t) atol(q);
339					break;
340				case _UC_MAXGID:
341					if ((q = unquote(q)) != NULL && isdigit(*q))
342						config.max_gid = (gid_t) atol(q);
343					break;
344				case _UC_EXPIRE:
345					if ((q = unquote(q)) != NULL && isdigit(*q))
346						config.expire_days = atoi(q);
347					break;
348				case _UC_PASSWORD:
349					if ((q = unquote(q)) != NULL && isdigit(*q))
350						config.password_days = atoi(q);
351					break;
352				case _UC_FIELDS:
353				case _UC_NONE:
354					break;
355				}
356			}
357		}
358		free(buf);
359		fclose(fp);
360	}
361	return &config;
362}
363
364
365int
366write_userconfig(char const * file)
367{
368	int             fd;
369
370	if (file == NULL)
371		file = _PATH_PW_CONF;
372
373	if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
374		FILE           *fp;
375
376		if ((fp = fdopen(fd, "w")) == NULL)
377			close(fd);
378		else {
379			int             i, j, k;
380			int		len = LNBUFSZ;
381			char           *buf = malloc(len);
382
383			for (i = _UC_NONE; i < _UC_FIELDS; i++) {
384				int             quote = 1;
385				char const     *val = buf;
386
387				*buf = '\0';
388				switch (i) {
389				case _UC_DEFAULTPWD:
390					val = boolean_str(config.default_password);
391					break;
392				case _UC_REUSEUID:
393					val = boolean_str(config.reuse_uids);
394					break;
395				case _UC_REUSEGID:
396					val = boolean_str(config.reuse_gids);
397					break;
398				case _UC_NISPASSWD:
399					val = config.nispasswd ? config.nispasswd : "";
400					quote = 0;
401					break;
402				case _UC_DOTDIR:
403					val = config.dotdir ? config.dotdir : boolean_str(0);
404					break;
405				case _UC_NEWMAIL:
406					val = config.newmail ? config.newmail : boolean_str(0);
407					break;
408				case _UC_LOGFILE:
409					val = config.logfile ? config.logfile : boolean_str(0);
410					break;
411				case _UC_HOMEROOT:
412					val = config.home;
413					break;
414				case _UC_SHELLPATH:
415					val = config.shelldir;
416					break;
417				case _UC_SHELLS:
418					for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
419						char	lbuf[64];
420						int	l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
421						if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
422							strcpy(buf + k, lbuf);
423							k += l;
424						}
425					}
426					quote = 0;
427					break;
428				case _UC_DEFAULTSHELL:
429					val = config.shell_default ? config.shell_default : bourne_shell;
430					break;
431				case _UC_DEFAULTGROUP:
432					val = config.default_group ? config.default_group : "";
433					break;
434				case _UC_EXTRAGROUPS:
435					extendarray(&config.groups, &config.numgroups, 200);
436					for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
437						char	lbuf[64];
438						int	l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
439						if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
440							strcpy(buf + k, lbuf);
441							k +=  l;
442						}
443					}
444					quote = 0;
445					break;
446				case _UC_DEFAULTCLASS:
447					val = config.default_class ? config.default_class : "";
448					break;
449				case _UC_MINUID:
450					sprintf(buf, "%lu", (unsigned long) config.min_uid);
451					quote = 0;
452					break;
453				case _UC_MAXUID:
454					sprintf(buf, "%lu", (unsigned long) config.max_uid);
455					quote = 0;
456					break;
457				case _UC_MINGID:
458					sprintf(buf, "%lu", (unsigned long) config.min_gid);
459					quote = 0;
460					break;
461				case _UC_MAXGID:
462					sprintf(buf, "%lu", (unsigned long) config.max_gid);
463					quote = 0;
464					break;
465				case _UC_EXPIRE:
466					sprintf(buf, "%d", config.expire_days);
467					quote = 0;
468					break;
469				case _UC_PASSWORD:
470					sprintf(buf, "%d", config.password_days);
471					quote = 0;
472					break;
473				case _UC_NONE:
474					break;
475				}
476
477				if (comments[i])
478					fputs(comments[i], fp);
479
480				if (*kwds[i]) {
481					if (quote)
482						fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
483					else
484						fprintf(fp, "%s = %s\n", kwds[i], val);
485#if debugging
486					printf("WROTE: %s = %s\n", kwds[i], val);
487#endif
488				}
489			}
490			free(buf);
491			return fclose(fp) != EOF;
492		}
493	}
494	return 0;
495}
496