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