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