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