]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pw/pw_conf.c
- Switch releng/12.1 from RC2 to RELEASE.
[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 ((fp = fopen(file, "r")) == NULL)
259                 return (&config);
260
261         while ((linelen = getline(&buf, &linecap, fp)) > 0) {
262                 if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
263                         static char const toks[] = " \t\r\n,=";
264                         char           *q = strtok(NULL, toks);
265                         int             i = 0;
266                         mode_t          *modeset;
267
268                         while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
269                                 ++i;
270 #if debugging
271                         if (i == _UC_FIELDS)
272                                 printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
273                         else
274                                 printf("Got kwd[%s]=%s\n", p, q);
275 #endif
276                         switch (i) {
277                         case _UC_DEFAULTPWD:
278                                 config.default_password = passwd_val(q, 1);
279                                 break;
280                         case _UC_REUSEUID:
281                                 config.reuse_uids = boolean_val(q, 0);
282                                 break;
283                         case _UC_REUSEGID:
284                                 config.reuse_gids = boolean_val(q, 0);
285                                 break;
286                         case _UC_NISPASSWD:
287                                 config.nispasswd = (q == NULL || !boolean_val(q, 1))
288                                         ? NULL : newstr(q);
289                                 break;
290                         case _UC_DOTDIR:
291                                 config.dotdir = (q == NULL || !boolean_val(q, 1))
292                                         ? NULL : newstr(q);
293                                 break;
294                                 case _UC_NEWMAIL:
295                                 config.newmail = (q == NULL || !boolean_val(q, 1))
296                                         ? NULL : newstr(q);
297                                 break;
298                         case _UC_LOGFILE:
299                                 config.logfile = (q == NULL || !boolean_val(q, 1))
300                                         ? NULL : newstr(q);
301                                 break;
302                         case _UC_HOMEROOT:
303                                 config.home = (q == NULL || !boolean_val(q, 1))
304                                         ? "/home" : newstr(q);
305                                 break;
306                         case _UC_HOMEMODE:
307                                 modeset = setmode(q);
308                                 config.homemode = (q == NULL || !boolean_val(q, 1))
309                                         ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
310                                 free(modeset);
311                                 break;
312                         case _UC_SHELLPATH:
313                                 config.shelldir = (q == NULL || !boolean_val(q, 1))
314                                         ? "/bin" : newstr(q);
315                                 break;
316                         case _UC_SHELLS:
317                                 for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
318                                         system_shells[i] = newstr(q);
319                                 if (i > 0)
320                                         while (i < _UC_MAXSHELLS)
321                                                 system_shells[i++] = NULL;
322                                 break;
323                         case _UC_DEFAULTSHELL:
324                                 config.shell_default = (q == NULL || !boolean_val(q, 1))
325                                         ? (char *) bourne_shell : newstr(q);
326                                 break;
327                         case _UC_DEFAULTGROUP:
328                                 q = unquote(q);
329                                 config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
330                                         ? NULL : newstr(q);
331                                 break;
332                         case _UC_EXTRAGROUPS:
333                                 while ((q = strtok(NULL, toks)) != NULL) {
334                                         if (config.groups == NULL)
335                                                 config.groups = sl_init();
336                                         sl_add(config.groups, newstr(q));
337                                 }
338                                 break;
339                         case _UC_DEFAULTCLASS:
340                                 config.default_class = (q == NULL || !boolean_val(q, 1))
341                                         ? NULL : newstr(q);
342                                 break;
343                         case _UC_MINUID:
344                                 if ((q = unquote(q)) != NULL) {
345                                         config.min_uid = strtounum(q, 0,
346                                             UID_MAX, &errstr);
347                                         if (errstr)
348                                                 warnx("Invalid min_uid: '%s';"
349                                                     " ignoring", q);
350                                 }
351                                 break;
352                         case _UC_MAXUID:
353                                 if ((q = unquote(q)) != NULL) {
354                                         config.max_uid = strtounum(q, 0,
355                                             UID_MAX, &errstr);
356                                         if (errstr)
357                                                 warnx("Invalid max_uid: '%s';"
358                                                     " ignoring", q);
359                                 }
360                                 break;
361                         case _UC_MINGID:
362                                 if ((q = unquote(q)) != NULL) {
363                                         config.min_gid = strtounum(q, 0,
364                                             GID_MAX, &errstr);
365                                         if (errstr)
366                                                 warnx("Invalid min_gid: '%s';"
367                                                     " ignoring", q);
368                                 }
369                                 break;
370                         case _UC_MAXGID:
371                                 if ((q = unquote(q)) != NULL) {
372                                         config.max_gid = strtounum(q, 0,
373                                             GID_MAX, &errstr);
374                                         if (errstr)
375                                                 warnx("Invalid max_gid: '%s';"
376                                                     " ignoring", q);
377                                 }
378                                 break;
379                         case _UC_EXPIRE:
380                                 if ((q = unquote(q)) != NULL) {
381                                         config.expire_days = strtonum(q, 0,
382                                             INT_MAX, &errstr);
383                                         if (errstr)
384                                                 warnx("Invalid expire days:"
385                                                     " '%s'; ignoring", q);
386                                 }
387                                 break;
388                         case _UC_PASSWORD:
389                                 if ((q = unquote(q)) != NULL) {
390                                         config.password_days = strtonum(q, 0,
391                                             INT_MAX, &errstr);
392                                         if (errstr)
393                                                 warnx("Invalid password days:"
394                                                     " '%s'; ignoring", q);
395                                 }
396                                 break;
397                         case _UC_FIELDS:
398                         case _UC_NONE:
399                                 break;
400                         }
401                 }
402         }
403         free(buf);
404         fclose(fp);
405
406         return (&config);
407 }
408
409
410 int
411 write_userconfig(struct userconf *cnf, const char *file)
412 {
413         int             fd;
414         int             i, j;
415         struct sbuf     *buf;
416         FILE           *fp;
417         char            cfgfile[MAXPATHLEN];
418
419         if (file == NULL) {
420                 snprintf(cfgfile, sizeof(cfgfile), "%s/" _PW_CONF,
421                     conf.etcpath);
422                 file = cfgfile;
423         }
424
425         if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
426                 return (0);
427
428         if ((fp = fdopen(fd, "w")) == NULL) {
429                 close(fd);
430                 return (0);
431         }
432                         
433         buf = sbuf_new_auto();
434         for (i = _UC_NONE; i < _UC_FIELDS; i++) {
435                 int             quote = 1;
436
437                 sbuf_clear(buf);
438                 switch (i) {
439                 case _UC_DEFAULTPWD:
440                         sbuf_cat(buf, boolean_str(cnf->default_password));
441                         break;
442                 case _UC_REUSEUID:
443                         sbuf_cat(buf, boolean_str(cnf->reuse_uids));
444                         break;
445                 case _UC_REUSEGID:
446                         sbuf_cat(buf, boolean_str(cnf->reuse_gids));
447                         break;
448                 case _UC_NISPASSWD:
449                         sbuf_cat(buf, cnf->nispasswd ?  cnf->nispasswd : "");
450                         quote = 0;
451                         break;
452                 case _UC_DOTDIR:
453                         sbuf_cat(buf, cnf->dotdir ?  cnf->dotdir :
454                             boolean_str(0));
455                         break;
456                 case _UC_NEWMAIL:
457                         sbuf_cat(buf, cnf->newmail ?  cnf->newmail :
458                             boolean_str(0));
459                         break;
460                 case _UC_LOGFILE:
461                         sbuf_cat(buf, cnf->logfile ?  cnf->logfile :
462                             boolean_str(0));
463                         break;
464                 case _UC_HOMEROOT:
465                         sbuf_cat(buf, cnf->home);
466                         break;
467                 case _UC_HOMEMODE:
468                         sbuf_printf(buf, "%04o", cnf->homemode);
469                         quote = 0;
470                         break;
471                 case _UC_SHELLPATH:
472                         sbuf_cat(buf, cnf->shelldir);
473                         break;
474                 case _UC_SHELLS:
475                         for (j = 0; j < _UC_MAXSHELLS &&
476                             system_shells[j] != NULL; j++)
477                                 sbuf_printf(buf, "%s\"%s\"", j ?
478                                     "," : "", system_shells[j]);
479                         quote = 0;
480                         break;
481                 case _UC_DEFAULTSHELL:
482                         sbuf_cat(buf, cnf->shell_default ?
483                             cnf->shell_default : bourne_shell);
484                         break;
485                 case _UC_DEFAULTGROUP:
486                         sbuf_cat(buf, cnf->default_group ?
487                             cnf->default_group : "");
488                         break;
489                 case _UC_EXTRAGROUPS:
490                         for (j = 0; cnf->groups != NULL &&
491                             j < (int)cnf->groups->sl_cur; j++)
492                                 sbuf_printf(buf, "%s\"%s\"", j ?
493                                     "," : "", cnf->groups->sl_str[j]);
494                         quote = 0;
495                         break;
496                 case _UC_DEFAULTCLASS:
497                         sbuf_cat(buf, cnf->default_class ?
498                             cnf->default_class : "");
499                         break;
500                 case _UC_MINUID:
501                         sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_uid);
502                         quote = 0;
503                         break;
504                 case _UC_MAXUID:
505                         sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_uid);
506                         quote = 0;
507                         break;
508                 case _UC_MINGID:
509                         sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_gid);
510                         quote = 0;
511                         break;
512                 case _UC_MAXGID:
513                         sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_gid);
514                         quote = 0;
515                         break;
516                 case _UC_EXPIRE:
517                         sbuf_printf(buf, "%jd", (intmax_t)cnf->expire_days);
518                         quote = 0;
519                         break;
520                 case _UC_PASSWORD:
521                         sbuf_printf(buf, "%jd", (intmax_t)cnf->password_days);
522                         quote = 0;
523                         break;
524                 case _UC_NONE:
525                         break;
526                 }
527                 sbuf_finish(buf);
528
529                 if (comments[i])
530                         fputs(comments[i], fp);
531
532                 if (*kwds[i]) {
533                         if (quote)
534                                 fprintf(fp, "%s = \"%s\"\n", kwds[i],
535                                     sbuf_data(buf));
536                         else
537                                 fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
538 #if debugging
539                         printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
540 #endif
541                 }
542         }
543         sbuf_delete(buf);
544         return (fclose(fp) != EOF);
545 }