]> git.cameronkatri.com Git - pw-darwin.git/blob - pw/pw_conf.c
Merge from head
[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
35 #include <err.h>
36 #include <fcntl.h>
37 #include <string.h>
38 #include <unistd.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 if (file == NULL)
239 file = _PATH_PW_CONF;
240
241 if ((fp = fopen(file, "r")) == NULL)
242 return (&config);
243
244 while ((linelen = getline(&buf, &linecap, fp)) > 0) {
245 if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
246 static char const toks[] = " \t\r\n,=";
247 char *q = strtok(NULL, toks);
248 int i = 0;
249 mode_t *modeset;
250
251 while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
252 ++i;
253 #if debugging
254 if (i == _UC_FIELDS)
255 printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
256 else
257 printf("Got kwd[%s]=%s\n", p, q);
258 #endif
259 switch (i) {
260 case _UC_DEFAULTPWD:
261 config.default_password = boolean_val(q, 1);
262 break;
263 case _UC_REUSEUID:
264 config.reuse_uids = boolean_val(q, 0);
265 break;
266 case _UC_REUSEGID:
267 config.reuse_gids = boolean_val(q, 0);
268 break;
269 case _UC_NISPASSWD:
270 config.nispasswd = (q == NULL || !boolean_val(q, 1))
271 ? NULL : newstr(q);
272 break;
273 case _UC_DOTDIR:
274 config.dotdir = (q == NULL || !boolean_val(q, 1))
275 ? NULL : newstr(q);
276 break;
277 case _UC_NEWMAIL:
278 config.newmail = (q == NULL || !boolean_val(q, 1))
279 ? NULL : newstr(q);
280 break;
281 case _UC_LOGFILE:
282 config.logfile = (q == NULL || !boolean_val(q, 1))
283 ? NULL : newstr(q);
284 break;
285 case _UC_HOMEROOT:
286 config.home = (q == NULL || !boolean_val(q, 1))
287 ? "/home" : newstr(q);
288 break;
289 case _UC_HOMEMODE:
290 modeset = setmode(q);
291 config.homemode = (q == NULL || !boolean_val(q, 1))
292 ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
293 free(modeset);
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 (config.groups == NULL)
318 config.groups = sl_init();
319 sl_add(config.groups, newstr(q));
320 }
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 }
352 break;
353 case _UC_MAXGID:
354 if ((q = unquote(q)) != NULL) {
355 config.max_gid = strtounum(q, 0,
356 GID_MAX, &errstr);
357 if (errstr)
358 warnx("Invalid max_gid: '%s';"
359 " ignoring", q);
360 }
361 break;
362 case _UC_EXPIRE:
363 if ((q = unquote(q)) != NULL) {
364 config.expire_days = strtonum(q, 0,
365 INT_MAX, &errstr);
366 if (errstr)
367 warnx("Invalid expire days:"
368 " '%s'; ignoring", q);
369 }
370 break;
371 case _UC_PASSWORD:
372 if ((q = unquote(q)) != NULL) {
373 config.password_days = strtonum(q, 0,
374 INT_MAX, &errstr);
375 if (errstr)
376 warnx("Invalid password days:"
377 " '%s'; ignoring", q);
378 }
379 break;
380 case _UC_FIELDS:
381 case _UC_NONE:
382 break;
383 }
384 }
385 }
386 free(buf);
387 fclose(fp);
388
389 return (&config);
390 }
391
392
393 int
394 write_userconfig(struct userconf *cnf, const char *file)
395 {
396 int fd;
397 int i, j;
398 struct sbuf *buf;
399 FILE *fp;
400
401 if (file == NULL)
402 file = _PATH_PW_CONF;
403
404 if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
405 return (0);
406
407 if ((fp = fdopen(fd, "w")) == NULL) {
408 close(fd);
409 return (0);
410 }
411
412 buf = sbuf_new_auto();
413 for (i = _UC_NONE; i < _UC_FIELDS; i++) {
414 int quote = 1;
415
416 sbuf_clear(buf);
417 switch (i) {
418 case _UC_DEFAULTPWD:
419 sbuf_cat(buf, boolean_str(cnf->default_password));
420 break;
421 case _UC_REUSEUID:
422 sbuf_cat(buf, boolean_str(cnf->reuse_uids));
423 break;
424 case _UC_REUSEGID:
425 sbuf_cat(buf, boolean_str(cnf->reuse_gids));
426 break;
427 case _UC_NISPASSWD:
428 sbuf_cat(buf, cnf->nispasswd ? cnf->nispasswd : "");
429 quote = 0;
430 break;
431 case _UC_DOTDIR:
432 sbuf_cat(buf, cnf->dotdir ? cnf->dotdir :
433 boolean_str(0));
434 break;
435 case _UC_NEWMAIL:
436 sbuf_cat(buf, cnf->newmail ? cnf->newmail :
437 boolean_str(0));
438 break;
439 case _UC_LOGFILE:
440 sbuf_cat(buf, cnf->logfile ? cnf->logfile :
441 boolean_str(0));
442 break;
443 case _UC_HOMEROOT:
444 sbuf_cat(buf, cnf->home);
445 break;
446 case _UC_HOMEMODE:
447 sbuf_printf(buf, "%04o", cnf->homemode);
448 quote = 0;
449 break;
450 case _UC_SHELLPATH:
451 sbuf_cat(buf, cnf->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, cnf->shell_default ?
462 cnf->shell_default : bourne_shell);
463 break;
464 case _UC_DEFAULTGROUP:
465 sbuf_cat(buf, cnf->default_group ?
466 cnf->default_group : "");
467 break;
468 case _UC_EXTRAGROUPS:
469 for (j = 0; cnf->groups != NULL &&
470 j < (int)cnf->groups->sl_cur; j++)
471 sbuf_printf(buf, "%s\"%s\"", j ?
472 "," : "", cnf->groups->sl_str[j]);
473 quote = 0;
474 break;
475 case _UC_DEFAULTCLASS:
476 sbuf_cat(buf, cnf->default_class ?
477 cnf->default_class : "");
478 break;
479 case _UC_MINUID:
480 sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_uid);
481 quote = 0;
482 break;
483 case _UC_MAXUID:
484 sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_uid);
485 quote = 0;
486 break;
487 case _UC_MINGID:
488 sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_gid);
489 quote = 0;
490 break;
491 case _UC_MAXGID:
492 sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_gid);
493 quote = 0;
494 break;
495 case _UC_EXPIRE:
496 sbuf_printf(buf, "%jd", (intmax_t)cnf->expire_days);
497 quote = 0;
498 break;
499 case _UC_PASSWORD:
500 sbuf_printf(buf, "%jd", (intmax_t)cnf->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 }