]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.sbin/pw/pw_conf.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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 #include <string.h>
35 #include <ctype.h>
36 #include <fcntl.h>
37 #include <err.h>
38
39 #include "pw.h"
40
41 #define debugging 0
42
43 enum {
44         _UC_NONE,
45         _UC_DEFAULTPWD,
46         _UC_REUSEUID,
47         _UC_REUSEGID,
48         _UC_NISPASSWD,
49         _UC_DOTDIR,
50         _UC_NEWMAIL,
51         _UC_LOGFILE,
52         _UC_HOMEROOT,
53         _UC_HOMEMODE,
54         _UC_SHELLPATH,
55         _UC_SHELLS,
56         _UC_DEFAULTSHELL,
57         _UC_DEFAULTGROUP,
58         _UC_EXTRAGROUPS,
59         _UC_DEFAULTCLASS,
60         _UC_MINUID,
61         _UC_MAXUID,
62         _UC_MINGID,
63         _UC_MAXGID,
64         _UC_EXPIRE,
65         _UC_PASSWORD,
66         _UC_FIELDS
67 };
68
69 static char     bourne_shell[] = "sh";
70
71 static char    *system_shells[_UC_MAXSHELLS] =
72 {
73         bourne_shell,
74         "csh",
75         "tcsh"
76 };
77
78 static char const *booltrue[] =
79 {
80         "yes", "true", "1", "on", NULL
81 };
82 static char const *boolfalse[] =
83 {
84         "no", "false", "0", "off", NULL
85 };
86
87 static struct userconf config =
88 {
89         0,                      /* Default password for new users? (nologin) */
90         0,                      /* Reuse uids? */
91         0,                      /* Reuse gids? */
92         NULL,                   /* NIS version of the passwd file */
93         "/usr/share/skel",      /* Where to obtain skeleton files */
94         NULL,                   /* Mail to send to new accounts */
95         "/var/log/userlog",     /* Where to log changes */
96         "/home",                /* Where to create home directory */
97         _DEF_DIRMODE,           /* Home directory perms, modified by umask */
98         "/bin",                 /* Where shells are located */
99         system_shells,          /* List of shells (first is default) */
100         bourne_shell,           /* Default shell */
101         NULL,                   /* Default group name */
102         NULL,                   /* Default (additional) groups */
103         NULL,                   /* Default login class */
104         1000, 32000,            /* Allowed range of uids */
105         1000, 32000,            /* Allowed range of gids */
106         0,                      /* Days until account expires */
107         0,                      /* Days until password expires */
108         0                       /* size of default_group array */
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         size_t  linecap;
232         ssize_t linelen;
233
234         buf = NULL;
235         linecap = 0;
236
237         config.numgroups = 200;
238         config.groups = calloc(config.numgroups, sizeof(char *));
239         if (config.groups == NULL)
240                 err(1, "calloc()");
241         if (file == NULL)
242                 file = _PATH_PW_CONF;
243
244         if ((fp = fopen(file, "r")) == NULL)
245                 return (&config);
246
247         while ((linelen = getline(&buf, &linecap, fp)) > 0) {
248                 if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
249                         static char const toks[] = " \t\r\n,=";
250                         char           *q = strtok(NULL, toks);
251                         int             i = 0;
252                         mode_t          *modeset;
253
254                         while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
255                                 ++i;
256 #if debugging
257                         if (i == _UC_FIELDS)
258                                 printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
259                         else
260                                 printf("Got kwd[%s]=%s\n", p, q);
261 #endif
262                         switch (i) {
263                         case _UC_DEFAULTPWD:
264                                 config.default_password = boolean_val(q, 1);
265                                 break;
266                         case _UC_REUSEUID:
267                                 config.reuse_uids = boolean_val(q, 0);
268                                 break;
269                         case _UC_REUSEGID:
270                                 config.reuse_gids = boolean_val(q, 0);
271                                 break;
272                         case _UC_NISPASSWD:
273                                 config.nispasswd = (q == NULL || !boolean_val(q, 1))
274                                         ? NULL : newstr(q);
275                                 break;
276                         case _UC_DOTDIR:
277                                 config.dotdir = (q == NULL || !boolean_val(q, 1))
278                                         ? NULL : newstr(q);
279                                 break;
280                                 case _UC_NEWMAIL:
281                                 config.newmail = (q == NULL || !boolean_val(q, 1))
282                                         ? NULL : newstr(q);
283                                 break;
284                         case _UC_LOGFILE:
285                                 config.logfile = (q == NULL || !boolean_val(q, 1))
286                                         ? NULL : newstr(q);
287                                 break;
288                         case _UC_HOMEROOT:
289                                 config.home = (q == NULL || !boolean_val(q, 1))
290                                         ? "/home" : newstr(q);
291                                 break;
292                         case _UC_HOMEMODE:
293                                 modeset = setmode(q);
294                                 config.homemode = (q == NULL || !boolean_val(q, 1))
295                                         ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
296                                 free(modeset);
297                                 break;
298                         case _UC_SHELLPATH:
299                                 config.shelldir = (q == NULL || !boolean_val(q, 1))
300                                         ? "/bin" : newstr(q);
301                                 break;
302                         case _UC_SHELLS:
303                                 for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
304                                         system_shells[i] = newstr(q);
305                                 if (i > 0)
306                                         while (i < _UC_MAXSHELLS)
307                                                 system_shells[i++] = NULL;
308                                 break;
309                         case _UC_DEFAULTSHELL:
310                                 config.shell_default = (q == NULL || !boolean_val(q, 1))
311                                         ? (char *) bourne_shell : newstr(q);
312                                 break;
313                         case _UC_DEFAULTGROUP:
314                                 q = unquote(q);
315                                 config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
316                                         ? NULL : newstr(q);
317                                 break;
318                         case _UC_EXTRAGROUPS:
319                                 for (i = 0; q != NULL; q = strtok(NULL, toks)) {
320                                         if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
321                                                 config.groups[i++] = newstr(q);
322                                 }
323                                 if (i > 0)
324                                         while (i < config.numgroups)
325                                                 config.groups[i++] = NULL;
326                                 break;
327                         case _UC_DEFAULTCLASS:
328                                 config.default_class = (q == NULL || !boolean_val(q, 1))
329                                         ? NULL : newstr(q);
330                                 break;
331                         case _UC_MINUID:
332                                 if ((q = unquote(q)) != NULL && isdigit(*q))
333                                         config.min_uid = (uid_t) atol(q);
334                                 break;
335                         case _UC_MAXUID:
336                                 if ((q = unquote(q)) != NULL && isdigit(*q))
337                                         config.max_uid = (uid_t) atol(q);
338                                 break;
339                         case _UC_MINGID:
340                                 if ((q = unquote(q)) != NULL && isdigit(*q))
341                                         config.min_gid = (gid_t) atol(q);
342                                 break;
343                         case _UC_MAXGID:
344                                 if ((q = unquote(q)) != NULL && isdigit(*q))
345                                         config.max_gid = (gid_t) atol(q);
346                                 break;
347                         case _UC_EXPIRE:
348                                 if ((q = unquote(q)) != NULL && isdigit(*q))
349                                         config.expire_days = atoi(q);
350                                 break;
351                         case _UC_PASSWORD:
352                                 if ((q = unquote(q)) != NULL && isdigit(*q))
353                                         config.password_days = atoi(q);
354                                 break;
355                         case _UC_FIELDS:
356                         case _UC_NONE:
357                                 break;
358                         }
359                 }
360         }
361         free(buf);
362         fclose(fp);
363
364         return (&config);
365 }
366
367
368 int
369 write_userconfig(char const * file)
370 {
371         int             fd;
372         int             i, j;
373         struct sbuf     *buf;
374         FILE           *fp;
375
376         if (file == NULL)
377                 file = _PATH_PW_CONF;
378
379         if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
380                 return (0);
381
382         if ((fp = fdopen(fd, "w")) == NULL) {
383                 close(fd);
384                 return (0);
385         }
386                         
387         buf = sbuf_new_auto();
388         for (i = _UC_NONE; i < _UC_FIELDS; i++) {
389                 int             quote = 1;
390
391                 sbuf_clear(buf);
392                 switch (i) {
393                 case _UC_DEFAULTPWD:
394                         sbuf_cat(buf, boolean_str(config.default_password));
395                         break;
396                 case _UC_REUSEUID:
397                         sbuf_cat(buf, boolean_str(config.reuse_uids));
398                         break;
399                 case _UC_REUSEGID:
400                         sbuf_cat(buf, boolean_str(config.reuse_gids));
401                         break;
402                 case _UC_NISPASSWD:
403                         sbuf_cat(buf, config.nispasswd ?  config.nispasswd :
404                             "");
405                         quote = 0;
406                         break;
407                 case _UC_DOTDIR:
408                         sbuf_cat(buf, config.dotdir ?  config.dotdir :
409                             boolean_str(0));
410                         break;
411                 case _UC_NEWMAIL:
412                         sbuf_cat(buf, config.newmail ?  config.newmail :
413                             boolean_str(0));
414                         break;
415                 case _UC_LOGFILE:
416                         sbuf_cat(buf, config.logfile ?  config.logfile :
417                             boolean_str(0));
418                         break;
419                 case _UC_HOMEROOT:
420                         sbuf_cat(buf, config.home);
421                         break;
422                 case _UC_HOMEMODE:
423                         sbuf_printf(buf, "%04o", config.homemode);
424                         quote = 0;
425                         break;
426                 case _UC_SHELLPATH:
427                         sbuf_cat(buf, config.shelldir);
428                         break;
429                 case _UC_SHELLS:
430                         for (j = 0; j < _UC_MAXSHELLS &&
431                             system_shells[j] != NULL; j++)
432                                 sbuf_printf(buf, "%s\"%s\"", j ?
433                                     "," : "", system_shells[j]);
434                         quote = 0;
435                         break;
436                 case _UC_DEFAULTSHELL:
437                         sbuf_cat(buf, config.shell_default ?
438                             config.shell_default : bourne_shell);
439                         break;
440                 case _UC_DEFAULTGROUP:
441                         sbuf_cat(buf, config.default_group ?
442                             config.default_group : "");
443                         break;
444                 case _UC_EXTRAGROUPS:
445                         for (j = 0; j < config.numgroups &&
446                             config.groups[j] != NULL; j++)
447                                 sbuf_printf(buf, "%s\"%s\"", j ?
448                                     "," : "", config.groups[j]);
449                         quote = 0;
450                         break;
451                 case _UC_DEFAULTCLASS:
452                         sbuf_cat(buf, config.default_class ?
453                             config.default_class : "");
454                         break;
455                 case _UC_MINUID:
456                         sbuf_printf(buf, "%u", config.min_uid);
457                         quote = 0;
458                         break;
459                 case _UC_MAXUID:
460                         sbuf_printf(buf, "%u", config.max_uid);
461                         quote = 0;
462                         break;
463                 case _UC_MINGID:
464                         sbuf_printf(buf, "%u", config.min_gid);
465                         quote = 0;
466                         break;
467                 case _UC_MAXGID:
468                         sbuf_printf(buf, "%u", config.max_gid);
469                         quote = 0;
470                         break;
471                 case _UC_EXPIRE:
472                         sbuf_printf(buf, "%d", config.expire_days);
473                         quote = 0;
474                         break;
475                 case _UC_PASSWORD:
476                         sbuf_printf(buf, "%d", config.password_days);
477                         quote = 0;
478                         break;
479                 case _UC_NONE:
480                         break;
481                 }
482                 sbuf_finish(buf);
483
484                 if (comments[i])
485                         fputs(comments[i], fp);
486
487                 if (*kwds[i]) {
488                         if (quote)
489                                 fprintf(fp, "%s = \"%s\"\n", kwds[i],
490                                     sbuf_data(buf));
491                         else
492                                 fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
493 #if debugging
494                         printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
495 #endif
496                 }
497         }
498         sbuf_delete(buf);
499         return (fclose(fp) != EOF);
500 }