]> git.cameronkatri.com Git - pw-darwin.git/blob - pw/pw_conf.c
Return from the function as early as possible
[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 <string.h>
35 #include <ctype.h>
36 #include <fcntl.h>
37
38 #include "pw.h"
39
40 #define debugging 0
41
42 enum {
43 _UC_NONE,
44 _UC_DEFAULTPWD,
45 _UC_REUSEUID,
46 _UC_REUSEGID,
47 _UC_NISPASSWD,
48 _UC_DOTDIR,
49 _UC_NEWMAIL,
50 _UC_LOGFILE,
51 _UC_HOMEROOT,
52 _UC_HOMEMODE,
53 _UC_SHELLPATH,
54 _UC_SHELLS,
55 _UC_DEFAULTSHELL,
56 _UC_DEFAULTGROUP,
57 _UC_EXTRAGROUPS,
58 _UC_DEFAULTCLASS,
59 _UC_MINUID,
60 _UC_MAXUID,
61 _UC_MINGID,
62 _UC_MAXGID,
63 _UC_EXPIRE,
64 _UC_PASSWORD,
65 _UC_FIELDS
66 };
67
68 static char bourne_shell[] = "sh";
69
70 static char *system_shells[_UC_MAXSHELLS] =
71 {
72 bourne_shell,
73 "csh",
74 "tcsh"
75 };
76
77 static char const *booltrue[] =
78 {
79 "yes", "true", "1", "on", NULL
80 };
81 static char const *boolfalse[] =
82 {
83 "no", "false", "0", "off", NULL
84 };
85
86 static struct userconf config =
87 {
88 0, /* Default password for new users? (nologin) */
89 0, /* Reuse uids? */
90 0, /* Reuse gids? */
91 NULL, /* NIS version of the passwd file */
92 "/usr/share/skel", /* Where to obtain skeleton files */
93 NULL, /* Mail to send to new accounts */
94 "/var/log/userlog", /* Where to log changes */
95 "/home", /* Where to create home directory */
96 _DEF_DIRMODE, /* Home directory perms, modified by umask */
97 "/bin", /* Where shells are located */
98 system_shells, /* List of shells (first is default) */
99 bourne_shell, /* Default shell */
100 NULL, /* Default group name */
101 NULL, /* Default (additional) groups */
102 NULL, /* Default login class */
103 1000, 32000, /* Allowed range of uids */
104 1000, 32000, /* Allowed range of gids */
105 0, /* Days until account expires */
106 0, /* Days until password expires */
107 0 /* size of default_group array */
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 /*
190 * Special cases for defaultpassword
191 */
192 if (strcmp(str, "random") == 0)
193 return -1;
194 if (strcmp(str, "none") == 0)
195 return -2;
196 }
197 return dflt;
198 }
199
200 char const *
201 boolean_str(int val)
202 {
203 if (val == -1)
204 return "random";
205 else if (val == -2)
206 return "none";
207 else
208 return val ? booltrue[0] : boolfalse[0];
209 }
210
211 char *
212 newstr(char const * p)
213 {
214 char *q = NULL;
215
216 if ((p = unquote(p)) != NULL) {
217 int l = strlen(p) + 1;
218
219 if ((q = malloc(l)) != NULL)
220 memcpy(q, p, l);
221 }
222 return q;
223 }
224
225 #define LNBUFSZ 1024
226
227
228 struct userconf *
229 read_userconfig(char const * file)
230 {
231 FILE *fp;
232 char *buf, *p;
233 size_t linecap;
234 ssize_t linelen;
235
236 buf = NULL;
237 linecap = 0;
238
239 extendarray(&config.groups, &config.numgroups, 200);
240 memset(config.groups, 0, config.numgroups * sizeof(char *));
241 if (file == NULL)
242 file = _PATH_PW_CONF;
243
244 if ((fp = fopen(file, "r")) != NULL) {
245 while ((linelen = getline(&buf, &linecap, fp)) > 0) {
246 if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
247 static char const toks[] = " \t\r\n,=";
248 char *q = strtok(NULL, toks);
249 int i = 0;
250 mode_t *modeset;
251
252 while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
253 ++i;
254 #if debugging
255 if (i == _UC_FIELDS)
256 printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
257 else
258 printf("Got kwd[%s]=%s\n", p, q);
259 #endif
260 switch (i) {
261 case _UC_DEFAULTPWD:
262 config.default_password = boolean_val(q, 1);
263 break;
264 case _UC_REUSEUID:
265 config.reuse_uids = boolean_val(q, 0);
266 break;
267 case _UC_REUSEGID:
268 config.reuse_gids = boolean_val(q, 0);
269 break;
270 case _UC_NISPASSWD:
271 config.nispasswd = (q == NULL || !boolean_val(q, 1))
272 ? NULL : newstr(q);
273 break;
274 case _UC_DOTDIR:
275 config.dotdir = (q == NULL || !boolean_val(q, 1))
276 ? NULL : newstr(q);
277 break;
278 case _UC_NEWMAIL:
279 config.newmail = (q == NULL || !boolean_val(q, 1))
280 ? NULL : newstr(q);
281 break;
282 case _UC_LOGFILE:
283 config.logfile = (q == NULL || !boolean_val(q, 1))
284 ? NULL : newstr(q);
285 break;
286 case _UC_HOMEROOT:
287 config.home = (q == NULL || !boolean_val(q, 1))
288 ? "/home" : newstr(q);
289 break;
290 case _UC_HOMEMODE:
291 modeset = setmode(q);
292 config.homemode = (q == NULL || !boolean_val(q, 1))
293 ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
294 free(modeset);
295 break;
296 case _UC_SHELLPATH:
297 config.shelldir = (q == NULL || !boolean_val(q, 1))
298 ? "/bin" : newstr(q);
299 break;
300 case _UC_SHELLS:
301 for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
302 system_shells[i] = newstr(q);
303 if (i > 0)
304 while (i < _UC_MAXSHELLS)
305 system_shells[i++] = NULL;
306 break;
307 case _UC_DEFAULTSHELL:
308 config.shell_default = (q == NULL || !boolean_val(q, 1))
309 ? (char *) bourne_shell : newstr(q);
310 break;
311 case _UC_DEFAULTGROUP:
312 q = unquote(q);
313 config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
314 ? NULL : newstr(q);
315 break;
316 case _UC_EXTRAGROUPS:
317 for (i = 0; q != NULL; q = strtok(NULL, toks)) {
318 if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
319 config.groups[i++] = newstr(q);
320 }
321 if (i > 0)
322 while (i < config.numgroups)
323 config.groups[i++] = NULL;
324 break;
325 case _UC_DEFAULTCLASS:
326 config.default_class = (q == NULL || !boolean_val(q, 1))
327 ? NULL : newstr(q);
328 break;
329 case _UC_MINUID:
330 if ((q = unquote(q)) != NULL && isdigit(*q))
331 config.min_uid = (uid_t) atol(q);
332 break;
333 case _UC_MAXUID:
334 if ((q = unquote(q)) != NULL && isdigit(*q))
335 config.max_uid = (uid_t) atol(q);
336 break;
337 case _UC_MINGID:
338 if ((q = unquote(q)) != NULL && isdigit(*q))
339 config.min_gid = (gid_t) atol(q);
340 break;
341 case _UC_MAXGID:
342 if ((q = unquote(q)) != NULL && isdigit(*q))
343 config.max_gid = (gid_t) atol(q);
344 break;
345 case _UC_EXPIRE:
346 if ((q = unquote(q)) != NULL && isdigit(*q))
347 config.expire_days = atoi(q);
348 break;
349 case _UC_PASSWORD:
350 if ((q = unquote(q)) != NULL && isdigit(*q))
351 config.password_days = atoi(q);
352 break;
353 case _UC_FIELDS:
354 case _UC_NONE:
355 break;
356 }
357 }
358 }
359 if (linecap > 0)
360 free(buf);
361 fclose(fp);
362 }
363 return &config;
364 }
365
366
367 int
368 write_userconfig(char const * file)
369 {
370 int fd;
371 int i, j;
372 struct sbuf *buf;
373 FILE *fp;
374
375 if (file == NULL)
376 file = _PATH_PW_CONF;
377
378 if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
379 return (0);
380
381 if ((fp = fdopen(fd, "w")) == NULL) {
382 close(fd);
383 return (0);
384 }
385
386 buf = sbuf_new_auto();
387 for (i = _UC_NONE; i < _UC_FIELDS; i++) {
388 int quote = 1;
389
390 sbuf_clear(buf);
391 switch (i) {
392 case _UC_DEFAULTPWD:
393 sbuf_cat(buf, boolean_str(config.default_password));
394 break;
395 case _UC_REUSEUID:
396 sbuf_cat(buf, boolean_str(config.reuse_uids));
397 break;
398 case _UC_REUSEGID:
399 sbuf_cat(buf, boolean_str(config.reuse_gids));
400 break;
401 case _UC_NISPASSWD:
402 sbuf_cat(buf, config.nispasswd ? config.nispasswd :
403 "");
404 quote = 0;
405 break;
406 case _UC_DOTDIR:
407 sbuf_cat(buf, config.dotdir ? config.dotdir :
408 boolean_str(0));
409 break;
410 case _UC_NEWMAIL:
411 sbuf_cat(buf, config.newmail ? config.newmail :
412 boolean_str(0));
413 break;
414 case _UC_LOGFILE:
415 sbuf_cat(buf, config.logfile ? config.logfile :
416 boolean_str(0));
417 break;
418 case _UC_HOMEROOT:
419 sbuf_cat(buf, config.home);
420 break;
421 case _UC_HOMEMODE:
422 sbuf_printf(buf, "%04o", config.homemode);
423 quote = 0;
424 break;
425 case _UC_SHELLPATH:
426 sbuf_cat(buf, config.shelldir);
427 break;
428 case _UC_SHELLS:
429 for (j = 0; j < _UC_MAXSHELLS &&
430 system_shells[j] != NULL; j++)
431 sbuf_printf(buf, "%s\"%s\"", j ?
432 "," : "", system_shells[j]);
433 quote = 0;
434 break;
435 case _UC_DEFAULTSHELL:
436 sbuf_cat(buf, config.shell_default ?
437 config.shell_default : bourne_shell);
438 break;
439 case _UC_DEFAULTGROUP:
440 sbuf_cat(buf, config.default_group ?
441 config.default_group : "");
442 break;
443 case _UC_EXTRAGROUPS:
444 extendarray(&config.groups, &config.numgroups, 200);
445 for (j = 0; j < config.numgroups &&
446 config.groups[j] != NULL; j++)
447 sbuf_printf(buf, "%s\"%s\"", j ?
448 "," : "", config.groups[j]);
449 quote = 0;
450 break;
451 case _UC_DEFAULTCLASS:
452 sbuf_cat(buf, config.default_class ?
453 config.default_class : "");
454 break;
455 case _UC_MINUID:
456 sbuf_printf(buf, "%lu", (unsigned long) config.min_uid);
457 quote = 0;
458 break;
459 case _UC_MAXUID:
460 sbuf_printf(buf, "%lu", (unsigned long) config.max_uid);
461 quote = 0;
462 break;
463 case _UC_MINGID:
464 sbuf_printf(buf, "%lu", (unsigned long) config.min_gid);
465 quote = 0;
466 break;
467 case _UC_MAXGID:
468 sbuf_printf(buf, "%lu", (unsigned long) config.max_gid);
469 quote = 0;
470 break;
471 case _UC_EXPIRE:
472 sbuf_printf(buf, "%d", config.expire_days);
473 quote = 0;
474 break;
475 case _UC_PASSWORD:
476 sbuf_printf(buf, "%d", config.password_days);
477 quote = 0;
478 break;
479 case _UC_NONE:
480 break;
481 }
482 sbuf_finish(buf);
483
484 if (comments[i])
485 fputs(comments[i], fp);
486
487 if (*kwds[i]) {
488 if (quote)
489 fprintf(fp, "%s = \"%s\"\n", kwds[i],
490 sbuf_data(buf));
491 else
492 fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
493 #if debugging
494 printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
495 #endif
496 }
497 }
498 sbuf_delete(buf);
499 return (fclose(fp) != EOF);
500 }