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