]> git.cameronkatri.com Git - pw-darwin.git/blob - pw/pw_conf.c
PREFIX stuff
[pw-darwin.git] / pw / pw_conf.c
1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (C) 1996
5 * David L. Nugent. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #ifndef lint
30 static const char rcsid[] =
31 "$FreeBSD$";
32 #endif /* not lint */
33
34 #include <err.h>
35 #include <fcntl.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 #include "pw.h"
40
41 #define debugging 0
42
43 enum {
44 _UC_NONE,
45 _UC_DEFAULTPWD,
46 _UC_REUSEUID,
47 _UC_REUSEGID,
48 _UC_NISPASSWD,
49 _UC_DOTDIR,
50 _UC_NEWMAIL,
51 _UC_LOGFILE,
52 _UC_HOMEROOT,
53 _UC_HOMEMODE,
54 _UC_SHELLPATH,
55 _UC_SHELLS,
56 _UC_DEFAULTSHELL,
57 _UC_DEFAULTGROUP,
58 _UC_EXTRAGROUPS,
59 _UC_DEFAULTCLASS,
60 _UC_MINUID,
61 _UC_MAXUID,
62 _UC_MINGID,
63 _UC_MAXGID,
64 _UC_EXPIRE,
65 _UC_PASSWORD,
66 _UC_FIELDS
67 };
68
69 static char bourne_shell[] = "sh";
70
71 static char *system_shells[_UC_MAXSHELLS] =
72 {
73 bourne_shell,
74 "csh",
75 "tcsh"
76 };
77
78 static char const *booltrue[] =
79 {
80 "yes", "true", "1", "on", NULL
81 };
82 static char const *boolfalse[] =
83 {
84 "no", "false", "0", "off", NULL
85 };
86
87 static struct userconf config =
88 {
89 0, /* Default password for new users? (nologin) */
90 0, /* Reuse uids? */
91 0, /* Reuse gids? */
92 NULL, /* NIS version of the passwd file */
93 "/usr/share/skel", /* Where to obtain skeleton files */
94 NULL, /* Mail to send to new accounts */
95 "/var/log/userlog", /* Where to log changes */
96 "/var", /* Where to create home directory */
97 _DEF_DIRMODE, /* Home directory perms, modified by umask */
98 "/bin", /* Where shells are located */
99 system_shells, /* List of shells (first is default) */
100 bourne_shell, /* Default shell */
101 NULL, /* Default group name */
102 NULL, /* Default (additional) groups */
103 NULL, /* Default login class */
104 1000, 32000, /* Allowed range of uids */
105 1000, 32000, /* Allowed range of gids */
106 0, /* Days until account expires */
107 0 /* Days until password expires */
108 };
109
110 static char const *comments[_UC_FIELDS] =
111 {
112 "#\n# pw.conf - user/group configuration defaults\n#\n",
113 "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
114 "\n# Reuse gaps in uid sequence? (yes or no)\n",
115 "\n# Reuse gaps in gid sequence? (yes or no)\n",
116 "\n# Path to the NIS passwd file (blank or 'no' for none)\n",
117 "\n# Obtain default dotfiles from this directory\n",
118 "\n# Mail this file to new user (/etc/newuser.msg or no)\n",
119 "\n# Log add/change/remove information in this file\n",
120 "\n# Root directory in which $HOME directory is created\n",
121 "\n# Mode for the new $HOME directory, will be modified by umask\n",
122 "\n# Colon separated list of directories containing valid shells\n",
123 "\n# Comma separated list of available shells (without paths)\n",
124 "\n# Default shell (without path)\n",
125 "\n# Default group (leave blank for new group per user)\n",
126 "\n# Extra groups for new users\n",
127 "\n# Default login class for new users\n",
128 "\n# Range of valid default user ids\n",
129 NULL,
130 "\n# Range of valid default group ids\n",
131 NULL,
132 "\n# Days after which account expires (0=disabled)\n",
133 "\n# Days after which password expires (0=disabled)\n"
134 };
135
136 static char const *kwds[] =
137 {
138 "",
139 "defaultpasswd",
140 "reuseuids",
141 "reusegids",
142 "nispasswd",
143 "skeleton",
144 "newmail",
145 "logfile",
146 "home",
147 "homemode",
148 "shellpath",
149 "shells",
150 "defaultshell",
151 "defaultgroup",
152 "extragroups",
153 "defaultclass",
154 "minuid",
155 "maxuid",
156 "mingid",
157 "maxgid",
158 "expire_days",
159 "password_days",
160 NULL
161 };
162
163 static char *
164 unquote(char const * str)
165 {
166 if (str && (*str == '"' || *str == '\'')) {
167 char *p = strchr(str + 1, *str);
168
169 if (p != NULL)
170 *p = '\0';
171 return (char *) (*++str ? str : NULL);
172 }
173 return (char *) str;
174 }
175
176 int
177 boolean_val(char const * str, int dflt)
178 {
179 if ((str = unquote(str)) != NULL) {
180 int i;
181
182 for (i = 0; booltrue[i]; i++)
183 if (strcmp(str, booltrue[i]) == 0)
184 return 1;
185 for (i = 0; boolfalse[i]; i++)
186 if (strcmp(str, boolfalse[i]) == 0)
187 return 0;
188 }
189 return dflt;
190 }
191
192 int
193 passwd_val(char const * str, int dflt)
194 {
195 if ((str = unquote(str)) != NULL) {
196 int i;
197
198 for (i = 0; booltrue[i]; i++)
199 if (strcmp(str, booltrue[i]) == 0)
200 return P_YES;
201 for (i = 0; boolfalse[i]; i++)
202 if (strcmp(str, boolfalse[i]) == 0)
203 return P_NO;
204
205 /*
206 * Special cases for defaultpassword
207 */
208 if (strcmp(str, "random") == 0)
209 return P_RANDOM;
210 if (strcmp(str, "none") == 0)
211 return P_NONE;
212
213 errx(1, "Invalid value for default password");
214 }
215 return dflt;
216 }
217
218 char const *
219 boolean_str(int val)
220 {
221 if (val == P_NO)
222 return (boolfalse[0]);
223 else if (val == P_RANDOM)
224 return ("random");
225 else if (val == P_NONE)
226 return ("none");
227 else
228 return (booltrue[0]);
229 }
230
231 char *
232 newstr(char const * p)
233 {
234 char *q;
235
236 if ((p = unquote(p)) == NULL)
237 return (NULL);
238
239 if ((q = strdup(p)) == NULL)
240 err(1, "strdup()");
241
242 return (q);
243 }
244
245 struct userconf *
246 read_userconfig(char const * file)
247 {
248 FILE *fp;
249 char *buf, *p;
250 const char *errstr;
251 size_t linecap;
252 ssize_t linelen;
253
254 buf = NULL;
255 linecap = 0;
256
257 if ((fp = fopen(file, "r")) == NULL)
258 return (&config);
259
260 while ((linelen = getline(&buf, &linecap, fp)) > 0) {
261 if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
262 static char const toks[] = " \t\r\n,=";
263 char *q = strtok(NULL, toks);
264 int i = 0;
265 mode_t *modeset;
266
267 while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
268 ++i;
269 #if debugging
270 if (i == _UC_FIELDS)
271 printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
272 else
273 printf("Got kwd[%s]=%s\n", p, q);
274 #endif
275 switch (i) {
276 case _UC_DEFAULTPWD:
277 config.default_password = passwd_val(q, 1);
278 break;
279 case _UC_REUSEUID:
280 config.reuse_uids = boolean_val(q, 0);
281 break;
282 case _UC_REUSEGID:
283 config.reuse_gids = boolean_val(q, 0);
284 break;
285 case _UC_NISPASSWD:
286 config.nispasswd = (q == NULL || !boolean_val(q, 1))
287 ? NULL : newstr(q);
288 break;
289 case _UC_DOTDIR:
290 config.dotdir = (q == NULL || !boolean_val(q, 1))
291 ? NULL : newstr(q);
292 break;
293 case _UC_NEWMAIL:
294 config.newmail = (q == NULL || !boolean_val(q, 1))
295 ? NULL : newstr(q);
296 break;
297 case _UC_LOGFILE:
298 config.logfile = (q == NULL || !boolean_val(q, 1))
299 ? NULL : newstr(q);
300 break;
301 case _UC_HOMEROOT:
302 config.home = (q == NULL || !boolean_val(q, 1))
303 ? "/var" : newstr(q);
304 break;
305 case _UC_HOMEMODE:
306 modeset = setmode(q);
307 config.homemode = (q == NULL || !boolean_val(q, 1))
308 ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
309 free(modeset);
310 break;
311 case _UC_SHELLPATH:
312 config.shelldir = (q == NULL || !boolean_val(q, 1))
313 ? "/bin" : newstr(q);
314 break;
315 case _UC_SHELLS:
316 for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
317 system_shells[i] = newstr(q);
318 if (i > 0)
319 while (i < _UC_MAXSHELLS)
320 system_shells[i++] = NULL;
321 break;
322 case _UC_DEFAULTSHELL:
323 config.shell_default = (q == NULL || !boolean_val(q, 1))
324 ? (char *) bourne_shell : newstr(q);
325 break;
326 case _UC_DEFAULTGROUP:
327 q = unquote(q);
328 config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
329 ? NULL : newstr(q);
330 break;
331 case _UC_EXTRAGROUPS:
332 while ((q = strtok(NULL, toks)) != NULL) {
333 if (config.groups == NULL)
334 config.groups = sl_init();
335 sl_add(config.groups, newstr(q));
336 }
337 break;
338 case _UC_DEFAULTCLASS:
339 config.default_class = (q == NULL || !boolean_val(q, 1))
340 ? NULL : newstr(q);
341 break;
342 case _UC_MINUID:
343 if ((q = unquote(q)) != NULL) {
344 config.min_uid = strtounum(q, 0,
345 UID_MAX, &errstr);
346 if (errstr)
347 warnx("Invalid min_uid: '%s';"
348 " ignoring", q);
349 }
350 break;
351 case _UC_MAXUID:
352 if ((q = unquote(q)) != NULL) {
353 config.max_uid = strtounum(q, 0,
354 UID_MAX, &errstr);
355 if (errstr)
356 warnx("Invalid max_uid: '%s';"
357 " ignoring", q);
358 }
359 break;
360 case _UC_MINGID:
361 if ((q = unquote(q)) != NULL) {
362 config.min_gid = strtounum(q, 0,
363 GID_MAX, &errstr);
364 if (errstr)
365 warnx("Invalid min_gid: '%s';"
366 " ignoring", q);
367 }
368 break;
369 case _UC_MAXGID:
370 if ((q = unquote(q)) != NULL) {
371 config.max_gid = strtounum(q, 0,
372 GID_MAX, &errstr);
373 if (errstr)
374 warnx("Invalid max_gid: '%s';"
375 " ignoring", q);
376 }
377 break;
378 case _UC_EXPIRE:
379 if ((q = unquote(q)) != NULL) {
380 config.expire_days = strtonum(q, 0,
381 INT_MAX, &errstr);
382 if (errstr)
383 warnx("Invalid expire days:"
384 " '%s'; ignoring", q);
385 }
386 break;
387 case _UC_PASSWORD:
388 if ((q = unquote(q)) != NULL) {
389 config.password_days = strtonum(q, 0,
390 INT_MAX, &errstr);
391 if (errstr)
392 warnx("Invalid password days:"
393 " '%s'; ignoring", q);
394 }
395 break;
396 case _UC_FIELDS:
397 case _UC_NONE:
398 break;
399 }
400 }
401 }
402 free(buf);
403 fclose(fp);
404
405 return (&config);
406 }
407
408
409 int
410 write_userconfig(struct userconf *cnf, const char *file)
411 {
412 int fd;
413 int i, j;
414 FILE *buffp;
415 FILE *fp;
416 char cfgfile[MAXPATHLEN];
417 char *buf;
418 size_t sz;
419
420 if (file == NULL) {
421 snprintf(cfgfile, sizeof(cfgfile), "%s/" _PW_CONF,
422 conf.etcpath);
423 file = cfgfile;
424 }
425
426 if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
427 return (0);
428
429 if ((fp = fdopen(fd, "w")) == NULL) {
430 close(fd);
431 return (0);
432 }
433
434 sz = 0;
435 buf = NULL;
436 buffp = open_memstream(&buf, &sz);
437 if (buffp == NULL)
438 err(EXIT_FAILURE, "open_memstream()");
439
440 for (i = _UC_NONE; i < _UC_FIELDS; i++) {
441 int quote = 1;
442
443 if (buf != NULL)
444 memset(buf, 0, sz);
445 rewind(buffp);
446 switch (i) {
447 case _UC_DEFAULTPWD:
448 fputs(boolean_str(cnf->default_password), buffp);
449 break;
450 case _UC_REUSEUID:
451 fputs(boolean_str(cnf->reuse_uids), buffp);
452 break;
453 case _UC_REUSEGID:
454 fputs(boolean_str(cnf->reuse_gids), buffp);
455 break;
456 case _UC_NISPASSWD:
457 fputs(cnf->nispasswd ? cnf->nispasswd : "", buffp);
458 quote = 0;
459 break;
460 case _UC_DOTDIR:
461 fputs(cnf->dotdir ? cnf->dotdir : boolean_str(0),
462 buffp);
463 break;
464 case _UC_NEWMAIL:
465 fputs(cnf->newmail ? cnf->newmail : boolean_str(0),
466 buffp);
467 break;
468 case _UC_LOGFILE:
469 fputs(cnf->logfile ? cnf->logfile : boolean_str(0),
470 buffp);
471 break;
472 case _UC_HOMEROOT:
473 fputs(cnf->home, buffp);
474 break;
475 case _UC_HOMEMODE:
476 fprintf(buffp, "%04o", cnf->homemode);
477 quote = 0;
478 break;
479 case _UC_SHELLPATH:
480 fputs(cnf->shelldir, buffp);
481 break;
482 case _UC_SHELLS:
483 for (j = 0; j < _UC_MAXSHELLS &&
484 system_shells[j] != NULL; j++)
485 fprintf(buffp, "%s\"%s\"", j ?
486 "," : "", system_shells[j]);
487 quote = 0;
488 break;
489 case _UC_DEFAULTSHELL:
490 fputs(cnf->shell_default ? cnf->shell_default :
491 bourne_shell, buffp);
492 break;
493 case _UC_DEFAULTGROUP:
494 fputs(cnf->default_group ? cnf->default_group : "",
495 buffp);
496 break;
497 case _UC_EXTRAGROUPS:
498 for (j = 0; cnf->groups != NULL &&
499 j < (int)cnf->groups->sl_cur; j++)
500 fprintf(buffp, "%s\"%s\"", j ?
501 "," : "", cnf->groups->sl_str[j]);
502 quote = 0;
503 break;
504 case _UC_DEFAULTCLASS:
505 fputs(cnf->default_class ? cnf->default_class : "",
506 buffp);
507 break;
508 case _UC_MINUID:
509 fprintf(buffp, "%ju", (uintmax_t)cnf->min_uid);
510 quote = 0;
511 break;
512 case _UC_MAXUID:
513 fprintf(buffp, "%ju", (uintmax_t)cnf->max_uid);
514 quote = 0;
515 break;
516 case _UC_MINGID:
517 fprintf(buffp, "%ju", (uintmax_t)cnf->min_gid);
518 quote = 0;
519 break;
520 case _UC_MAXGID:
521 fprintf(buffp, "%ju", (uintmax_t)cnf->max_gid);
522 quote = 0;
523 break;
524 case _UC_EXPIRE:
525 fprintf(buffp, "%jd", (intmax_t)cnf->expire_days);
526 quote = 0;
527 break;
528 case _UC_PASSWORD:
529 fprintf(buffp, "%jd", (intmax_t)cnf->password_days);
530 quote = 0;
531 break;
532 case _UC_NONE:
533 break;
534 }
535 fflush(buffp);
536
537 if (comments[i])
538 fputs(comments[i], fp);
539
540 if (*kwds[i]) {
541 if (quote)
542 fprintf(fp, "%s = \"%s\"\n", kwds[i], buf);
543 else
544 fprintf(fp, "%s = %s\n", kwds[i], buf);
545 #if debugging
546 printf("WROTE: %s = %s\n", kwds[i], buf);
547 #endif
548 }
549 }
550 fclose(buffp);
551 free(buf);
552 return (fclose(fp) != EOF);
553 }