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