pw_conf.c revision 20253
1281843Sdteske/*-
2120031Sscottl * Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
3281843Sdteske * All rights reserved.
4115410Sscottl *
5222417Sjulian * Redistribution and use in source and binary forms, with or without
6115410Sscottl * modification, are permitted provided that the following conditions
7115410Sscottl * are met:
8115410Sscottl * 1. Redistributions of source code must retain the above copyright
9115410Sscottl *    notice, this list of conditions and the following disclaimer as
10115410Sscottl *    the first lines of this file unmodified.
11115410Sscottl * 2. Redistributions in binary form must reproduce the above copyright
12115410Sscottl *    notice, this list of conditions and the following disclaimer in the
13115410Sscottl *    documentation and/or other materials provided with the distribution.
14222417Sjulian * 3. All advertising materials mentioning features or use of this software
15115410Sscottl *    must display the following acknowledgement:
16115410Sscottl *	This product includes software developed by David L. Nugent.
17115410Sscottl * 4. The name of the author may not be used to endorse or promote products
18115410Sscottl *    derived from this software without specific prior written permission.
19115410Sscottl *
20115410Sscottl * THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
21115410Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22115410Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23115410Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
24115410Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25115410Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26222417Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27115410Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28115410Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29115410Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30115410Sscottl * SUCH DAMAGE.
31281843Sdteske *
32262701Sdteske *	$Id$
33222417Sjulian */
34222417Sjulian
35115410Sscottl#include <string.h>
36222417Sjulian#include <ctype.h>
37222417Sjulian#include <fcntl.h>
38222417Sjulian
39115410Sscottl#include "pw.h"
40222417Sjulian
41222417Sjulian#define debugging 0
42222417Sjulian
43222417Sjulianenum {
44281843Sdteske	_UC_NONE,
45281843Sdteske	_UC_DEFAULTPWD,
46281843Sdteske	_UC_REUSEUID,
47222417Sjulian	_UC_REUSEGID,
48222417Sjulian	_UC_DOTDIR,
49222417Sjulian	_UC_NEWMAIL,
50222417Sjulian	_UC_LOGFILE,
51222417Sjulian	_UC_HOMEROOT,
52281843Sdteske	_UC_SHELLPATH,
53222417Sjulian	_UC_SHELLS,
54222417Sjulian	_UC_DEFAULTSHELL,
55281843Sdteske	_UC_DEFAULTGROUP,
56115410Sscottl	_UC_EXTRAGROUPS,
57281843Sdteske	_UC_DEFAULTCLASS,
58281843Sdteske	_UC_MINUID,
59281843Sdteske	_UC_MAXUID,
60281843Sdteske	_UC_MINGID,
61262703Sdteske	_UC_MAXGID,
62281843Sdteske	_UC_EXPIRE,
63281843Sdteske	_UC_PASSWORD,
64281843Sdteske	_UC_FIELDS
65281843Sdteske};
66281843Sdteske
67281843Sdteskestatic char     bourne_shell[] = "sh";
68281843Sdteske
69281843Sdteskestatic char    *system_shells[_UC_MAXSHELLS] =
70281843Sdteske{
71115410Sscottl	bourne_shell,
72281843Sdteske	"csh"
73281843Sdteske};
74115410Sscottl
75281843Sdteskestatic char    *default_groups[_UC_MAXGROUPS] =
76281843Sdteske{
77281843Sdteske	NULL
78281843Sdteske};
79281843Sdteske
80281843Sdteskestatic char const *booltrue[] =
81281843Sdteske{
82115410Sscottl	"yes", "true", "1", "on", NULL
83115410Sscottl};
84115410Sscottlstatic char const *boolfalse[] =
85281843Sdteske{
86222417Sjulian	"no", "false", "0", "off", NULL
87222417Sjulian};
88281843Sdteske
89116175Sscottlstatic struct userconf config =
90262701Sdteske{
91294417Sroyger	0,			/* Default password for new users? (nologin) */
92262701Sdteske	0,			/* Reuse uids? */
93262701Sdteske	0,			/* Reuse gids? */
94262701Sdteske	"/usr/share/skel",	/* Where to obtain skeleton files */
95262701Sdteske	NULL,			/* Mail to send to new accounts */
96116175Sscottl	"/var/log/userlog",	/* Where to log changes */
97281843Sdteske	"/home",		/* Where to create home directory */
98222417Sjulian	"/bin",			/* Where shells are located */
99281843Sdteske	system_shells,		/* List of shells (first is default) */
100222417Sjulian	bourne_shell,		/* Default shell */
101222417Sjulian	NULL,			/* Default group name */
102115410Sscottl	default_groups,		/* Default (additional) groups */
103222417Sjulian	NULL,			/* Default login class */
104222417Sjulian	1000, 32000,		/* Allowed range of uids */
105222417Sjulian	1000, 32000,		/* Allowed range of gids */
106222417Sjulian	0,			/* Days until account expires */
107115410Sscottl	0			/* Days until password expires */
108115410Sscottl};
109262701Sdteske
110281843Sdteskestatic char const *comments[_UC_FIELDS] =
111{
112	"#\n# pw.conf - user/group configuration defaults\n#\n",
113	"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
114	"\n# Reuse gaps in uid sequence? (yes or no)\n",
115	"\n# Reuse gaps in gid sequence? (yes or no)\n",
116	"\n# Obtain default dotfiles from this directory\n",
117	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
118	"\n# Log add/change/remove information in this file\n",
119	"\n# Root directory in which $HOME directory is created\n",
120	"\n# Colon separated list of directories containing valid shells\n",
121	"\n# Space separated list of available shells (without paths)\n",
122	"\n# Default shell (without path)\n",
123	"\n# Default group (leave blank for new group per user)\n",
124	"\n# Extra groups for new users\n",
125	"\n# Default login class for new users\n",
126	"\n# Range of valid default user ids\n",
127	NULL,
128	"\n# Range of valid default group ids\n",
129	NULL,
130	"\n# Days after which account expires (0=disabled)\n",
131	"\n# Days after which password expires (0=disabled)\n"
132};
133
134static char const *kwds[] =
135{
136	"",
137	"defaultpasswd",
138	"reuseuids",
139	"reusegids",
140	"skeleton",
141	"newmail",
142	"logfile",
143	"home",
144	"shellpath",
145	"shells",
146	"defaultshell",
147	"defaultgroup",
148	"extragroups",
149	"defaultclass",
150	"minuid",
151	"maxuid",
152	"mingid",
153	"maxgid",
154	"expire_days",
155	"password_days",
156	NULL
157};
158
159static char    *
160unquote(char const * str)
161{
162	if (str && (*str == '"' || *str == '\'')) {
163		char           *p = strchr(str + 1, *str);
164
165		if (p != NULL)
166			*p = '\0';
167		return (char *) (*++str ? str : NULL);
168	}
169	return (char *) str;
170}
171
172int
173boolean_val(char const * str, int dflt)
174{
175	if ((str = unquote(str)) != NULL) {
176		int             i;
177
178		for (i = 0; booltrue[i]; i++)
179			if (strcmp(str, booltrue[i]) == 0)
180				return 1;
181		for (i = 0; boolfalse[i]; i++)
182			if (strcmp(str, boolfalse[i]) == 0)
183				return 0;
184
185		/*
186		 * Special cases for defaultpassword
187		 */
188		if (strcmp(str, "random") == 0)
189			return -1;
190		if (strcmp(str, "none") == 0)
191			return -2;
192	}
193	return dflt;
194}
195
196char const     *
197boolean_str(int val)
198{
199	if (val == -1)
200		return "random";
201	else if (val == -2)
202		return "none";
203	else
204		return val ? booltrue[0] : boolfalse[0];
205}
206
207char           *
208newstr(char const * p)
209{
210	char           *q = NULL;
211
212	if ((p = unquote(p)) != NULL) {
213		int             l = strlen(p) + 1;
214
215		if ((q = malloc(l)) != NULL)
216			memcpy(q, p, l);
217	}
218	return q;
219}
220
221
222struct userconf *
223read_userconfig(char const * file)
224{
225	FILE           *fp;
226
227	if (file == NULL)
228		file = _PATH_PW_CONF;
229	if ((fp = fopen(file, "r")) != NULL) {
230		char            buf[_UC_MAXLINE];
231
232		while (fgets(buf, sizeof buf, fp) != NULL) {
233			char           *p = strchr(buf, '\n');
234
235			if (p == NULL) {	/* Line too long */
236				int             ch;
237
238				while ((ch = fgetc(fp)) != '\n' && ch != EOF);
239			} else {
240				*p = '\0';
241				if (*buf && *buf != '\n' && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
242					static char const toks[] = " \t\r\n,=";
243					char           *q = strtok(NULL, toks);
244					int             i = 0;
245
246					while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
247						++i;
248#if debugging
249					if (i == _UC_FIELDS)
250						printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
251					else
252						printf("Got kwd[%s]=%s\n", p, q);
253#endif
254					switch (i) {
255					case _UC_DEFAULTPWD:
256						config.default_password = boolean_val(q, 1);
257						break;
258					case _UC_REUSEUID:
259						config.reuse_uids = boolean_val(q, 0);
260						break;
261					case _UC_REUSEGID:
262						config.reuse_gids = boolean_val(q, 0);
263						break;
264					case _UC_DOTDIR:
265						config.dotdir = (q == NULL || !boolean_val(q, 1))
266							? NULL : newstr(q);
267						break;
268					case _UC_NEWMAIL:
269						config.newmail = (q == NULL || !boolean_val(q, 1))
270							? NULL : newstr(q);
271						break;
272					case _UC_LOGFILE:
273						config.logfile = (q == NULL || !boolean_val(q, 1))
274							? NULL : newstr(q);
275						break;
276					case _UC_HOMEROOT:
277						config.home = (q == NULL || !boolean_val(q, 1))
278							? "/home" : newstr(q);
279						break;
280					case _UC_SHELLPATH:
281						config.shelldir = (q == NULL || !boolean_val(q, 1))
282							? "/bin" : newstr(q);
283						break;
284					case _UC_SHELLS:
285						for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
286							system_shells[i] = newstr(q);
287						if (i > 0)
288							while (i < _UC_MAXSHELLS)
289								system_shells[i++] = NULL;
290						break;
291					case _UC_DEFAULTSHELL:
292						config.shell_default = (q == NULL || !boolean_val(q, 1))
293							? (char *) bourne_shell : newstr(q);
294						break;
295					case _UC_DEFAULTGROUP:
296						config.default_group = (q == NULL || !boolean_val(q, 1) || getgrnam(q) == NULL)
297							? NULL : newstr(q);
298						break;
299					case _UC_EXTRAGROUPS:
300						for (i = 0; i < _UC_MAXGROUPS && q != NULL; i++, q = strtok(NULL, toks))
301							default_groups[i] = newstr(q);
302						if (i > 0)
303							while (i < _UC_MAXGROUPS)
304								default_groups[i++] = NULL;
305						break;
306					case _UC_DEFAULTCLASS:
307						config.default_class = (q == NULL || !boolean_val(q, 1))
308							? NULL : newstr(q);
309						break;
310					case _UC_MINUID:
311						if ((q = unquote(q)) != NULL && isdigit(*q))
312							config.min_uid = (uid_t) atol(q);
313						break;
314					case _UC_MAXUID:
315						if ((q = unquote(q)) != NULL && isdigit(*q))
316							config.max_uid = (uid_t) atol(q);
317						break;
318					case _UC_MINGID:
319						if ((q = unquote(q)) != NULL && isdigit(*q))
320							config.min_gid = (gid_t) atol(q);
321						break;
322					case _UC_MAXGID:
323						if ((q = unquote(q)) != NULL && isdigit(*q))
324							config.max_gid = (gid_t) atol(q);
325						break;
326					case _UC_EXPIRE:
327						if ((q = unquote(q)) != NULL && isdigit(*q))
328							config.expire_days = atoi(q);
329						break;
330					case _UC_PASSWORD:
331						if ((q = unquote(q)) != NULL && isdigit(*q))
332							config.password_days = atoi(q);
333						break;
334					case _UC_FIELDS:
335					case _UC_NONE:
336						break;
337					}
338				}
339			}
340		}
341		fclose(fp);
342	}
343	return &config;
344}
345
346
347int
348write_userconfig(char const * file)
349{
350	int             fd;
351
352	if (file == NULL)
353		file = _PATH_PW_CONF;
354
355	if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
356		FILE           *fp;
357
358		if ((fp = fdopen(fd, "w")) == NULL)
359			close(fd);
360		else {
361			int             i, j, k;
362			char            buf[_UC_MAXLINE];
363
364			for (i = _UC_NONE; i < _UC_FIELDS; i++) {
365				int             quote = 1;
366				char const     *val = buf;
367
368				*buf = '\0';
369				switch (i) {
370				case _UC_DEFAULTPWD:
371					val = boolean_str(config.default_password);
372					break;
373				case _UC_REUSEUID:
374					val = boolean_str(config.reuse_uids);
375					break;
376				case _UC_REUSEGID:
377					val = boolean_str(config.reuse_gids);
378					break;
379				case _UC_DOTDIR:
380					val = config.dotdir ? config.dotdir : boolean_str(0);
381					break;
382				case _UC_NEWMAIL:
383					val = config.newmail ? config.newmail : boolean_str(0);
384					break;
385				case _UC_LOGFILE:
386					val = config.logfile ? config.logfile : boolean_str(0);
387					break;
388				case _UC_HOMEROOT:
389					val = config.home;
390					break;
391				case _UC_SHELLPATH:
392					val = config.shelldir;
393					break;
394				case _UC_SHELLS:
395					for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++)
396						k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", system_shells[j]);
397					quote = 0;
398					break;
399				case _UC_DEFAULTSHELL:
400					val = config.shell_default ? config.shell_default : bourne_shell;
401					break;
402				case _UC_DEFAULTGROUP:
403					val = config.default_group ? config.default_group : "";
404					break;
405				case _UC_EXTRAGROUPS:
406					for (j = k = 0; j < _UC_MAXGROUPS && default_groups[j] != NULL; j++)
407						k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", default_groups[j]);
408					quote = 0;
409					break;
410				case _UC_DEFAULTCLASS:
411					val = config.default_class ? config.default_class : "";
412					break;
413				case _UC_MINUID:
414					sprintf(buf, "%lu", (unsigned long) config.min_uid);
415					quote = 0;
416					break;
417				case _UC_MAXUID:
418					sprintf(buf, "%lu", (unsigned long) config.max_uid);
419					quote = 0;
420					break;
421				case _UC_MINGID:
422					sprintf(buf, "%lu", (unsigned long) config.min_gid);
423					quote = 0;
424					break;
425				case _UC_MAXGID:
426					sprintf(buf, "%lu", (unsigned long) config.max_gid);
427					quote = 0;
428					break;
429				case _UC_EXPIRE:
430					sprintf(buf, "%d", config.expire_days);
431					quote = 0;
432					break;
433				case _UC_PASSWORD:
434					sprintf(buf, "%d", config.password_days);
435					quote = 0;
436					break;
437				case _UC_NONE:
438					break;
439				}
440
441				if (comments[i])
442					fputs(comments[i], fp);
443
444				if (*kwds[i]) {
445					if (quote)
446						fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
447					else
448						fprintf(fp, "%s = %s\n", kwds[i], val);
449#if debugging
450					printf("WROTE: %s = %s\n", kwds[i], val);
451#endif
452				}
453			}
454			return fclose(fp) != EOF;
455		}
456	}
457	return 0;
458}
459