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