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