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