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