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