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