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