<sys/errno.h>: Remove EUNUSED*.
[dragonfly.git] / usr.sbin / pw / pw_conf.c
blob05c91fca3be23e62b13e61a22d199b3f8c345196
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.16 2011/03/08 20:13:29 jkim Exp $
29 #include <string.h>
30 #include <ctype.h>
31 #include <fcntl.h>
33 #include "pw.h"
35 #define debugging 0
37 enum {
38 _UC_NONE,
39 _UC_DEFAULTPWD,
40 _UC_REUSEUID,
41 _UC_REUSEGID,
42 _UC_NISPASSWD,
43 _UC_DOTDIR,
44 _UC_NEWMAIL,
45 _UC_LOGFILE,
46 _UC_HOMEROOT,
47 _UC_HOMEMODE,
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 _DEF_DIRMODE, /* Home directory perms, modified by umask */
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 NULL, /* 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 0 /* size of default_group array */
105 static char const *comments[_UC_FIELDS] =
107 "#\n# pw.conf - user/group configuration defaults\n#\n",
108 "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
109 "\n# Reuse gaps in uid sequence? (yes or no)\n",
110 "\n# Reuse gaps in gid sequence? (yes or no)\n",
111 "\n# Path to the NIS passwd file (blank or 'no' for none)\n",
112 "\n# Obtain default dotfiles from this directory\n",
113 "\n# Mail this file to new user (/etc/newuser.msg or no)\n",
114 "\n# Log add/change/remove information in this file\n",
115 "\n# Root directory in which $HOME directory is created\n",
116 "\n# Mode for the new $HOME directory, will be modified by umask\n",
117 "\n# Colon separated list of directories containing valid shells\n",
118 "\n# Comma separated list of available shells (without paths)\n",
119 "\n# Default shell (without path)\n",
120 "\n# Default group (leave blank for new group per user)\n",
121 "\n# Extra groups for new users\n",
122 "\n# Default login class for new users\n",
123 "\n# Range of valid default user ids\n",
124 NULL,
125 "\n# Range of valid default group ids\n",
126 NULL,
127 "\n# Days after which account expires (0=disabled)\n",
128 "\n# Days after which password expires (0=disabled)\n"
131 static char const *kwds[] =
134 "defaultpasswd",
135 "reuseuids",
136 "reusegids",
137 "nispasswd",
138 "skeleton",
139 "newmail",
140 "logfile",
141 "home",
142 "homemode",
143 "shellpath",
144 "shells",
145 "defaultshell",
146 "defaultgroup",
147 "extragroups",
148 "defaultclass",
149 "minuid",
150 "maxuid",
151 "mingid",
152 "maxgid",
153 "expire_days",
154 "password_days",
155 NULL
158 static char *
159 unquote(char const * str)
161 if (str && (*str == '"' || *str == '\'')) {
162 char *p = strchr(str + 1, *str);
164 if (p != NULL)
165 *p = '\0';
166 return (char *) (*++str ? str : NULL);
168 return (char *) str;
172 boolean_val(char const * str, int dflt)
174 if ((str = unquote(str)) != NULL) {
175 int i;
177 for (i = 0; booltrue[i]; i++)
178 if (strcmp(str, booltrue[i]) == 0)
179 return 1;
180 for (i = 0; boolfalse[i]; i++)
181 if (strcmp(str, boolfalse[i]) == 0)
182 return 0;
185 * Special cases for defaultpassword
187 if (strcmp(str, "random") == 0)
188 return -1;
189 if (strcmp(str, "none") == 0)
190 return -2;
192 return dflt;
195 char const *
196 boolean_str(int val)
198 if (val == -1)
199 return "random";
200 else if (val == -2)
201 return "none";
202 else
203 return val ? booltrue[0] : boolfalse[0];
206 char *
207 newstr(char const * p)
209 char *q = NULL;
211 if ((p = unquote(p)) != NULL) {
212 int l = strlen(p) + 1;
214 if ((q = malloc(l)) != NULL)
215 memcpy(q, p, l);
217 return q;
220 #define LNBUFSZ 1024
223 struct userconf *
224 read_userconfig(char const * file)
226 FILE *fp;
228 extendarray(&config.groups, &config.numgroups, 200);
229 memset(config.groups, 0, config.numgroups * sizeof(char *));
230 if (file == NULL)
231 file = _PATH_PW_CONF;
232 if ((fp = fopen(file, "r")) != NULL) {
233 int buflen = LNBUFSZ;
234 char *buf = malloc(buflen);
236 nextline:
237 while (fgets(buf, buflen, fp) != NULL) {
238 char *p;
240 while ((p = strchr(buf, '\n')) == NULL) {
241 int l;
242 if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
243 int ch;
244 while ((ch = fgetc(fp)) != '\n' && ch != EOF);
245 goto nextline; /* Ignore it */
247 l = strlen(buf);
248 if (fgets(buf + l, buflen - l, fp) == NULL)
249 break; /* Unterminated last line */
252 if (p != NULL)
253 *p = '\0';
255 if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
256 static char const toks[] = " \t\r\n,=";
257 char *q = strtok(NULL, toks);
258 int i = 0;
259 mode_t *modeset;
261 while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
262 ++i;
263 #if debugging
264 if (i == _UC_FIELDS)
265 printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
266 else
267 printf("Got kwd[%s]=%s\n", p, q);
268 #endif
269 switch (i) {
270 case _UC_DEFAULTPWD:
271 config.default_password = boolean_val(q, 1);
272 break;
273 case _UC_REUSEUID:
274 config.reuse_uids = boolean_val(q, 0);
275 break;
276 case _UC_REUSEGID:
277 config.reuse_gids = boolean_val(q, 0);
278 break;
279 case _UC_NISPASSWD:
280 config.nispasswd = (q == NULL || !boolean_val(q, 1))
281 ? NULL : newstr(q);
282 break;
283 case _UC_DOTDIR:
284 config.dotdir = (q == NULL || !boolean_val(q, 1))
285 ? NULL : newstr(q);
286 break;
287 case _UC_NEWMAIL:
288 config.newmail = (q == NULL || !boolean_val(q, 1))
289 ? NULL : newstr(q);
290 break;
291 case _UC_LOGFILE:
292 config.logfile = (q == NULL || !boolean_val(q, 1))
293 ? NULL : newstr(q);
294 break;
295 case _UC_HOMEROOT:
296 config.home = (q == NULL || !boolean_val(q, 1))
297 ? "/home" : newstr(q);
298 break;
299 case _UC_HOMEMODE:
300 modeset = setmode(q);
301 config.homemode = (q == NULL || !boolean_val(q, 1))
302 ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
303 free(modeset);
304 break;
305 case _UC_SHELLPATH:
306 config.shelldir = (q == NULL || !boolean_val(q, 1))
307 ? "/bin" : newstr(q);
308 break;
309 case _UC_SHELLS:
310 for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
311 system_shells[i] = newstr(q);
312 if (i > 0)
313 while (i < _UC_MAXSHELLS)
314 system_shells[i++] = NULL;
315 break;
316 case _UC_DEFAULTSHELL:
317 config.shell_default = (q == NULL || !boolean_val(q, 1))
318 ? (char *) bourne_shell : newstr(q);
319 break;
320 case _UC_DEFAULTGROUP:
321 q = unquote(q);
322 config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
323 ? NULL : newstr(q);
324 break;
325 case _UC_EXTRAGROUPS:
326 for (i = 0; q != NULL; q = strtok(NULL, toks)) {
327 if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
328 config.groups[i++] = newstr(q);
330 if (i > 0)
331 while (i < config.numgroups)
332 config.groups[i++] = NULL;
333 break;
334 case _UC_DEFAULTCLASS:
335 config.default_class = (q == NULL || !boolean_val(q, 1))
336 ? NULL : newstr(q);
337 break;
338 case _UC_MINUID:
339 if ((q = unquote(q)) != NULL && isdigit(*q))
340 config.min_uid = (uid_t) atol(q);
341 break;
342 case _UC_MAXUID:
343 if ((q = unquote(q)) != NULL && isdigit(*q))
344 config.max_uid = (uid_t) atol(q);
345 break;
346 case _UC_MINGID:
347 if ((q = unquote(q)) != NULL && isdigit(*q))
348 config.min_gid = (gid_t) atol(q);
349 break;
350 case _UC_MAXGID:
351 if ((q = unquote(q)) != NULL && isdigit(*q))
352 config.max_gid = (gid_t) atol(q);
353 break;
354 case _UC_EXPIRE:
355 if ((q = unquote(q)) != NULL && isdigit(*q))
356 config.expire_days = atoi(q);
357 break;
358 case _UC_PASSWORD:
359 if ((q = unquote(q)) != NULL && isdigit(*q))
360 config.password_days = atoi(q);
361 break;
362 case _UC_FIELDS:
363 case _UC_NONE:
364 break;
368 free(buf);
369 fclose(fp);
371 return &config;
376 write_userconfig(char const * file)
378 int fd;
380 if (file == NULL)
381 file = _PATH_PW_CONF;
383 if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
384 FILE *fp;
386 if ((fp = fdopen(fd, "w")) == NULL)
387 close(fd);
388 else {
389 int i, j, k;
390 int len = LNBUFSZ;
391 char *buf = malloc(len);
393 for (i = _UC_NONE; i < _UC_FIELDS; i++) {
394 int quote = 1;
395 char const *val = buf;
397 *buf = '\0';
398 switch (i) {
399 case _UC_DEFAULTPWD:
400 val = boolean_str(config.default_password);
401 break;
402 case _UC_REUSEUID:
403 val = boolean_str(config.reuse_uids);
404 break;
405 case _UC_REUSEGID:
406 val = boolean_str(config.reuse_gids);
407 break;
408 case _UC_NISPASSWD:
409 val = config.nispasswd ? config.nispasswd : "";
410 quote = 0;
411 break;
412 case _UC_DOTDIR:
413 val = config.dotdir ? config.dotdir : boolean_str(0);
414 break;
415 case _UC_NEWMAIL:
416 val = config.newmail ? config.newmail : boolean_str(0);
417 break;
418 case _UC_LOGFILE:
419 val = config.logfile ? config.logfile : boolean_str(0);
420 break;
421 case _UC_HOMEROOT:
422 val = config.home;
423 break;
424 case _UC_HOMEMODE:
425 sprintf(buf, "%04o", config.homemode);
426 quote = 0;
427 break;
428 case _UC_SHELLPATH:
429 val = config.shelldir;
430 break;
431 case _UC_SHELLS:
432 for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
433 char lbuf[64];
434 int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
435 if (l < 0)
436 l = 0;
437 if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
438 strcpy(buf + k, lbuf);
439 k += l;
442 quote = 0;
443 break;
444 case _UC_DEFAULTSHELL:
445 val = config.shell_default ? config.shell_default : bourne_shell;
446 break;
447 case _UC_DEFAULTGROUP:
448 val = config.default_group ? config.default_group : "";
449 break;
450 case _UC_EXTRAGROUPS:
451 extendarray(&config.groups, &config.numgroups, 200);
452 for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
453 char lbuf[64];
454 int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
455 if (l < 0)
456 l = 0;
457 if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
458 strcpy(buf + k, lbuf);
459 k += l;
462 quote = 0;
463 break;
464 case _UC_DEFAULTCLASS:
465 val = config.default_class ? config.default_class : "";
466 break;
467 case _UC_MINUID:
468 sprintf(buf, "%lu", (unsigned long) config.min_uid);
469 quote = 0;
470 break;
471 case _UC_MAXUID:
472 sprintf(buf, "%lu", (unsigned long) config.max_uid);
473 quote = 0;
474 break;
475 case _UC_MINGID:
476 sprintf(buf, "%lu", (unsigned long) config.min_gid);
477 quote = 0;
478 break;
479 case _UC_MAXGID:
480 sprintf(buf, "%lu", (unsigned long) config.max_gid);
481 quote = 0;
482 break;
483 case _UC_EXPIRE:
484 sprintf(buf, "%d", config.expire_days);
485 quote = 0;
486 break;
487 case _UC_PASSWORD:
488 sprintf(buf, "%d", config.password_days);
489 quote = 0;
490 break;
491 case _UC_NONE:
492 break;
495 if (comments[i])
496 fputs(comments[i], fp);
498 if (*kwds[i]) {
499 if (quote)
500 fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
501 else
502 fprintf(fp, "%s = %s\n", kwds[i], val);
503 #if debugging
504 printf("WROTE: %s = %s\n", kwds[i], val);
505 #endif
508 free(buf);
509 return fclose(fp) != EOF;
512 return 0;