pw_conf.c revision 44229
1228753Smm/*-
2228753Smm * Copyright (C) 1996
3228753Smm *	David L. Nugent.  All rights reserved.
4228753Smm *
5228753Smm * Redistribution and use in source and binary forms, with or without
6228753Smm * modification, are permitted provided that the following conditions
7228753Smm * are met:
8228753Smm * 1. Redistributions of source code must retain the above copyright
9228753Smm *    notice, this list of conditions and the following disclaimer.
10228753Smm * 2. Redistributions in binary form must reproduce the above copyright
11228753Smm *    notice, this list of conditions and the following disclaimer in the
12228753Smm *    documentation and/or other materials provided with the distribution.
13228753Smm *
14228753Smm * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15228753Smm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16228753Smm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17228753Smm * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18228753Smm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19228753Smm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20228753Smm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21228753Smm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22228753Smm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23228753Smm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24228753Smm * SUCH DAMAGE.
25229592Smm */
26228753Smm
27228753Smm#ifndef lint
28228753Smmstatic const char rcsid[] =
29228753Smm	"$Id: pw_conf.c,v 1.7 1997/10/10 06:23:36 charnier Exp $";
30228753Smm#endif /* not lint */
31228753Smm
32228753Smm#include <string.h>
33228753Smm#include <ctype.h>
34228753Smm#include <fcntl.h>
35228753Smm
36228753Smm#include "pw.h"
37228753Smm
38228753Smm#define debugging 0
39228753Smm
40228753Smmenum {
41228753Smm	_UC_NONE,
42228753Smm	_UC_DEFAULTPWD,
43228753Smm	_UC_REUSEUID,
44228753Smm	_UC_REUSEGID,
45228753Smm	_UC_NISPASSWD,
46228753Smm	_UC_DOTDIR,
47228753Smm	_UC_NEWMAIL,
48228753Smm	_UC_LOGFILE,
49228753Smm	_UC_HOMEROOT,
50228753Smm	_UC_SHELLPATH,
51228753Smm	_UC_SHELLS,
52228753Smm	_UC_DEFAULTSHELL,
53228753Smm	_UC_DEFAULTGROUP,
54228753Smm	_UC_EXTRAGROUPS,
55228753Smm	_UC_DEFAULTCLASS,
56228753Smm	_UC_MINUID,
57228753Smm	_UC_MAXUID,
58228753Smm	_UC_MINGID,
59228753Smm	_UC_MAXGID,
60228753Smm	_UC_EXPIRE,
61228753Smm	_UC_PASSWORD,
62228753Smm	_UC_FIELDS
63228753Smm};
64228753Smm
65228753Smmstatic char     bourne_shell[] = "sh";
66228753Smm
67228753Smmstatic char    *system_shells[_UC_MAXSHELLS] =
68228753Smm{
69228753Smm	bourne_shell,
70228753Smm	"csh"
71228753Smm};
72228753Smm
73228753Smmstatic char const *booltrue[] =
74228753Smm{
75228753Smm	"yes", "true", "1", "on", NULL
76228753Smm};
77228753Smmstatic char const *boolfalse[] =
78228753Smm{
79228753Smm	"no", "false", "0", "off", NULL
80228753Smm};
81228753Smm
82228753Smmstatic struct userconf config =
83228753Smm{
84228753Smm	0,			/* Default password for new users? (nologin) */
85228753Smm	0,			/* Reuse uids? */
86228753Smm	0,			/* Reuse gids? */
87228753Smm	NULL,			/* NIS version of the passwd file */
88228753Smm	"/usr/share/skel",	/* Where to obtain skeleton files */
89228753Smm	NULL,			/* Mail to send to new accounts */
90228753Smm	"/var/log/userlog",	/* Where to log changes */
91228753Smm	"/home",		/* Where to create home directory */
92228753Smm	"/bin",			/* Where shells are located */
93228753Smm	system_shells,		/* List of shells (first is default) */
94228753Smm	bourne_shell,		/* Default shell */
95228753Smm	NULL,			/* Default group name */
96228753Smm	NULL,			/* Default (additional) groups */
97228753Smm	NULL,			/* Default login class */
98228753Smm	1000, 32000,		/* Allowed range of uids */
99228753Smm	1000, 32000,		/* Allowed range of gids */
100228753Smm	0,			/* Days until account expires */
101228753Smm	0			/* Days until password expires */
102228753Smm};
103228753Smm
104228753Smmstatic char const *comments[_UC_FIELDS] =
105228753Smm{
106228753Smm	"#\n# pw.conf - user/group configuration defaults\n#\n",
107228753Smm	"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
108228753Smm	"\n# Reuse gaps in uid sequence? (yes or no)\n",
109228753Smm	"\n# Reuse gaps in gid sequence? (yes or no)\n",
110228753Smm	"\n# Path to the NIS passwd file (blank or 'no' for none)\n",
111228753Smm	"\n# Obtain default dotfiles from this directory\n",
112228753Smm	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
113228753Smm	"\n# Log add/change/remove information in this file\n",
114228753Smm	"\n# Root directory in which $HOME directory is created\n",
115228753Smm	"\n# Colon separated list of directories containing valid shells\n",
116228753Smm	"\n# Space separated list of available shells (without paths)\n",
117228753Smm	"\n# Default shell (without path)\n",
118228753Smm	"\n# Default group (leave blank for new group per user)\n",
119228753Smm	"\n# Extra groups for new users\n",
120228753Smm	"\n# Default login class for new users\n",
121228753Smm	"\n# Range of valid default user ids\n",
122228753Smm	NULL,
123228753Smm	"\n# Range of valid default group ids\n",
124228753Smm	NULL,
125228753Smm	"\n# Days after which account expires (0=disabled)\n",
126228753Smm	"\n# Days after which password expires (0=disabled)\n"
127228753Smm};
128228753Smm
129228753Smmstatic char const *kwds[] =
130228753Smm{
131228753Smm	"",
132228753Smm	"defaultpasswd",
133228753Smm	"reuseuids",
134228753Smm	"reusegids",
135228753Smm	"nispasswd",
136228753Smm	"skeleton",
137228753Smm	"newmail",
138228753Smm	"logfile",
139228753Smm	"home",
140228753Smm	"shellpath",
141228753Smm	"shells",
142228753Smm	"defaultshell",
143228753Smm	"defaultgroup",
144228753Smm	"extragroups",
145228753Smm	"defaultclass",
146228753Smm	"minuid",
147228753Smm	"maxuid",
148228753Smm	"mingid",
149228753Smm	"maxgid",
150228753Smm	"expire_days",
151228753Smm	"password_days",
152228753Smm	NULL
153228753Smm};
154228753Smm
155228753Smmstatic char    *
156228753Smmunquote(char const * str)
157228753Smm{
158228753Smm	if (str && (*str == '"' || *str == '\'')) {
159228753Smm		char           *p = strchr(str + 1, *str);
160228753Smm
161228753Smm		if (p != NULL)
162228753Smm			*p = '\0';
163228753Smm		return (char *) (*++str ? str : NULL);
164228753Smm	}
165228753Smm	return (char *) str;
166228753Smm}
167228753Smm
168228753Smmint
169228753Smmboolean_val(char const * str, int dflt)
170228753Smm{
171228753Smm	if ((str = unquote(str)) != NULL) {
172228753Smm		int             i;
173228753Smm
174228753Smm		for (i = 0; booltrue[i]; i++)
175228753Smm			if (strcmp(str, booltrue[i]) == 0)
176228753Smm				return 1;
177228753Smm		for (i = 0; boolfalse[i]; i++)
178228753Smm			if (strcmp(str, boolfalse[i]) == 0)
179228753Smm				return 0;
180228753Smm
181228753Smm		/*
182228753Smm		 * Special cases for defaultpassword
183228753Smm		 */
184228753Smm		if (strcmp(str, "random") == 0)
185228753Smm			return -1;
186228753Smm		if (strcmp(str, "none") == 0)
187228753Smm			return -2;
188228753Smm	}
189228753Smm	return dflt;
190228753Smm}
191228753Smm
192228753Smmchar const     *
193228753Smmboolean_str(int val)
194228753Smm{
195228753Smm	if (val == -1)
196228753Smm		return "random";
197228753Smm	else if (val == -2)
198228753Smm		return "none";
199228753Smm	else
200228753Smm		return val ? booltrue[0] : boolfalse[0];
201228753Smm}
202228753Smm
203228753Smmchar           *
204228753Smmnewstr(char const * p)
205228753Smm{
206228753Smm	char           *q = NULL;
207228753Smm
208228753Smm	if ((p = unquote(p)) != NULL) {
209228753Smm		int             l = strlen(p) + 1;
210228753Smm
211228753Smm		if ((q = malloc(l)) != NULL)
212228753Smm			memcpy(q, p, l);
213228753Smm	}
214228753Smm	return q;
215228753Smm}
216228753Smm
217228753Smm#define LNBUFSZ 1024
218228753Smm
219228753Smm
220228753Smmstruct userconf *
221228753Smmread_userconfig(char const * file)
222228753Smm{
223228753Smm	FILE           *fp;
224228753Smm
225228753Smm	extendarray(&config.groups, &config.numgroups, 200);
226228753Smm	memset(config.groups, 0, config.numgroups * sizeof(char *));
227228753Smm	if (file == NULL)
228228753Smm		file = _PATH_PW_CONF;
229228753Smm	if ((fp = fopen(file, "r")) != NULL) {
230228753Smm		int	    buflen = LNBUFSZ;
231228753Smm		char       *buf = malloc(buflen);
232228753Smm
233228753Smm	nextline:
234228753Smm		while (fgets(buf, buflen, fp) != NULL) {
235228753Smm			char           *p;
236228753Smm
237228753Smm			while ((p = strchr(buf, '\n')) == NULL) {
238228753Smm				int	  l;
239228753Smm				if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
240228753Smm					int	ch;
241228753Smm					while ((ch = fgetc(fp)) != '\n' && ch != EOF);
242228753Smm					goto nextline;	/* Ignore it */
243228753Smm				}
244228753Smm				l = strlen(buf);
245228753Smm				if (fgets(buf + l, buflen - l, fp) == NULL)
246228753Smm					break;	/* Unterminated last line */
247228753Smm			}
248228753Smm
249228753Smm			if (p != NULL)
250228753Smm				*p = '\0';
251228753Smm
252228753Smm			if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
253228753Smm				static char const toks[] = " \t\r\n,=";
254228753Smm				char           *q = strtok(NULL, toks);
255228753Smm				int             i = 0;
256228753Smm
257228753Smm				while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
258228753Smm					++i;
259228753Smm#if debugging
260228753Smm				if (i == _UC_FIELDS)
261228753Smm					printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
262228753Smm				else
263228753Smm					printf("Got kwd[%s]=%s\n", p, q);
264228753Smm#endif
265228753Smm				switch (i) {
266228753Smm				case _UC_DEFAULTPWD:
267228753Smm					config.default_password = boolean_val(q, 1);
268228753Smm					break;
269228753Smm				case _UC_REUSEUID:
270228753Smm					config.reuse_uids = boolean_val(q, 0);
271228753Smm					break;
272228753Smm				case _UC_REUSEGID:
273228753Smm					config.reuse_gids = boolean_val(q, 0);
274228753Smm					break;
275228753Smm				case _UC_NISPASSWD:
276228753Smm					config.nispasswd = (q == NULL || !boolean_val(q, 1))
277228753Smm						? NULL : newstr(q);
278228753Smm					break;
279228753Smm				case _UC_DOTDIR:
280228753Smm					config.dotdir = (q == NULL || !boolean_val(q, 1))
281228753Smm						? NULL : newstr(q);
282228753Smm					break;
283228753Smm				case _UC_NEWMAIL:
284228753Smm					config.newmail = (q == NULL || !boolean_val(q, 1))
285228753Smm						? NULL : newstr(q);
286228753Smm					break;
287228753Smm				case _UC_LOGFILE:
288228753Smm					config.logfile = (q == NULL || !boolean_val(q, 1))
289228753Smm						? NULL : newstr(q);
290228753Smm					break;
291228753Smm				case _UC_HOMEROOT:
292228753Smm					config.home = (q == NULL || !boolean_val(q, 1))
293228753Smm						? "/home" : newstr(q);
294228753Smm					break;
295228753Smm				case _UC_SHELLPATH:
296228753Smm					config.shelldir = (q == NULL || !boolean_val(q, 1))
297228753Smm						? "/bin" : newstr(q);
298228753Smm					break;
299228753Smm				case _UC_SHELLS:
300228753Smm					for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
301229592Smm						system_shells[i] = newstr(q);
302229592Smm					if (i > 0)
303229592Smm						while (i < _UC_MAXSHELLS)
304229592Smm							system_shells[i++] = NULL;
305229592Smm					break;
306229592Smm				case _UC_DEFAULTSHELL:
307229592Smm					config.shell_default = (q == NULL || !boolean_val(q, 1))
308229592Smm						? (char *) bourne_shell : newstr(q);
309229592Smm					break;
310229592Smm				case _UC_DEFAULTGROUP:
311229592Smm					q = unquote(q);
312229592Smm					config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
313229592Smm						? NULL : newstr(q);
314229592Smm					break;
315229592Smm				case _UC_EXTRAGROUPS:
316229592Smm					for (i = 0; q != NULL; q = strtok(NULL, toks)) {
317229592Smm						if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
318229592Smm							config.groups[i++] = newstr(q);
319229592Smm					}
320229592Smm					if (i > 0)
321229592Smm						while (i < config.numgroups)
322229592Smm							config.groups[i++] = NULL;
323229592Smm					break;
324229592Smm				case _UC_DEFAULTCLASS:
325229592Smm					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