]> git.cameronkatri.com Git - pw-darwin.git/blob - pw/pw_conf.c
The initial logic for allocating the new string was wrong, the conversion
[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 #include <err.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 "/home", /* 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 0 /* size of default_group array */
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 size_t linecap;
232 ssize_t linelen;
233
234 buf = NULL;
235 linecap = 0;
236
237 extendarray(&config.groups, &config.numgroups, 200);
238 memset(config.groups, 0, config.numgroups * sizeof(char *));
239 if (file == NULL)
240 file = _PATH_PW_CONF;
241
242 if ((fp = fopen(file, "r")) != NULL) {
243 while ((linelen = getline(&buf, &linecap, fp)) > 0) {
244 if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
245 static char const toks[] = " \t\r\n,=";
246 char *q = strtok(NULL, toks);
247 int i = 0;
248 mode_t *modeset;
249
250 while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
251 ++i;
252 #if debugging
253 if (i == _UC_FIELDS)
254 printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
255 else
256 printf("Got kwd[%s]=%s\n", p, q);
257 #endif
258 switch (i) {
259 case _UC_DEFAULTPWD:
260 config.default_password = boolean_val(q, 1);
261 break;
262 case _UC_REUSEUID:
263 config.reuse_uids = boolean_val(q, 0);
264 break;
265 case _UC_REUSEGID:
266 config.reuse_gids = boolean_val(q, 0);
267 break;
268 case _UC_NISPASSWD:
269 config.nispasswd = (q == NULL || !boolean_val(q, 1))
270 ? NULL : newstr(q);
271 break;
272 case _UC_DOTDIR:
273 config.dotdir = (q == NULL || !boolean_val(q, 1))
274 ? NULL : newstr(q);
275 break;
276 case _UC_NEWMAIL:
277 config.newmail = (q == NULL || !boolean_val(q, 1))
278 ? NULL : newstr(q);
279 break;
280 case _UC_LOGFILE:
281 config.logfile = (q == NULL || !boolean_val(q, 1))
282 ? NULL : newstr(q);
283 break;
284 case _UC_HOMEROOT:
285 config.home = (q == NULL || !boolean_val(q, 1))
286 ? "/home" : newstr(q);
287 break;
288 case _UC_HOMEMODE:
289 modeset = setmode(q);
290 config.homemode = (q == NULL || !boolean_val(q, 1))
291 ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
292 free(modeset);
293 break;
294 case _UC_SHELLPATH:
295 config.shelldir = (q == NULL || !boolean_val(q, 1))
296 ? "/bin" : newstr(q);
297 break;
298 case _UC_SHELLS:
299 for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
300 system_shells[i] = newstr(q);
301 if (i > 0)
302 while (i < _UC_MAXSHELLS)
303 system_shells[i++] = NULL;
304 break;
305 case _UC_DEFAULTSHELL:
306 config.shell_default = (q == NULL || !boolean_val(q, 1))
307 ? (char *) bourne_shell : newstr(q);
308 break;
309 case _UC_DEFAULTGROUP:
310 q = unquote(q);
311 config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
312 ? NULL : newstr(q);
313 break;
314 case _UC_EXTRAGROUPS:
315 for (i = 0; q != NULL; q = strtok(NULL, toks)) {
316 if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
317 config.groups[i++] = newstr(q);
318 }
319 if (i > 0)
320 while (i < config.numgroups)
321 config.groups[i++] = NULL;
322 break;
323 case _UC_DEFAULTCLASS:
324 config.default_class = (q == NULL || !boolean_val(q, 1))
325 ? NULL : newstr(q);
326 break;
327 case _UC_MINUID:
328 if ((q = unquote(q)) != NULL && isdigit(*q))
329 config.min_uid = (uid_t) atol(q);
330 break;
331 case _UC_MAXUID:
332 if ((q = unquote(q)) != NULL && isdigit(*q))
333 config.max_uid = (uid_t) atol(q);
334 break;
335 case _UC_MINGID:
336 if ((q = unquote(q)) != NULL && isdigit(*q))
337 config.min_gid = (gid_t) atol(q);
338 break;
339 case _UC_MAXGID:
340 if ((q = unquote(q)) != NULL && isdigit(*q))
341 config.max_gid = (gid_t) atol(q);
342 break;
343 case _UC_EXPIRE:
344 if ((q = unquote(q)) != NULL && isdigit(*q))
345 config.expire_days = atoi(q);
346 break;
347 case _UC_PASSWORD:
348 if ((q = unquote(q)) != NULL && isdigit(*q))
349 config.password_days = atoi(q);
350 break;
351 case _UC_FIELDS:
352 case _UC_NONE:
353 break;
354 }
355 }
356 }
357 if (linecap > 0)
358 free(buf);
359 fclose(fp);
360 }
361 return &config;
362 }
363
364
365 int
366 write_userconfig(char const * file)
367 {
368 int fd;
369 int i, j;
370 struct sbuf *buf;
371 FILE *fp;
372
373 if (file == NULL)
374 file = _PATH_PW_CONF;
375
376 if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
377 return (0);
378
379 if ((fp = fdopen(fd, "w")) == NULL) {
380 close(fd);
381 return (0);
382 }
383
384 buf = sbuf_new_auto();
385 for (i = _UC_NONE; i < _UC_FIELDS; i++) {
386 int quote = 1;
387
388 sbuf_clear(buf);
389 switch (i) {
390 case _UC_DEFAULTPWD:
391 sbuf_cat(buf, boolean_str(config.default_password));
392 break;
393 case _UC_REUSEUID:
394 sbuf_cat(buf, boolean_str(config.reuse_uids));
395 break;
396 case _UC_REUSEGID:
397 sbuf_cat(buf, boolean_str(config.reuse_gids));
398 break;
399 case _UC_NISPASSWD:
400 sbuf_cat(buf, config.nispasswd ? config.nispasswd :
401 "");
402 quote = 0;
403 break;
404 case _UC_DOTDIR:
405 sbuf_cat(buf, config.dotdir ? config.dotdir :
406 boolean_str(0));
407 break;
408 case _UC_NEWMAIL:
409 sbuf_cat(buf, config.newmail ? config.newmail :
410 boolean_str(0));
411 break;
412 case _UC_LOGFILE:
413 sbuf_cat(buf, config.logfile ? config.logfile :
414 boolean_str(0));
415 break;
416 case _UC_HOMEROOT:
417 sbuf_cat(buf, config.home);
418 break;
419 case _UC_HOMEMODE:
420 sbuf_printf(buf, "%04o", config.homemode);
421 quote = 0;
422 break;
423 case _UC_SHELLPATH:
424 sbuf_cat(buf, config.shelldir);
425 break;
426 case _UC_SHELLS:
427 for (j = 0; j < _UC_MAXSHELLS &&
428 system_shells[j] != NULL; j++)
429 sbuf_printf(buf, "%s\"%s\"", j ?
430 "," : "", system_shells[j]);
431 quote = 0;
432 break;
433 case _UC_DEFAULTSHELL:
434 sbuf_cat(buf, config.shell_default ?
435 config.shell_default : bourne_shell);
436 break;
437 case _UC_DEFAULTGROUP:
438 sbuf_cat(buf, config.default_group ?
439 config.default_group : "");
440 break;
441 case _UC_EXTRAGROUPS:
442 for (j = 0; j < config.numgroups &&
443 config.groups[j] != NULL; j++)
444 sbuf_printf(buf, "%s\"%s\"", j ?
445 "," : "", config.groups[j]);
446 quote = 0;
447 break;
448 case _UC_DEFAULTCLASS:
449 sbuf_cat(buf, config.default_class ?
450 config.default_class : "");
451 break;
452 case _UC_MINUID:
453 sbuf_printf(buf, "%lu", (unsigned long) config.min_uid);
454 quote = 0;
455 break;
456 case _UC_MAXUID:
457 sbuf_printf(buf, "%lu", (unsigned long) config.max_uid);
458 quote = 0;
459 break;
460 case _UC_MINGID:
461 sbuf_printf(buf, "%lu", (unsigned long) config.min_gid);
462 quote = 0;
463 break;
464 case _UC_MAXGID:
465 sbuf_printf(buf, "%lu", (unsigned long) config.max_gid);
466 quote = 0;
467 break;
468 case _UC_EXPIRE:
469 sbuf_printf(buf, "%d", config.expire_days);
470 quote = 0;
471 break;
472 case _UC_PASSWORD:
473 sbuf_printf(buf, "%d", config.password_days);
474 quote = 0;
475 break;
476 case _UC_NONE:
477 break;
478 }
479 sbuf_finish(buf);
480
481 if (comments[i])
482 fputs(comments[i], fp);
483
484 if (*kwds[i]) {
485 if (quote)
486 fprintf(fp, "%s = \"%s\"\n", kwds[i],
487 sbuf_data(buf));
488 else
489 fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
490 #if debugging
491 printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
492 #endif
493 }
494 }
495 sbuf_delete(buf);
496 return (fclose(fp) != EOF);
497 }