pw_conf.c revision 20302
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 *	$Id: pw_conf.c,v 1.1.1.1 1996/12/09 14:05:35 joerg Exp $
27 */
28
29#include <string.h>
30#include <ctype.h>
31#include <fcntl.h>
32
33#include "pw.h"
34
35#define debugging 0
36
37enum {
38	_UC_NONE,
39	_UC_DEFAULTPWD,
40	_UC_REUSEUID,
41	_UC_REUSEGID,
42	_UC_DOTDIR,
43	_UC_NEWMAIL,
44	_UC_LOGFILE,
45	_UC_HOMEROOT,
46	_UC_SHELLPATH,
47	_UC_SHELLS,
48	_UC_DEFAULTSHELL,
49	_UC_DEFAULTGROUP,
50	_UC_EXTRAGROUPS,
51	_UC_DEFAULTCLASS,
52	_UC_MINUID,
53	_UC_MAXUID,
54	_UC_MINGID,
55	_UC_MAXGID,
56	_UC_EXPIRE,
57	_UC_PASSWORD,
58	_UC_FIELDS
59};
60
61static char     bourne_shell[] = "sh";
62
63static char    *system_shells[_UC_MAXSHELLS] =
64{
65	bourne_shell,
66	"csh"
67};
68
69static char    *default_groups[_UC_MAXGROUPS] =
70{
71	NULL
72};
73
74static char const *booltrue[] =
75{
76	"yes", "true", "1", "on", NULL
77};
78static char const *boolfalse[] =
79{
80	"no", "false", "0", "off", NULL
81};
82
83static struct userconf config =
84{
85	0,			/* Default password for new users? (nologin) */
86	0,			/* Reuse uids? */
87	0,			/* Reuse gids? */
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	default_groups,		/* 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# Obtain default dotfiles from this directory\n",
111	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
112	"\n# Log add/change/remove information in this file\n",
113	"\n# Root directory in which $HOME directory is created\n",
114	"\n# Colon separated list of directories containing valid shells\n",
115	"\n# Space separated list of available shells (without paths)\n",
116	"\n# Default shell (without path)\n",
117	"\n# Default group (leave blank for new group per user)\n",
118	"\n# Extra groups for new users\n",
119	"\n# Default login class for new users\n",
120	"\n# Range of valid default user ids\n",
121	NULL,
122	"\n# Range of valid default group ids\n",
123	NULL,
124	"\n# Days after which account expires (0=disabled)\n",
125	"\n# Days after which password expires (0=disabled)\n"
126};
127
128static char const *kwds[] =
129{
130	"",
131	"defaultpasswd",
132	"reuseuids",
133	"reusegids",
134	"skeleton",
135	"newmail",
136	"logfile",
137	"home",
138	"shellpath",
139	"shells",
140	"defaultshell",
141	"defaultgroup",
142	"extragroups",
143	"defaultclass",
144	"minuid",
145	"maxuid",
146	"mingid",
147	"maxgid",
148	"expire_days",
149	"password_days",
150	NULL
151};
152
153static char    *
154unquote(char const * str)
155{
156	if (str && (*str == '"' || *str == '\'')) {
157		char           *p = strchr(str + 1, *str);
158
159		if (p != NULL)
160			*p = '\0';
161		return (char *) (*++str ? str : NULL);
162	}
163	return (char *) str;
164}
165
166int
167boolean_val(char const * str, int dflt)
168{
169	if ((str = unquote(str)) != NULL) {
170		int             i;
171
172		for (i = 0; booltrue[i]; i++)
173			if (strcmp(str, booltrue[i]) == 0)
174				return 1;
175		for (i = 0; boolfalse[i]; i++)
176			if (strcmp(str, boolfalse[i]) == 0)
177				return 0;
178
179		/*
180		 * Special cases for defaultpassword
181		 */
182		if (strcmp(str, "random") == 0)
183			return -1;
184		if (strcmp(str, "none") == 0)
185			return -2;
186	}
187	return dflt;
188}
189
190char const     *
191boolean_str(int val)
192{
193	if (val == -1)
194		return "random";
195	else if (val == -2)
196		return "none";
197	else
198		return val ? booltrue[0] : boolfalse[0];
199}
200
201char           *
202newstr(char const * p)
203{
204	char           *q = NULL;
205
206	if ((p = unquote(p)) != NULL) {
207		int             l = strlen(p) + 1;
208
209		if ((q = malloc(l)) != NULL)
210			memcpy(q, p, l);
211	}
212	return q;
213}
214
215
216struct userconf *
217read_userconfig(char const * file)
218{
219	FILE           *fp;
220
221	if (file == NULL)
222		file = _PATH_PW_CONF;
223	if ((fp = fopen(file, "r")) != NULL) {
224		char            buf[_UC_MAXLINE];
225
226		while (fgets(buf, sizeof buf, fp) != NULL) {
227			char           *p = strchr(buf, '\n');
228
229			if (p == NULL) {	/* Line too long */
230				int             ch;
231
232				while ((ch = fgetc(fp)) != '\n' && ch != EOF);
233			} else {
234				*p = '\0';
235				if (*buf && *buf != '\n' && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
236					static char const toks[] = " \t\r\n,=";
237					char           *q = strtok(NULL, toks);
238					int             i = 0;
239
240					while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
241						++i;
242#if debugging
243					if (i == _UC_FIELDS)
244						printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
245					else
246						printf("Got kwd[%s]=%s\n", p, q);
247#endif
248					switch (i) {
249					case _UC_DEFAULTPWD:
250						config.default_password = boolean_val(q, 1);
251						break;
252					case _UC_REUSEUID:
253						config.reuse_uids = boolean_val(q, 0);
254						break;
255					case _UC_REUSEGID:
256						config.reuse_gids = boolean_val(q, 0);
257						break;
258					case _UC_DOTDIR:
259						config.dotdir = (q == NULL || !boolean_val(q, 1))
260							? NULL : newstr(q);
261						break;
262					case _UC_NEWMAIL:
263						config.newmail = (q == NULL || !boolean_val(q, 1))
264							? NULL : newstr(q);
265						break;
266					case _UC_LOGFILE:
267						config.logfile = (q == NULL || !boolean_val(q, 1))
268							? NULL : newstr(q);
269						break;
270					case _UC_HOMEROOT:
271						config.home = (q == NULL || !boolean_val(q, 1))
272							? "/home" : newstr(q);
273						break;
274					case _UC_SHELLPATH:
275						config.shelldir = (q == NULL || !boolean_val(q, 1))
276							? "/bin" : newstr(q);
277						break;
278					case _UC_SHELLS:
279						for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
280							system_shells[i] = newstr(q);
281						if (i > 0)
282							while (i < _UC_MAXSHELLS)
283								system_shells[i++] = NULL;
284						break;
285					case _UC_DEFAULTSHELL:
286						config.shell_default = (q == NULL || !boolean_val(q, 1))
287							? (char *) bourne_shell : newstr(q);
288						break;
289					case _UC_DEFAULTGROUP:
290						config.default_group = (q == NULL || !boolean_val(q, 1) || getgrnam(q) == NULL)
291							? NULL : newstr(q);
292						break;
293					case _UC_EXTRAGROUPS:
294						for (i = 0; i < _UC_MAXGROUPS && q != NULL; i++, q = strtok(NULL, toks))
295							default_groups[i] = newstr(q);
296						if (i > 0)
297							while (i < _UC_MAXGROUPS)
298								default_groups[i++] = NULL;
299						break;
300					case _UC_DEFAULTCLASS:
301						config.default_class = (q == NULL || !boolean_val(q, 1))
302							? NULL : newstr(q);
303						break;
304					case _UC_MINUID:
305						if ((q = unquote(q)) != NULL && isdigit(*q))
306							config.min_uid = (uid_t) atol(q);
307						break;
308					case _UC_MAXUID:
309						if ((q = unquote(q)) != NULL && isdigit(*q))
310							config.max_uid = (uid_t) atol(q);
311						break;
312					case _UC_MINGID:
313						if ((q = unquote(q)) != NULL && isdigit(*q))
314							config.min_gid = (gid_t) atol(q);
315						break;
316					case _UC_MAXGID:
317						if ((q = unquote(q)) != NULL && isdigit(*q))
318							config.max_gid = (gid_t) atol(q);
319						break;
320					case _UC_EXPIRE:
321						if ((q = unquote(q)) != NULL && isdigit(*q))
322							config.expire_days = atoi(q);
323						break;
324					case _UC_PASSWORD:
325						if ((q = unquote(q)) != NULL && isdigit(*q))
326							config.password_days = atoi(q);
327						break;
328					case _UC_FIELDS:
329					case _UC_NONE:
330						break;
331					}
332				}
333			}
334		}
335		fclose(fp);
336	}
337	return &config;
338}
339
340
341int
342write_userconfig(char const * file)
343{
344	int             fd;
345
346	if (file == NULL)
347		file = _PATH_PW_CONF;
348
349	if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
350		FILE           *fp;
351
352		if ((fp = fdopen(fd, "w")) == NULL)
353			close(fd);
354		else {
355			int             i, j, k;
356			char            buf[_UC_MAXLINE];
357
358			for (i = _UC_NONE; i < _UC_FIELDS; i++) {
359				int             quote = 1;
360				char const     *val = buf;
361
362				*buf = '\0';
363				switch (i) {
364				case _UC_DEFAULTPWD:
365					val = boolean_str(config.default_password);
366					break;
367				case _UC_REUSEUID:
368					val = boolean_str(config.reuse_uids);
369					break;
370				case _UC_REUSEGID:
371					val = boolean_str(config.reuse_gids);
372					break;
373				case _UC_DOTDIR:
374					val = config.dotdir ? config.dotdir : boolean_str(0);
375					break;
376				case _UC_NEWMAIL:
377					val = config.newmail ? config.newmail : boolean_str(0);
378					break;
379				case _UC_LOGFILE:
380					val = config.logfile ? config.logfile : boolean_str(0);
381					break;
382				case _UC_HOMEROOT:
383					val = config.home;
384					break;
385				case _UC_SHELLPATH:
386					val = config.shelldir;
387					break;
388				case _UC_SHELLS:
389					for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++)
390						k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", system_shells[j]);
391					quote = 0;
392					break;
393				case _UC_DEFAULTSHELL:
394					val = config.shell_default ? config.shell_default : bourne_shell;
395					break;
396				case _UC_DEFAULTGROUP:
397					val = config.default_group ? config.default_group : "";
398					break;
399				case _UC_EXTRAGROUPS:
400					for (j = k = 0; j < _UC_MAXGROUPS && default_groups[j] != NULL; j++)
401						k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", default_groups[j]);
402					quote = 0;
403					break;
404				case _UC_DEFAULTCLASS:
405					val = config.default_class ? config.default_class : "";
406					break;
407				case _UC_MINUID:
408					sprintf(buf, "%lu", (unsigned long) config.min_uid);
409					quote = 0;
410					break;
411				case _UC_MAXUID:
412					sprintf(buf, "%lu", (unsigned long) config.max_uid);
413					quote = 0;
414					break;
415				case _UC_MINGID:
416					sprintf(buf, "%lu", (unsigned long) config.min_gid);
417					quote = 0;
418					break;
419				case _UC_MAXGID:
420					sprintf(buf, "%lu", (unsigned long) config.max_gid);
421					quote = 0;
422					break;
423				case _UC_EXPIRE:
424					sprintf(buf, "%d", config.expire_days);
425					quote = 0;
426					break;
427				case _UC_PASSWORD:
428					sprintf(buf, "%d", config.password_days);
429					quote = 0;
430					break;
431				case _UC_NONE:
432					break;
433				}
434
435				if (comments[i])
436					fputs(comments[i], fp);
437
438				if (*kwds[i]) {
439					if (quote)
440						fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
441					else
442						fprintf(fp, "%s = %s\n", kwds[i], val);
443#if debugging
444					printf("WROTE: %s = %s\n", kwds[i], val);
445#endif
446				}
447			}
448			return fclose(fp) != EOF;
449		}
450	}
451	return 0;
452}
453