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