MFC:
[dragonfly.git] / usr.sbin / pw / pw_conf.c
blobf9e4723ccbd4b8d53158fedca517da52681fd34d
1 /*-
2 * Copyright (C) 1996
3 * David L. Nugent. All rights reserved.
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.
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.
26 * $FreeBSD: src/usr.sbin/pw/pw_conf.c,v 1.10.2.2 2001/01/14 08:41:19 dougb Exp $
27 * $DragonFly: src/usr.sbin/pw/pw_conf.c,v 1.2 2003/06/17 04:30:02 dillon Exp $
30 #include <string.h>
31 #include <ctype.h>
32 #include <fcntl.h>
34 #include "pw.h"
36 #define debugging 0
38 enum {
39 _UC_NONE,
40 _UC_DEFAULTPWD,
41 _UC_REUSEUID,
42 _UC_REUSEGID,
43 _UC_NISPASSWD,
44 _UC_DOTDIR,
45 _UC_NEWMAIL,
46 _UC_LOGFILE,
47 _UC_HOMEROOT,
48 _UC_SHELLPATH,
49 _UC_SHELLS,
50 _UC_DEFAULTSHELL,
51 _UC_DEFAULTGROUP,
52 _UC_EXTRAGROUPS,
53 _UC_DEFAULTCLASS,
54 _UC_MINUID,
55 _UC_MAXUID,
56 _UC_MINGID,
57 _UC_MAXGID,
58 _UC_EXPIRE,
59 _UC_PASSWORD,
60 _UC_FIELDS
63 static char bourne_shell[] = "sh";
65 static char *system_shells[_UC_MAXSHELLS] =
67 bourne_shell,
68 "csh",
69 "tcsh"
72 static char const *booltrue[] =
74 "yes", "true", "1", "on", NULL
76 static char const *boolfalse[] =
78 "no", "false", "0", "off", NULL
81 static struct userconf config =
83 0, /* Default password for new users? (nologin) */
84 0, /* Reuse uids? */
85 0, /* Reuse gids? */
86 NULL, /* NIS version of the passwd file */
87 "/usr/share/skel", /* Where to obtain skeleton files */
88 NULL, /* Mail to send to new accounts */
89 "/var/log/userlog", /* Where to log changes */
90 "/home", /* Where to create home directory */
91 "/bin", /* Where shells are located */
92 system_shells, /* List of shells (first is default) */
93 bourne_shell, /* Default shell */
94 NULL, /* Default group name */
95 NULL, /* Default (additional) groups */
96 NULL, /* Default login class */
97 1000, 32000, /* Allowed range of uids */
98 1000, 32000, /* Allowed range of gids */
99 0, /* Days until account expires */
100 0, /* Days until password expires */
101 0 /* size of default_group array */
104 static char const *comments[_UC_FIELDS] =
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# Comma 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"
129 static char const *kwds[] =
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
155 static char *
156 unquote(char const * str)
158 if (str && (*str == '"' || *str == '\'')) {
159 char *p = strchr(str + 1, *str);
161 if (p != NULL)
162 *p = '\0';
163 return (char *) (*++str ? str : NULL);
165 return (char *) str;
169 boolean_val(char const * str, int dflt)
171 if ((str = unquote(str)) != NULL) {
172 int i;
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;
182 * Special cases for defaultpassword
184 if (strcmp(str, "random") == 0)
185 return -1;
186 if (strcmp(str, "none") == 0)
187 return -2;
189 return dflt;
192 char const *
193 boolean_str(int val)
195 if (val == -1)
196 return "random";
197 else if (val == -2)
198 return "none";
199 else
200 return val ? booltrue[0] : boolfalse[0];
203 char *
204 newstr(char const * p)
206 char *q = NULL;
208 if ((p = unquote(p)) != NULL) {
209 int l = strlen(p) + 1;
211 if ((q = malloc(l)) != NULL)
212 memcpy(q, p, l);
214 return q;
217 #define LNBUFSZ 1024
220 struct userconf *
221 read_userconfig(char const * file)
223 FILE *fp;
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);
233 nextline:
234 while (fgets(buf, buflen, fp) != NULL) {
235 char *p;
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 */
244 l = strlen(buf);
245 if (fgets(buf + l, buflen - l, fp) == NULL)
246 break; /* Unterminated last line */
249 if (p != NULL)
250 *p = '\0';
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;
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);
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;
358 free(buf);
359 fclose(fp);
361 return &config;
366 write_userconfig(char const * file)
368 int fd;
370 if (file == NULL)
371 file = _PATH_PW_CONF;
373 if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
374 FILE *fp;
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);
383 for (i = _UC_NONE; i < _UC_FIELDS; i++) {
384 int quote = 1;
385 char const *val = buf;
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;
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;
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;
477 if (comments[i])
478 fputs(comments[i], fp);
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
490 free(buf);
491 return fclose(fp) != EOF;
494 return 0;