Merge branch 'vendor/GCC50'
[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.16 2011/03/08 20:13:29 jkim Exp $
27  */
28
29 #include <string.h>
30 #include <ctype.h>
31 #include <fcntl.h>
32
33 #include "pw.h"
34
35 #define debugging 0
36
37 enum {
38         _UC_NONE,
39         _UC_DEFAULTPWD,
40         _UC_REUSEUID,
41         _UC_REUSEGID,
42         _UC_NISPASSWD,
43         _UC_DOTDIR,
44         _UC_NEWMAIL,
45         _UC_LOGFILE,
46         _UC_HOMEROOT,
47         _UC_HOMEMODE,
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         _DEF_DIRMODE,           /* Home directory perms, modified by umask */
92         "/bin",                 /* Where shells are located */
93         system_shells,          /* List of shells (first is default) */
94         bourne_shell,           /* Default shell */
95         NULL,                   /* Default group name */
96         NULL,                   /* Default (additional) groups */
97         NULL,                   /* Default login class */
98         1000, 32000,            /* Allowed range of uids */
99         1000, 32000,            /* Allowed range of gids */
100         0,                      /* Days until account expires */
101         0,                      /* Days until password expires */
102         0                       /* size of default_group array */
103 };
104
105 static char const *comments[_UC_FIELDS] =
106 {
107         "#\n# pw.conf - user/group configuration defaults\n#\n",
108         "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
109         "\n# Reuse gaps in uid sequence? (yes or no)\n",
110         "\n# Reuse gaps in gid sequence? (yes or no)\n",
111         "\n# Path to the NIS passwd file (blank or 'no' for none)\n",
112         "\n# Obtain default dotfiles from this directory\n",
113         "\n# Mail this file to new user (/etc/newuser.msg or no)\n",
114         "\n# Log add/change/remove information in this file\n",
115         "\n# Root directory in which $HOME directory is created\n",
116         "\n# Mode for the new $HOME directory, will be modified by umask\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         "homemode",
143         "shellpath",
144         "shells",
145         "defaultshell",
146         "defaultgroup",
147         "extragroups",
148         "defaultclass",
149         "minuid",
150         "maxuid",
151         "mingid",
152         "maxgid",
153         "expire_days",
154         "password_days",
155         NULL
156 };
157
158 static char    *
159 unquote(char const * str)
160 {
161         if (str && (*str == '"' || *str == '\'')) {
162                 char           *p = strchr(str + 1, *str);
163
164                 if (p != NULL)
165                         *p = '\0';
166                 return (char *) (*++str ? str : NULL);
167         }
168         return (char *) str;
169 }
170
171 int
172 boolean_val(char const * str, int dflt)
173 {
174         if ((str = unquote(str)) != NULL) {
175                 int             i;
176
177                 for (i = 0; booltrue[i]; i++)
178                         if (strcmp(str, booltrue[i]) == 0)
179                                 return 1;
180                 for (i = 0; boolfalse[i]; i++)
181                         if (strcmp(str, boolfalse[i]) == 0)
182                                 return 0;
183
184                 /*
185                  * Special cases for defaultpassword
186                  */
187                 if (strcmp(str, "random") == 0)
188                         return -1;
189                 if (strcmp(str, "none") == 0)
190                         return -2;
191         }
192         return dflt;
193 }
194
195 char const     *
196 boolean_str(int val)
197 {
198         if (val == -1)
199                 return "random";
200         else if (val == -2)
201                 return "none";
202         else
203                 return val ? booltrue[0] : boolfalse[0];
204 }
205
206 char           *
207 newstr(char const * p)
208 {
209         char           *q = NULL;
210
211         if ((p = unquote(p)) != NULL) {
212                 int             l = strlen(p) + 1;
213
214                 if ((q = malloc(l)) != NULL)
215                         memcpy(q, p, l);
216         }
217         return q;
218 }
219
220 #define LNBUFSZ 1024
221
222
223 struct userconf *
224 read_userconfig(char const * file)
225 {
226         FILE           *fp;
227
228         extendarray(&config.groups, &config.numgroups, 200);
229         memset(config.groups, 0, config.numgroups * sizeof(char *));
230         if (file == NULL)
231                 file = _PATH_PW_CONF;
232         if ((fp = fopen(file, "r")) != NULL) {
233                 int         buflen = LNBUFSZ;
234                 char       *buf = malloc(buflen);
235
236         nextline:
237                 while (fgets(buf, buflen, fp) != NULL) {
238                         char           *p;
239
240                         while ((p = strchr(buf, '\n')) == NULL) {
241                                 int       l;
242                                 if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
243                                         int     ch;
244                                         while ((ch = fgetc(fp)) != '\n' && ch != EOF);
245                                         goto nextline;  /* Ignore it */
246                                 }
247                                 l = strlen(buf);
248                                 if (fgets(buf + l, buflen - l, fp) == NULL)
249                                         break;  /* Unterminated last line */
250                         }
251
252                         if (p != NULL)
253                                 *p = '\0';
254
255                         if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
256                                 static char const toks[] = " \t\r\n,=";
257                                 char           *q = strtok(NULL, toks);
258                                 int             i = 0;
259                                 mode_t          *modeset;
260
261                                 while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
262                                         ++i;
263 #if debugging
264                                 if (i == _UC_FIELDS)
265                                         printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
266                                 else
267                                         printf("Got kwd[%s]=%s\n", p, q);
268 #endif
269                                 switch (i) {
270                                 case _UC_DEFAULTPWD:
271                                         config.default_password = boolean_val(q, 1);
272                                         break;
273                                 case _UC_REUSEUID:
274                                         config.reuse_uids = boolean_val(q, 0);
275                                         break;
276                                 case _UC_REUSEGID:
277                                         config.reuse_gids = boolean_val(q, 0);
278                                         break;
279                                 case _UC_NISPASSWD:
280                                         config.nispasswd = (q == NULL || !boolean_val(q, 1))
281                                                 ? NULL : newstr(q);
282                                         break;
283                                 case _UC_DOTDIR:
284                                         config.dotdir = (q == NULL || !boolean_val(q, 1))
285                                                 ? NULL : newstr(q);
286                                         break;
287                                 case _UC_NEWMAIL:
288                                         config.newmail = (q == NULL || !boolean_val(q, 1))
289                                                 ? NULL : newstr(q);
290                                         break;
291                                 case _UC_LOGFILE:
292                                         config.logfile = (q == NULL || !boolean_val(q, 1))
293                                                 ? NULL : newstr(q);
294                                         break;
295                                 case _UC_HOMEROOT:
296                                         config.home = (q == NULL || !boolean_val(q, 1))
297                                                 ? "/home" : newstr(q);
298                                         break;
299                                 case _UC_HOMEMODE:
300                                         modeset = setmode(q);
301                                         config.homemode = (q == NULL || !boolean_val(q, 1))
302                                                 ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
303                                         free(modeset);
304                                         break;
305                                 case _UC_SHELLPATH:
306                                         config.shelldir = (q == NULL || !boolean_val(q, 1))
307                                                 ? "/bin" : newstr(q);
308                                         break;
309                                 case _UC_SHELLS:
310                                         for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
311                                                 system_shells[i] = newstr(q);
312                                         if (i > 0)
313                                                 while (i < _UC_MAXSHELLS)
314                                                         system_shells[i++] = NULL;
315                                         break;
316                                 case _UC_DEFAULTSHELL:
317                                         config.shell_default = (q == NULL || !boolean_val(q, 1))
318                                                 ? (char *) bourne_shell : newstr(q);
319                                         break;
320                                 case _UC_DEFAULTGROUP:
321                                         q = unquote(q);
322                                         config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
323                                                 ? NULL : newstr(q);
324                                         break;
325                                 case _UC_EXTRAGROUPS:
326                                         for (i = 0; q != NULL; q = strtok(NULL, toks)) {
327                                                 if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
328                                                         config.groups[i++] = newstr(q);
329                                         }
330                                         if (i > 0)
331                                                 while (i < config.numgroups)
332                                                         config.groups[i++] = NULL;
333                                         break;
334                                 case _UC_DEFAULTCLASS:
335                                         config.default_class = (q == NULL || !boolean_val(q, 1))
336                                                 ? NULL : newstr(q);
337                                         break;
338                                 case _UC_MINUID:
339                                         if ((q = unquote(q)) != NULL && isdigit(*q))
340                                                 config.min_uid = (uid_t) atol(q);
341                                         break;
342                                 case _UC_MAXUID:
343                                         if ((q = unquote(q)) != NULL && isdigit(*q))
344                                                 config.max_uid = (uid_t) atol(q);
345                                         break;
346                                 case _UC_MINGID:
347                                         if ((q = unquote(q)) != NULL && isdigit(*q))
348                                                 config.min_gid = (gid_t) atol(q);
349                                         break;
350                                 case _UC_MAXGID:
351                                         if ((q = unquote(q)) != NULL && isdigit(*q))
352                                                 config.max_gid = (gid_t) atol(q);
353                                         break;
354                                 case _UC_EXPIRE:
355                                         if ((q = unquote(q)) != NULL && isdigit(*q))
356                                                 config.expire_days = atoi(q);
357                                         break;
358                                 case _UC_PASSWORD:
359                                         if ((q = unquote(q)) != NULL && isdigit(*q))
360                                                 config.password_days = atoi(q);
361                                         break;
362                                 case _UC_FIELDS:
363                                 case _UC_NONE:
364                                         break;
365                                 }
366                         }
367                 }
368                 free(buf);
369                 fclose(fp);
370         }
371         return &config;
372 }
373
374
375 int
376 write_userconfig(char const * file)
377 {
378         int             fd;
379
380         if (file == NULL)
381                 file = _PATH_PW_CONF;
382
383         if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
384                 FILE           *fp;
385
386                 if ((fp = fdopen(fd, "w")) == NULL)
387                         close(fd);
388                 else {
389                         int             i, j, k;
390                         int             len = LNBUFSZ;
391                         char           *buf = malloc(len);
392
393                         for (i = _UC_NONE; i < _UC_FIELDS; i++) {
394                                 int             quote = 1;
395                                 char const     *val = buf;
396
397                                 *buf = '\0';
398                                 switch (i) {
399                                 case _UC_DEFAULTPWD:
400                                         val = boolean_str(config.default_password);
401                                         break;
402                                 case _UC_REUSEUID:
403                                         val = boolean_str(config.reuse_uids);
404                                         break;
405                                 case _UC_REUSEGID:
406                                         val = boolean_str(config.reuse_gids);
407                                         break;
408                                 case _UC_NISPASSWD:
409                                         val = config.nispasswd ? config.nispasswd : "";
410                                         quote = 0;
411                                         break;
412                                 case _UC_DOTDIR:
413                                         val = config.dotdir ? config.dotdir : boolean_str(0);
414                                         break;
415                                 case _UC_NEWMAIL:
416                                         val = config.newmail ? config.newmail : boolean_str(0);
417                                         break;
418                                 case _UC_LOGFILE:
419                                         val = config.logfile ? config.logfile : boolean_str(0);
420                                         break;
421                                 case _UC_HOMEROOT:
422                                         val = config.home;
423                                         break;
424                                 case _UC_HOMEMODE:
425                                         sprintf(buf, "%04o", config.homemode);
426                                         quote = 0;
427                                         break;
428                                 case _UC_SHELLPATH:
429                                         val = config.shelldir;
430                                         break;
431                                 case _UC_SHELLS:
432                                         for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
433                                                 char    lbuf[64];
434                                                 int     l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
435                                                 if (l < 0)
436                                                         l = 0;
437                                                 if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
438                                                         strcpy(buf + k, lbuf);
439                                                         k += l;
440                                                 }
441                                         }
442                                         quote = 0;
443                                         break;
444                                 case _UC_DEFAULTSHELL:
445                                         val = config.shell_default ? config.shell_default : bourne_shell;
446                                         break;
447                                 case _UC_DEFAULTGROUP:
448                                         val = config.default_group ? config.default_group : "";
449                                         break;
450                                 case _UC_EXTRAGROUPS:
451                                         extendarray(&config.groups, &config.numgroups, 200);
452                                         for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
453                                                 char    lbuf[64];
454                                                 int     l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
455                                                 if (l < 0)
456                                                         l = 0;
457                                                 if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
458                                                         strcpy(buf + k, lbuf);
459                                                         k +=  l;
460                                                 }
461                                         }
462                                         quote = 0;
463                                         break;
464                                 case _UC_DEFAULTCLASS:
465                                         val = config.default_class ? config.default_class : "";
466                                         break;
467                                 case _UC_MINUID:
468                                         sprintf(buf, "%lu", (unsigned long) config.min_uid);
469                                         quote = 0;
470                                         break;
471                                 case _UC_MAXUID:
472                                         sprintf(buf, "%lu", (unsigned long) config.max_uid);
473                                         quote = 0;
474                                         break;
475                                 case _UC_MINGID:
476                                         sprintf(buf, "%lu", (unsigned long) config.min_gid);
477                                         quote = 0;
478                                         break;
479                                 case _UC_MAXGID:
480                                         sprintf(buf, "%lu", (unsigned long) config.max_gid);
481                                         quote = 0;
482                                         break;
483                                 case _UC_EXPIRE:
484                                         sprintf(buf, "%d", config.expire_days);
485                                         quote = 0;
486                                         break;
487                                 case _UC_PASSWORD:
488                                         sprintf(buf, "%d", config.password_days);
489                                         quote = 0;
490                                         break;
491                                 case _UC_NONE:
492                                         break;
493                                 }
494
495                                 if (comments[i])
496                                         fputs(comments[i], fp);
497
498                                 if (*kwds[i]) {
499                                         if (quote)
500                                                 fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
501                                         else
502                                                 fprintf(fp, "%s = %s\n", kwds[i], val);
503 #if debugging
504                                         printf("WROTE: %s = %s\n", kwds[i], val);
505 #endif
506                                 }
507                         }
508                         free(buf);
509                         return fclose(fp) != EOF;
510                 }
511         }
512         return 0;
513 }