]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pw/pw_conf.c
Cast uid/git to uintmax_t when using printf-like functions so the size of
[FreeBSD/FreeBSD.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 <inttypes.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <fcntl.h>
38 #include <err.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         size_t  linecap;
232         ssize_t linelen;
233
234         buf = NULL;
235         linecap = 0;
236
237         config.groups = sl_init();
238         if (config.groups == NULL)
239                 err(1, "sl_init()");
240         if (file == NULL)
241                 file = _PATH_PW_CONF;
242
243         if ((fp = fopen(file, "r")) == NULL)
244                 return (&config);
245
246         while ((linelen = getline(&buf, &linecap, fp)) > 0) {
247                 if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
248                         static char const toks[] = " \t\r\n,=";
249                         char           *q = strtok(NULL, toks);
250                         int             i = 0;
251                         mode_t          *modeset;
252
253                         while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
254                                 ++i;
255 #if debugging
256                         if (i == _UC_FIELDS)
257                                 printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
258                         else
259                                 printf("Got kwd[%s]=%s\n", p, q);
260 #endif
261                         switch (i) {
262                         case _UC_DEFAULTPWD:
263                                 config.default_password = boolean_val(q, 1);
264                                 break;
265                         case _UC_REUSEUID:
266                                 config.reuse_uids = boolean_val(q, 0);
267                                 break;
268                         case _UC_REUSEGID:
269                                 config.reuse_gids = boolean_val(q, 0);
270                                 break;
271                         case _UC_NISPASSWD:
272                                 config.nispasswd = (q == NULL || !boolean_val(q, 1))
273                                         ? NULL : newstr(q);
274                                 break;
275                         case _UC_DOTDIR:
276                                 config.dotdir = (q == NULL || !boolean_val(q, 1))
277                                         ? NULL : newstr(q);
278                                 break;
279                                 case _UC_NEWMAIL:
280                                 config.newmail = (q == NULL || !boolean_val(q, 1))
281                                         ? NULL : newstr(q);
282                                 break;
283                         case _UC_LOGFILE:
284                                 config.logfile = (q == NULL || !boolean_val(q, 1))
285                                         ? NULL : newstr(q);
286                                 break;
287                         case _UC_HOMEROOT:
288                                 config.home = (q == NULL || !boolean_val(q, 1))
289                                         ? "/home" : newstr(q);
290                                 break;
291                         case _UC_HOMEMODE:
292                                 modeset = setmode(q);
293                                 config.homemode = (q == NULL || !boolean_val(q, 1))
294                                         ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
295                                 free(modeset);
296                                 break;
297                         case _UC_SHELLPATH:
298                                 config.shelldir = (q == NULL || !boolean_val(q, 1))
299                                         ? "/bin" : newstr(q);
300                                 break;
301                         case _UC_SHELLS:
302                                 for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
303                                         system_shells[i] = newstr(q);
304                                 if (i > 0)
305                                         while (i < _UC_MAXSHELLS)
306                                                 system_shells[i++] = NULL;
307                                 break;
308                         case _UC_DEFAULTSHELL:
309                                 config.shell_default = (q == NULL || !boolean_val(q, 1))
310                                         ? (char *) bourne_shell : newstr(q);
311                                 break;
312                         case _UC_DEFAULTGROUP:
313                                 q = unquote(q);
314                                 config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
315                                         ? NULL : newstr(q);
316                                 break;
317                         case _UC_EXTRAGROUPS:
318                                 for (i = 0; q != NULL; q = strtok(NULL, toks))
319                                         sl_add(config.groups, newstr(q));
320                                 break;
321                         case _UC_DEFAULTCLASS:
322                                 config.default_class = (q == NULL || !boolean_val(q, 1))
323                                         ? NULL : newstr(q);
324                                 break;
325                         case _UC_MINUID:
326                                 if ((q = unquote(q)) != NULL && isdigit(*q))
327                                         config.min_uid = (uid_t) atol(q);
328                                 break;
329                         case _UC_MAXUID:
330                                 if ((q = unquote(q)) != NULL && isdigit(*q))
331                                         config.max_uid = (uid_t) atol(q);
332                                 break;
333                         case _UC_MINGID:
334                                 if ((q = unquote(q)) != NULL && isdigit(*q))
335                                         config.min_gid = (gid_t) atol(q);
336                                 break;
337                         case _UC_MAXGID:
338                                 if ((q = unquote(q)) != NULL && isdigit(*q))
339                                         config.max_gid = (gid_t) atol(q);
340                                 break;
341                         case _UC_EXPIRE:
342                                 if ((q = unquote(q)) != NULL && isdigit(*q))
343                                         config.expire_days = atoi(q);
344                                 break;
345                         case _UC_PASSWORD:
346                                 if ((q = unquote(q)) != NULL && isdigit(*q))
347                                         config.password_days = atoi(q);
348                                 break;
349                         case _UC_FIELDS:
350                         case _UC_NONE:
351                                 break;
352                         }
353                 }
354         }
355         free(buf);
356         fclose(fp);
357
358         return (&config);
359 }
360
361
362 int
363 write_userconfig(char const * file)
364 {
365         int             fd;
366         int             i, j;
367         struct sbuf     *buf;
368         FILE           *fp;
369
370         if (file == NULL)
371                 file = _PATH_PW_CONF;
372
373         if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
374                 return (0);
375
376         if ((fp = fdopen(fd, "w")) == NULL) {
377                 close(fd);
378                 return (0);
379         }
380                         
381         buf = sbuf_new_auto();
382         for (i = _UC_NONE; i < _UC_FIELDS; i++) {
383                 int             quote = 1;
384
385                 sbuf_clear(buf);
386                 switch (i) {
387                 case _UC_DEFAULTPWD:
388                         sbuf_cat(buf, boolean_str(config.default_password));
389                         break;
390                 case _UC_REUSEUID:
391                         sbuf_cat(buf, boolean_str(config.reuse_uids));
392                         break;
393                 case _UC_REUSEGID:
394                         sbuf_cat(buf, boolean_str(config.reuse_gids));
395                         break;
396                 case _UC_NISPASSWD:
397                         sbuf_cat(buf, config.nispasswd ?  config.nispasswd :
398                             "");
399                         quote = 0;
400                         break;
401                 case _UC_DOTDIR:
402                         sbuf_cat(buf, config.dotdir ?  config.dotdir :
403                             boolean_str(0));
404                         break;
405                 case _UC_NEWMAIL:
406                         sbuf_cat(buf, config.newmail ?  config.newmail :
407                             boolean_str(0));
408                         break;
409                 case _UC_LOGFILE:
410                         sbuf_cat(buf, config.logfile ?  config.logfile :
411                             boolean_str(0));
412                         break;
413                 case _UC_HOMEROOT:
414                         sbuf_cat(buf, config.home);
415                         break;
416                 case _UC_HOMEMODE:
417                         sbuf_printf(buf, "%04o", config.homemode);
418                         quote = 0;
419                         break;
420                 case _UC_SHELLPATH:
421                         sbuf_cat(buf, config.shelldir);
422                         break;
423                 case _UC_SHELLS:
424                         for (j = 0; j < _UC_MAXSHELLS &&
425                             system_shells[j] != NULL; j++)
426                                 sbuf_printf(buf, "%s\"%s\"", j ?
427                                     "," : "", system_shells[j]);
428                         quote = 0;
429                         break;
430                 case _UC_DEFAULTSHELL:
431                         sbuf_cat(buf, config.shell_default ?
432                             config.shell_default : bourne_shell);
433                         break;
434                 case _UC_DEFAULTGROUP:
435                         sbuf_cat(buf, config.default_group ?
436                             config.default_group : "");
437                         break;
438                 case _UC_EXTRAGROUPS:
439                         for (j = 0; config.groups != NULL &&
440                             j < (int)config.groups->sl_cur; j++)
441                                 sbuf_printf(buf, "%s\"%s\"", j ?
442                                     "," : "", config.groups->sl_str[j]);
443                         quote = 0;
444                         break;
445                 case _UC_DEFAULTCLASS:
446                         sbuf_cat(buf, config.default_class ?
447                             config.default_class : "");
448                         break;
449                 case _UC_MINUID:
450                         sbuf_printf(buf, "%ju", (uintmax_t)config.min_uid);
451                         quote = 0;
452                         break;
453                 case _UC_MAXUID:
454                         sbuf_printf(buf, "%ju", (uintmax_t)config.max_uid);
455                         quote = 0;
456                         break;
457                 case _UC_MINGID:
458                         sbuf_printf(buf, "%ju", (uintmax_t)config.min_gid);
459                         quote = 0;
460                         break;
461                 case _UC_MAXGID:
462                         sbuf_printf(buf, "%ju", (uintmax_t)config.max_gid);
463                         quote = 0;
464                         break;
465                 case _UC_EXPIRE:
466                         sbuf_printf(buf, "%d", config.expire_days);
467                         quote = 0;
468                         break;
469                 case _UC_PASSWORD:
470                         sbuf_printf(buf, "%d", config.password_days);
471                         quote = 0;
472                         break;
473                 case _UC_NONE:
474                         break;
475                 }
476                 sbuf_finish(buf);
477
478                 if (comments[i])
479                         fputs(comments[i], fp);
480
481                 if (*kwds[i]) {
482                         if (quote)
483                                 fprintf(fp, "%s = \"%s\"\n", kwds[i],
484                                     sbuf_data(buf));
485                         else
486                                 fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
487 #if debugging
488                         printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
489 #endif
490                 }
491         }
492         sbuf_delete(buf);
493         return (fclose(fp) != EOF);
494 }