]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pw/pw_conf.c
Document r349620, tzdata 2019b.
[FreeBSD/FreeBSD.git] / usr.sbin / pw / pw_conf.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (C) 1996
5  *      David L. Nugent.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #ifndef lint
30 static const char rcsid[] =
31   "$FreeBSD$";
32 #endif /* not lint */
33
34 #include <sys/types.h>
35 #include <sys/sbuf.h>
36
37 #include <err.h>
38 #include <fcntl.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 #include "pw.h"
43
44 #define debugging 0
45
46 enum {
47         _UC_NONE,
48         _UC_DEFAULTPWD,
49         _UC_REUSEUID,
50         _UC_REUSEGID,
51         _UC_NISPASSWD,
52         _UC_DOTDIR,
53         _UC_NEWMAIL,
54         _UC_LOGFILE,
55         _UC_HOMEROOT,
56         _UC_HOMEMODE,
57         _UC_SHELLPATH,
58         _UC_SHELLS,
59         _UC_DEFAULTSHELL,
60         _UC_DEFAULTGROUP,
61         _UC_EXTRAGROUPS,
62         _UC_DEFAULTCLASS,
63         _UC_MINUID,
64         _UC_MAXUID,
65         _UC_MINGID,
66         _UC_MAXGID,
67         _UC_EXPIRE,
68         _UC_PASSWORD,
69         _UC_FIELDS
70 };
71
72 static char     bourne_shell[] = "sh";
73
74 static char    *system_shells[_UC_MAXSHELLS] =
75 {
76         bourne_shell,
77         "csh",
78         "tcsh"
79 };
80
81 static char const *booltrue[] =
82 {
83         "yes", "true", "1", "on", NULL
84 };
85 static char const *boolfalse[] =
86 {
87         "no", "false", "0", "off", NULL
88 };
89
90 static struct userconf config =
91 {
92         0,                      /* Default password for new users? (nologin) */
93         0,                      /* Reuse uids? */
94         0,                      /* Reuse gids? */
95         NULL,                   /* NIS version of the passwd file */
96         "/usr/share/skel",      /* Where to obtain skeleton files */
97         NULL,                   /* Mail to send to new accounts */
98         "/var/log/userlog",     /* Where to log changes */
99         "/home",                /* Where to create home directory */
100         _DEF_DIRMODE,           /* Home directory perms, modified by umask */
101         "/bin",                 /* Where shells are located */
102         system_shells,          /* List of shells (first is default) */
103         bourne_shell,           /* Default shell */
104         NULL,                   /* Default group name */
105         NULL,                   /* Default (additional) groups */
106         NULL,                   /* Default login class */
107         1000, 32000,            /* Allowed range of uids */
108         1000, 32000,            /* Allowed range of gids */
109         0,                      /* Days until account expires */
110         0                       /* Days until password expires */
111 };
112
113 static char const *comments[_UC_FIELDS] =
114 {
115         "#\n# pw.conf - user/group configuration defaults\n#\n",
116         "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
117         "\n# Reuse gaps in uid sequence? (yes or no)\n",
118         "\n# Reuse gaps in gid sequence? (yes or no)\n",
119         "\n# Path to the NIS passwd file (blank or 'no' for none)\n",
120         "\n# Obtain default dotfiles from this directory\n",
121         "\n# Mail this file to new user (/etc/newuser.msg or no)\n",
122         "\n# Log add/change/remove information in this file\n",
123         "\n# Root directory in which $HOME directory is created\n",
124         "\n# Mode for the new $HOME directory, will be modified by umask\n",
125         "\n# Colon separated list of directories containing valid shells\n",
126         "\n# Comma separated list of available shells (without paths)\n",
127         "\n# Default shell (without path)\n",
128         "\n# Default group (leave blank for new group per user)\n",
129         "\n# Extra groups for new users\n",
130         "\n# Default login class for new users\n",
131         "\n# Range of valid default user ids\n",
132         NULL,
133         "\n# Range of valid default group ids\n",
134         NULL,
135         "\n# Days after which account expires (0=disabled)\n",
136         "\n# Days after which password expires (0=disabled)\n"
137 };
138
139 static char const *kwds[] =
140 {
141         "",
142         "defaultpasswd",
143         "reuseuids",
144         "reusegids",
145         "nispasswd",
146         "skeleton",
147         "newmail",
148         "logfile",
149         "home",
150         "homemode",
151         "shellpath",
152         "shells",
153         "defaultshell",
154         "defaultgroup",
155         "extragroups",
156         "defaultclass",
157         "minuid",
158         "maxuid",
159         "mingid",
160         "maxgid",
161         "expire_days",
162         "password_days",
163         NULL
164 };
165
166 static char    *
167 unquote(char const * str)
168 {
169         if (str && (*str == '"' || *str == '\'')) {
170                 char           *p = strchr(str + 1, *str);
171
172                 if (p != NULL)
173                         *p = '\0';
174                 return (char *) (*++str ? str : NULL);
175         }
176         return (char *) str;
177 }
178
179 int
180 boolean_val(char const * str, int dflt)
181 {
182         if ((str = unquote(str)) != NULL) {
183                 int             i;
184
185                 for (i = 0; booltrue[i]; i++)
186                         if (strcmp(str, booltrue[i]) == 0)
187                                 return 1;
188                 for (i = 0; boolfalse[i]; i++)
189                         if (strcmp(str, boolfalse[i]) == 0)
190                                 return 0;
191         }
192         return dflt;
193 }
194
195 int
196 passwd_val(char const * str, int dflt)
197 {
198         if ((str = unquote(str)) != NULL) {
199                 int             i;
200
201                 for (i = 0; booltrue[i]; i++)
202                         if (strcmp(str, booltrue[i]) == 0)
203                                 return P_YES;
204                 for (i = 0; boolfalse[i]; i++)
205                         if (strcmp(str, boolfalse[i]) == 0)
206                                 return P_NO;
207
208                 /*
209                  * Special cases for defaultpassword
210                  */
211                 if (strcmp(str, "random") == 0)
212                         return P_RANDOM;
213                 if (strcmp(str, "none") == 0)
214                         return P_NONE;
215
216                 errx(1, "Invalid value for default password");
217         }
218         return dflt;
219 }
220
221 char const     *
222 boolean_str(int val)
223 {
224         if (val == -1)
225                 return "random";
226         else if (val == -2)
227                 return "none";
228         else
229                 return val ? booltrue[0] : boolfalse[0];
230 }
231
232 char           *
233 newstr(char const * p)
234 {
235         char    *q;
236
237         if ((p = unquote(p)) == NULL)
238                 return (NULL);
239
240         if ((q = strdup(p)) == NULL)
241                 err(1, "strdup()");
242
243         return (q);
244 }
245
246 struct userconf *
247 read_userconfig(char const * file)
248 {
249         FILE    *fp;
250         char    *buf, *p;
251         const char *errstr;
252         size_t  linecap;
253         ssize_t linelen;
254
255         buf = NULL;
256         linecap = 0;
257
258         if (file == NULL)
259                 file = _PATH_PW_CONF;
260
261         if ((fp = fopen(file, "r")) == NULL)
262                 return (&config);
263
264         while ((linelen = getline(&buf, &linecap, fp)) > 0) {
265                 if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
266                         static char const toks[] = " \t\r\n,=";
267                         char           *q = strtok(NULL, toks);
268                         int             i = 0;
269                         mode_t          *modeset;
270
271                         while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
272                                 ++i;
273 #if debugging
274                         if (i == _UC_FIELDS)
275                                 printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
276                         else
277                                 printf("Got kwd[%s]=%s\n", p, q);
278 #endif
279                         switch (i) {
280                         case _UC_DEFAULTPWD:
281                                 config.default_password = passwd_val(q, 1);
282                                 break;
283                         case _UC_REUSEUID:
284                                 config.reuse_uids = boolean_val(q, 0);
285                                 break;
286                         case _UC_REUSEGID:
287                                 config.reuse_gids = boolean_val(q, 0);
288                                 break;
289                         case _UC_NISPASSWD:
290                                 config.nispasswd = (q == NULL || !boolean_val(q, 1))
291                                         ? NULL : newstr(q);
292                                 break;
293                         case _UC_DOTDIR:
294                                 config.dotdir = (q == NULL || !boolean_val(q, 1))
295                                         ? NULL : newstr(q);
296                                 break;
297                                 case _UC_NEWMAIL:
298                                 config.newmail = (q == NULL || !boolean_val(q, 1))
299                                         ? NULL : newstr(q);
300                                 break;
301                         case _UC_LOGFILE:
302                                 config.logfile = (q == NULL || !boolean_val(q, 1))
303                                         ? NULL : newstr(q);
304                                 break;
305                         case _UC_HOMEROOT:
306                                 config.home = (q == NULL || !boolean_val(q, 1))
307                                         ? "/home" : newstr(q);
308                                 break;
309                         case _UC_HOMEMODE:
310                                 modeset = setmode(q);
311                                 config.homemode = (q == NULL || !boolean_val(q, 1))
312                                         ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
313                                 free(modeset);
314                                 break;
315                         case _UC_SHELLPATH:
316                                 config.shelldir = (q == NULL || !boolean_val(q, 1))
317                                         ? "/bin" : newstr(q);
318                                 break;
319                         case _UC_SHELLS:
320                                 for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
321                                         system_shells[i] = newstr(q);
322                                 if (i > 0)
323                                         while (i < _UC_MAXSHELLS)
324                                                 system_shells[i++] = NULL;
325                                 break;
326                         case _UC_DEFAULTSHELL:
327                                 config.shell_default = (q == NULL || !boolean_val(q, 1))
328                                         ? (char *) bourne_shell : newstr(q);
329                                 break;
330                         case _UC_DEFAULTGROUP:
331                                 q = unquote(q);
332                                 config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
333                                         ? NULL : newstr(q);
334                                 break;
335                         case _UC_EXTRAGROUPS:
336                                 while ((q = strtok(NULL, toks)) != NULL) {
337                                         if (config.groups == NULL)
338                                                 config.groups = sl_init();
339                                         sl_add(config.groups, newstr(q));
340                                 }
341                                 break;
342                         case _UC_DEFAULTCLASS:
343                                 config.default_class = (q == NULL || !boolean_val(q, 1))
344                                         ? NULL : newstr(q);
345                                 break;
346                         case _UC_MINUID:
347                                 if ((q = unquote(q)) != NULL) {
348                                         config.min_uid = strtounum(q, 0,
349                                             UID_MAX, &errstr);
350                                         if (errstr)
351                                                 warnx("Invalid min_uid: '%s';"
352                                                     " ignoring", q);
353                                 }
354                                 break;
355                         case _UC_MAXUID:
356                                 if ((q = unquote(q)) != NULL) {
357                                         config.max_uid = strtounum(q, 0,
358                                             UID_MAX, &errstr);
359                                         if (errstr)
360                                                 warnx("Invalid max_uid: '%s';"
361                                                     " ignoring", q);
362                                 }
363                                 break;
364                         case _UC_MINGID:
365                                 if ((q = unquote(q)) != NULL) {
366                                         config.min_gid = strtounum(q, 0,
367                                             GID_MAX, &errstr);
368                                         if (errstr)
369                                                 warnx("Invalid min_gid: '%s';"
370                                                     " ignoring", q);
371                                 }
372                                 break;
373                         case _UC_MAXGID:
374                                 if ((q = unquote(q)) != NULL) {
375                                         config.max_gid = strtounum(q, 0,
376                                             GID_MAX, &errstr);
377                                         if (errstr)
378                                                 warnx("Invalid max_gid: '%s';"
379                                                     " ignoring", q);
380                                 }
381                                 break;
382                         case _UC_EXPIRE:
383                                 if ((q = unquote(q)) != NULL) {
384                                         config.expire_days = strtonum(q, 0,
385                                             INT_MAX, &errstr);
386                                         if (errstr)
387                                                 warnx("Invalid expire days:"
388                                                     " '%s'; ignoring", q);
389                                 }
390                                 break;
391                         case _UC_PASSWORD:
392                                 if ((q = unquote(q)) != NULL) {
393                                         config.password_days = strtonum(q, 0,
394                                             INT_MAX, &errstr);
395                                         if (errstr)
396                                                 warnx("Invalid password days:"
397                                                     " '%s'; ignoring", q);
398                                 }
399                                 break;
400                         case _UC_FIELDS:
401                         case _UC_NONE:
402                                 break;
403                         }
404                 }
405         }
406         free(buf);
407         fclose(fp);
408
409         return (&config);
410 }
411
412
413 int
414 write_userconfig(struct userconf *cnf, const char *file)
415 {
416         int             fd;
417         int             i, j;
418         struct sbuf     *buf;
419         FILE           *fp;
420
421         if (file == NULL)
422                 file = _PATH_PW_CONF;
423
424         if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
425                 return (0);
426
427         if ((fp = fdopen(fd, "w")) == NULL) {
428                 close(fd);
429                 return (0);
430         }
431                         
432         buf = sbuf_new_auto();
433         for (i = _UC_NONE; i < _UC_FIELDS; i++) {
434                 int             quote = 1;
435
436                 sbuf_clear(buf);
437                 switch (i) {
438                 case _UC_DEFAULTPWD:
439                         sbuf_cat(buf, boolean_str(cnf->default_password));
440                         break;
441                 case _UC_REUSEUID:
442                         sbuf_cat(buf, boolean_str(cnf->reuse_uids));
443                         break;
444                 case _UC_REUSEGID:
445                         sbuf_cat(buf, boolean_str(cnf->reuse_gids));
446                         break;
447                 case _UC_NISPASSWD:
448                         sbuf_cat(buf, cnf->nispasswd ?  cnf->nispasswd : "");
449                         quote = 0;
450                         break;
451                 case _UC_DOTDIR:
452                         sbuf_cat(buf, cnf->dotdir ?  cnf->dotdir :
453                             boolean_str(0));
454                         break;
455                 case _UC_NEWMAIL:
456                         sbuf_cat(buf, cnf->newmail ?  cnf->newmail :
457                             boolean_str(0));
458                         break;
459                 case _UC_LOGFILE:
460                         sbuf_cat(buf, cnf->logfile ?  cnf->logfile :
461                             boolean_str(0));
462                         break;
463                 case _UC_HOMEROOT:
464                         sbuf_cat(buf, cnf->home);
465                         break;
466                 case _UC_HOMEMODE:
467                         sbuf_printf(buf, "%04o", cnf->homemode);
468                         quote = 0;
469                         break;
470                 case _UC_SHELLPATH:
471                         sbuf_cat(buf, cnf->shelldir);
472                         break;
473                 case _UC_SHELLS:
474                         for (j = 0; j < _UC_MAXSHELLS &&
475                             system_shells[j] != NULL; j++)
476                                 sbuf_printf(buf, "%s\"%s\"", j ?
477                                     "," : "", system_shells[j]);
478                         quote = 0;
479                         break;
480                 case _UC_DEFAULTSHELL:
481                         sbuf_cat(buf, cnf->shell_default ?
482                             cnf->shell_default : bourne_shell);
483                         break;
484                 case _UC_DEFAULTGROUP:
485                         sbuf_cat(buf, cnf->default_group ?
486                             cnf->default_group : "");
487                         break;
488                 case _UC_EXTRAGROUPS:
489                         for (j = 0; cnf->groups != NULL &&
490                             j < (int)cnf->groups->sl_cur; j++)
491                                 sbuf_printf(buf, "%s\"%s\"", j ?
492                                     "," : "", cnf->groups->sl_str[j]);
493                         quote = 0;
494                         break;
495                 case _UC_DEFAULTCLASS:
496                         sbuf_cat(buf, cnf->default_class ?
497                             cnf->default_class : "");
498                         break;
499                 case _UC_MINUID:
500                         sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_uid);
501                         quote = 0;
502                         break;
503                 case _UC_MAXUID:
504                         sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_uid);
505                         quote = 0;
506                         break;
507                 case _UC_MINGID:
508                         sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_gid);
509                         quote = 0;
510                         break;
511                 case _UC_MAXGID:
512                         sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_gid);
513                         quote = 0;
514                         break;
515                 case _UC_EXPIRE:
516                         sbuf_printf(buf, "%jd", (intmax_t)cnf->expire_days);
517                         quote = 0;
518                         break;
519                 case _UC_PASSWORD:
520                         sbuf_printf(buf, "%jd", (intmax_t)cnf->password_days);
521                         quote = 0;
522                         break;
523                 case _UC_NONE:
524                         break;
525                 }
526                 sbuf_finish(buf);
527
528                 if (comments[i])
529                         fputs(comments[i], fp);
530
531                 if (*kwds[i]) {
532                         if (quote)
533                                 fprintf(fp, "%s = \"%s\"\n", kwds[i],
534                                     sbuf_data(buf));
535                         else
536                                 fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
537 #if debugging
538                         printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
539 #endif
540                 }
541         }
542         sbuf_delete(buf);
543         return (fclose(fp) != EOF);
544 }